在c#中分配引用时添加神秘调用

Mysterious call is added when reference is assigned in c#

我知道其他人也写过类似的问题,但我认为我的情况不同,因为我找不到任何解决方案。

我有一个对象分配,像这样非常简单:

_buffer3 = buffer; //they are just simple reference types

生成的汇编代码如下

 mov         edx,dword ptr [ebp-3Ch] 
 mov         eax,dword ptr [ebp-40h] 
 lea         edx,[edx+4] 
 call        69C322F0 

现在,为了了解发生了什么,我想进入调用(为什么要在作业中使用调用?)。但是该地址的代码不存在,我无法介入。 如果我在地址代码字段中键入地址,则会显示以下内容:

69C322F0  ???  

对解决这个谜团有什么帮助吗? :)

编辑..显然,当在 class.

的方法中分配引用时,添加了神秘调用

如果我有这个class:

        private class Test
        {
            int _X;
            int _Y;
            Test _t;

            public void SetValues(int x, int y, Test t)
            {
                _X = x;
                _Y = y;
            }
        }

为方法 SetValues 生成的程序集是:

                _X = x;
00000028  mov         eax,dword ptr [ebp-3Ch] 
0000002b  mov         edx,dword ptr [ebp-40h] 
0000002e  mov         dword ptr [eax+8],edx 
                _Y = y;
00000031  mov         eax,dword ptr [ebp-3Ch] 
00000034  mov         edx,dword ptr [ebp+0Ch] 
00000037  mov         dword ptr [eax+0Ch],edx 

有道理

但是如果我写这个

        private class Test
        {
            int _X;
            int _Y;
            Test _t;

            public void SetValues(int x, int y, Test t)
            {
                _X = x;
                _Y = y;
                _t = t;
            }
        }

神秘召唤出现

                _X = x;
00000028  mov         eax,dword ptr [ebp-3Ch] 
0000002b  mov         edx,dword ptr [ebp-40h] 
0000002e  mov         dword ptr [eax+8],edx 
                _Y = y;
00000031  mov         eax,dword ptr [ebp-3Ch] 
00000034  mov         edx,dword ptr [ebp+0Ch] 
00000037  mov         dword ptr [eax+0Ch],edx 
                _t = t;
0000003a  mov         edx,dword ptr [ebp-3Ch] 
0000003d  mov         eax,dword ptr [ebp+8] 
00000040  lea         edx,[edx+4] 
00000043  call        515E2E48 

恕我直言,它与垃圾收集有关,但我不明白它是什么,我真的很想弄清楚。我知道你们当中一定有人知道 :)

答案的附录,这是我摘自 Google CLR via C# 的书籍:

我会对此稍加思考,一个完整的答案会填满一本书,让每个人都入睡。您对机器代码的看法非常不准确,32 位反汇编程序在转换 CALL 地址方面做得很差。如果您查看 64 位代码,这在最近的 VS 版本中有了很大改进,因为它不再伪造机器代码指令的地址。到达那里:

  • 项目 > 属性 > 构建选项卡:平台目标 = AnyCPU,取消勾选 "Prefer 32-bit"
  • 项目 > 属性 > 调试选项卡:勾选 "Enable native code debugging"
  • 工具 > 选项 > 调试 > 符号:启用 Microsoft 符号服务器
  • 工具 > 选项 > 调试 > 常规:取消勾选 "Suppress JIT optimization"。

仅当您想查看 真正的 机器代码(在您用户的机器上运行的那种)时,才需要最后的设置更改。请注意,您现在正在查看未优化的调试代码。与此问题无关。

这很好地点亮了反汇编程序,尽管它仍然远非理想。 Test.SetValues() 方法的尾端现在看起来像这样:

                _t = t;
00007FFA1ECB0C58  mov         rdx,qword ptr [rbp+50h]  
00007FFA1ECB0C5C  lea         rcx,[rdx+8]  
00007FFA1ECB0C60  mov         rdx,qword ptr [rbp+68h]  
00007FFA1ECB0C64  call        JIT_WriteBarrier (07FFA7E3312B0h)  

现在显示的地址是准确的,0x07FFA7E3312B0。查看该代码需要一个额外的步骤,您必须强制调试器进入本机模式。 Debug > Windows > Call Stack 和 double-click 跟踪底部的本机函数,如 RtlUserThreadStart。现在你可以copy/paste“07FFA7E3312B0”进入反汇编程序的地址框,先输入“0x”。我不会在这里展示它,这是 hand-written 汇编代码,它做了一件你永远无法从代码中 reverse-engineer 做的相当神秘的事情。

寻找这些抖动辅助函数的更好的地方是源代码,虽然它不是完全匹配,github CoreCLR project is your best bet. Takes you here

通常,抖动会根据需要发出这些类型的直接 CLR 函数调用,它们的名称通常以 "JIT" 开头。这个恰好是用汇编语言编写的,但是并不常见;大多数是用 C++ 编写的。