在过去的几周里,我一直在玩弄非托管 .NET 调试 API。
虽然 MSDN 记录了接口本身,但为了了解如何以任何有意义的方式实际使用它们,我求助于各种博客(主要是Mike Stall 的)和CLR 托管调试器示例和ILSpy 源中的托管包装器。最后,我能够将我的测试程序附加到正在运行的进程,设置断点并点击它们。
接下来我想做的是在遇到这样的断点时,读取相关对象实例的字段。当调试目标方法没有经过 NGEN 或 JIT 时,通过禁用加载 NGEN 程序集(环境设置“COMPLUS_ZAPDISABLE=1”)和禁用 JIT 优化(又名“.ini 文件技巧”),这可以正常工作)。
但是,当我对我的理想目标(零售优化(NGEN/JIT'ed)代码)进行相同尝试时,它不起作用。例如:我可以在点击入口断点时仍然检索方法参数的数量,但我无法获取第一个参数本身(调试 API 会引发异常)。
现在,我想这是因为调试 API 应该是平台独立的,在这种情况下,程序集不再是。但是,如果我接受这个平台对英特尔的依赖怎么办:据我所知,CLR 使用fastcall 调用约定,在这种情况下,ECX 寄存器包含第一个方法参数(成员函数的隐式“this”引用) .
我对此进行了测试,实际上在到达断点时,ECX 在 NGEN 的程序集中包含了 OBJECTREF(对象实例的地址)。
现在读取实例字段的最后一步是检索相对于该指针的字段偏移量,这就是我卡住的地方,因为我似乎无法找出 CLR在本机代码生成期间如何打包实例字段.
我意识到这可能取决于 CLR 版本/实现,但显然有办法,因为带有 SOS 扩展的 WinDbg 能够找到这种布局。如果不通过调试 API,我可以以某种方式利用 SOS.dll 吗?