潜在的.NET x86 JIT问题

本文关键字:JIT 问题 x86 NET | 更新日期: 2023-09-27 18:01:51

以下代码在Release模式(或启用了优化的Debug模式(下构建并在没有附加Visual Studio调试器的情况下运行时,其行为会有所不同。

它似乎只有在使用x86 JITter的情况下才能复制。我已经在x86机器上测试了这一点,并在x64机器上运行WOW64(通过将平台目标设置为x86(。

我只在.NET 4.0上尝试过。

当在Release中的调试器外运行时,我看到:

Value is 4

当在调试器内部运行时,WriteLine调用的e.Value.Length部分抛出NullReferenceException,这正是我所期望的。

代码:

namespace Test
{
    class UsingReleasable<T>
    {
        public UsingReleasable(T obj)
        {
            m_obj = obj;
        }
        public T Release()
        {
            T tmp = m_obj;
            m_obj = default(T);
            return tmp;
        }
        public T Value
        {
            get { return m_obj; }
        }
        T m_obj;
    }
    class Program
    {
        static void Main(string[] args)
        {
            var e = new UsingReleasable<string>("test");
            e.Release();
            System.Console.WriteLine("Value is {0}", e.Value.Length);
        }
    }
}

我仔细查看JIT生成的代码,觉得这是一个bug,但在将其转发到MSConnect之前,我想仔细检查一下。

潜在的.NET x86 JIT问题

我可以重现你的行为:

R:'>csc /platform:x86 releasable.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

R:'>releasable
Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
   at Test.Program.Main(String[] args)
R:'>csc /o+ /platform:x86 releasable.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

R:'>releasable
Value is 4
R:'>csc /platform:anycpu releasable.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

R:'>releasable
Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
   at Test.Program.Main(String[] args)
R:'>csc /o+ /platform:anycpu releasable.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

R:'>releasable
Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
   at Test.Program.Main(String[] args)

/checked编译器选项没有任何区别。调试数据/debug+的生成也是如此。当调用Console.ReadLine()(以便有机会连接调试器并查看优化的代码(时,问题仍然存在


我对Main做了一个轻微的修改,以允许调试优化的代码:

    static void Main(string[] args)
    {
        var e = new UsingReleasable<string>("test");
        System.Console.WriteLine("attach now");
        System.Console.ReadLine();
        e.Release();
        System.Console.WriteLine("Value is {0}", e.Value.Length);
    }

和实际拆卸:

--- r:'releasable.cs -----------------------------------------------------------
            var e = new UsingReleasable<string>("test");
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        edi 
00000004  push        esi 
00000005  mov         ecx,1839B0h 
0000000a  call        FFF81FB0 
0000000f  mov         esi,eax 
00000011  mov         eax,dword ptr ds:[03772030h] 
00000017  lea         edx,[esi+4] 
0000001a  call        60452F70 
            System.Console.WriteLine("attach now");
0000001f  call        5E927060 
00000024  mov         ecx,eax 
00000026  mov         edx,dword ptr ds:[03772034h] 
0000002c  mov         eax,dword ptr [ecx] 
0000002e  mov         eax,dword ptr [eax+3Ch] 
00000031  call        dword ptr [eax+10h] 
00000034  call        5EEF9A40 
00000039  mov         ecx,eax 
0000003b  mov         eax,dword ptr [ecx] 
0000003d  mov         eax,dword ptr [eax+2Ch] 
00000040  call        dword ptr [eax+1Ch] 
            e.Release();
00000043  mov         edi,dword ptr [esi+4]              ; edi = e.Value
00000046  lea         esi,[esi+4]                        ; esi = &e.Value
00000049  xor         edx,edx                            ; edx = null
0000004b  mov         dword ptr [esi],edx                ; *esi = edx (e.Value = null)
0000004d  mov         ecx,5EBE28F8h
00000052  call        FFF81FB0
00000057  mov         edx,eax 
00000059  mov         eax,dword ptr [edi+4]              ; this sets EAX to 4
0000005c  mov         dword ptr [edx+4],eax 
0000005f  mov         esi,edx 
00000061  call        5E927060 
00000066  push        esi 
00000067  mov         ecx,eax 
00000069  mov         edx,dword ptr ds:[03772038h] 
0000006f  mov         eax,dword ptr [ecx] 
00000071  mov         eax,dword ptr [eax+3Ch] 
00000074  call        dword ptr [eax+18h]                ; this results in the output "Value is 4'n"
00000077  pop         esi 
        }
00000078  pop         edi 
00000079  pop         ebp 
0000007a  ret 

当程序在调试器下启动时,会生成此代码(并且确实会生成NullReferenceException:

--- r:'releasable.cs -----------------------------------------------------------
            var e = new UsingReleasable<string>("test");
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  sub         esp,24h 
00000006  mov         dword ptr [ebp-4],ecx 
00000009  cmp         dword ptr ds:[001E313Ch],0 
00000010  je          00000017 
00000012  call        606B6807 
00000017  xor         edx,edx 
00000019  mov         dword ptr [ebp-0Ch],edx 
0000001c  mov         ecx,1E39B0h 
00000021  call        FFF91FB0 
00000026  mov         dword ptr [ebp-10h],eax 
00000029  mov         edx,dword ptr ds:[032E2030h] 
0000002f  mov         ecx,dword ptr [ebp-10h] 
00000032  call        dword ptr ds:[001E3990h] 
00000038  mov         eax,dword ptr [ebp-10h] 
0000003b  mov         dword ptr [ebp-0Ch],eax 
            System.Console.WriteLine("attach now");
0000003e  mov         ecx,dword ptr ds:[032E2034h] 
00000044  call        5E8D703C 
            System.Console.ReadLine();
00000049  call        5EEAA728 
0000004e  nop 
            e.Release();
0000004f  mov         ecx,dword ptr [ebp-0Ch] 
00000052  cmp         dword ptr [ecx],ecx 
00000054  call        dword ptr ds:[001E3994h] 
0000005a  nop 
            System.Console.WriteLine("Value is {0}", e.Value.Length);
0000005b  mov         eax,dword ptr ds:[032E2038h] 
00000061  mov         dword ptr [ebp-14h],eax 
00000064  mov         ecx,dword ptr [ebp-0Ch] 
00000067  cmp         dword ptr [ecx],ecx 
00000069  call        dword ptr ds:[001E3998h] 
0000006f  mov         dword ptr [ebp-18h],eax 
00000072  mov         ecx,dword ptr [ebp-18h] 
00000075  cmp         dword ptr [ecx],ecx                 ; access violation here
00000077  call        608CBA5B 
0000007c  mov         dword ptr [ebp-8],eax 
0000007f  mov         ecx,5EBE28F8h 
00000084  call        FFF91FB0
00000089  mov         dword ptr [ebp-1Ch],eax 
0000008c  mov         eax,dword ptr [ebp-14h] 
0000008f  mov         dword ptr [ebp-20h],eax 
00000092  mov         eax,dword ptr [ebp-1Ch] 
00000095  mov         edx,dword ptr [ebp-8] 
00000098  mov         dword ptr [eax+4],edx 
0000009b  mov         eax,dword ptr [ebp-1Ch] 
0000009e  mov         dword ptr [ebp-24h],eax 
000000a1  mov         ecx,dword ptr [ebp-20h] 
000000a4  mov         edx,dword ptr [ebp-24h] 
000000a7  call        5E8CD460 
        }
000000ac  nop 
000000ad  mov         esp,ebp 
000000af  pop         ebp 
000000b0  ret 

我想我已经在错误的版本中注释了所有相关的代码行。显然,寄存器edi用于保存指向e.Value的指针,该指针由组成,最有可能是指向内容的指针(偏移量为0(和v表的长度(偏移量4((偏移量0(,长度(偏移值4(,后面紧跟内容。不幸的是,e.Value(字符串"test"(在e.Value被清除之前被复制到edi中,因此使用错误的字符串指针来获取长度。哎哟


Connect上提交的错误(请投赞成票!(:x86 JIT不正确地重新排序了通用类型字段的加载,并将其分配给默认(T(