【问题标题】:Mysterious call is added when reference is assigned in c#在c#中分配引用时添加了神秘的调用
【发布时间】:2015-12-29 00:40:25
【问题描述】:

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

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

_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  ???  

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

Edit..显然,当在类的方法内分配引用时,添加了神秘的调用。

如果我有这门课:

        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 

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

答案的附录,这是我通过 C# 从 CLR 的 Google Books 中摘录的:

【问题讨论】:

  • 嗯,不,我实际上是故意禁用它的。我试试看!
  • @AlexD,您是否在项目属性(“调试”选项卡)中选中“启用非托管代码调试”?
  • 可能与this question重复
  • @alexD 对不起,我看错了,是的,它没有被选中,是的,启用非托管代码调试被选中
  • 调用是更新卡表以实现写屏障。 Details here.

标签: c# assembly native-code


【解决方案1】:

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

  • Project > Properties > Build 选项卡:Platform target = AnyCPU,取消勾选“Prefer 32-bit”
  • 项目 > 属性 > 调试选项卡:勾选“启用本机代码调试”
  • 工具 > 选项 > 调试 > 符号:启用 Microsoft 符号服务器
  • 工具 > 选项 > 调试 > 常规:取消选中“抑制 JIT 优化”。

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

这很好地照亮了反汇编程序,尽管它仍然远非理想。您的 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 并双击跟踪底部的本机函数,例如 RtlUserThreadStart。现在您可以将“07FFA7E3312B0”复制/粘贴到反汇编程序的地址框中,首先键入“0x”。我不会在这里展示它,这是手写的汇编代码,它做了一件相当神秘的事情,你永远无法从代码中逆向工程。

寻找这些抖动辅助函数的更好的地方是源代码,虽然它不是完全匹配的,但github CoreCLR project 是你最好的选择。带你here

一般来说,jitter 会根据需要发出这些直接的 CLR 函数调用,它们的名称通常以“JIT”开头。这个恰好是用汇编编写的,但这并不常见;大多数都是用 C++ 编写的。

【讨论】:

  • 谢谢,非常有用的答案。对我来说,了解 WriteBarrier 就足够了,但调试说明是最重要的。
  • 阅读你的答案总是很开心,每次都能学到新东西:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
  • 2016-03-27
相关资源
最近更新 更多