.Net 7的默认构造函数.Ctor下断点出错续

楔子

因为在默认的构造函数.Ctor上面下断点,导致了程序的崩溃,参见上一篇:一个奇怪的错误,找到了错误点,也找到了解决方法,但是结果不满意

因为没有相应的结构体在即时窗口赋值(除了),上篇解决的方案是八字节一个单元复制地址值,这种解决方案非常拙劣。因为如果高达几百个八字节,那岂不是复制几百次?本篇来看下另外一种解决方案。.

代码

  internal class Program
    {
        static void Main(string[] args)
        {
            Program pm = new Program();
            Console.WriteLine("Hello, World!");
            GC.Collect();
        }
    }

 

原理

在Main函数运行之前,RunMainInternal处下断点,然后再在WriteCodeBytes
函数里面的memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW, 
m_codeWriteBufferSize);这一行代码下断点,不要在Program.Ctor汇编地址处下断点。

这样的话,变量m_CodeHeader,m_CodeHeaderRW的MethodDesc第一次运行是
Program.Main函数,第二次运行就是Program.Ctor函数了。当第二次运行到
WriteCodeBytes的memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW, 
m_codeWriteBufferSize);这句代码的时候,m_CodeHeader奇迹般的被赋值了。

而如果上面在Program.Ctor处下了断点,则以上步骤在WriteCodeBytes函数里调用的memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW, 
m_codeWriteBufferSize)处,变量被MapViewOfFile映射出来的
codeWriterHolder.GetRW()(它实际上是
codeWriterHolder.m_addressRW)被m_CodeHeaderRW赋值,但是原值
codeWriterHolder.m_addressRX则不会被赋值映射。

这种截然不同的运作和调试方式,可以看到,上面的原理是可以支持调试.Ctor
的,也不需要8字节赋值如此的麻烦。

最后可以让它从WriteCodeBytes函数运行出来,直到运行到pCode,它是
Program.Ctor函数的起始地址,也就是函数头的上一个地址(机器码push地址的上
一个地址),然后再里面下个断点,F5跳转到断点,F10运行到前面的
Program.Ctor的下一句汇编代码。这样的话就完美的避开了关于打断点产生的异
常。

 

验证

用lldb验证下以上的说法

在RunMainInternal处下个断点,r运行到断点处
.Net 7的默认构造函数.Ctor下断点出错续

在WriteCodeBytes函数处下个断点,C运行到断点出,单步n运行到memcpy函数处
.Net 7的默认构造函数.Ctor下断点出错续

此时就可以看下当前它编译的是哪个函数了运行命令
p *(m_CodeHeaderRW.pRealCodeHeader)
.Net 7的默认构造函数.Ctor下断点出错续

把它里面的MethodDesc dump下
.Net 7的默认构造函数.Ctor下断点出错续
可以看到当前编译的是Program.Main函数

继续C命令,重复上面的步骤
.Net 7的默认构造函数.Ctor下断点出错续
然后dump methoddesc
.Net 7的默认构造函数.Ctor下断点出错续
可以看到它调用的是Program.Ctor函数。

单步n运行到pCode处
.Net 7的默认构造函数.Ctor下断点出错续
看下pcode的值
.Net 7的默认构造函数.Ctor下断点出错续
反汇编下pCode处函数
.Net 7的默认构造函数.Ctor下断点出错续
在函数头也就是机器码push处打个断点

把WriteCodeBytes处的断点删掉
.Net 7的默认构造函数.Ctor下断点出错续
运行C命令
.Net 7的默认构造函数.Ctor下断点出错续

程序的结果:Hello World打印出来了,也没有出错。

总结

这种.Ctor断点的错误,可以通过上述方法避免,而补丁式的修改。从而通达的调试整个项目,而仅仅多了几次步过。