现在的位置: 首页 > 综合 > 正文

.NET / Rotor源码分析5 – 开始使用WinDbg+SOS调试,sscoree.dll,加载SOS并设置JIT断点

2013年02月04日 ⁄ 综合 ⁄ 共 4669字 ⁄ 字号 评论关闭

准备工作

在经过一番准备之后,现在我们可以开始正式使用WinDbg+SOS来调试托管代码了。如果你没有看过前两篇文章,那么请先阅读这两篇文章以对WinDbg+SOS有一个大致的了解。这两篇文章的链接在这里:

.NET Rotor源码研究4 – 修改Rotor使其发送CLR Notificationhttp://blog.csdn.net/ATField/archive/2007/05/21/1618535.aspx

.NET Rotor源码研究3 - 调试Rotor托管代码的利器:WinDbgSOShttp://blog.csdn.net/ATField/archive/2007/05/12/1606151.aspx

除此之外,还需要准备一个小程序来进行调试,本文所使用的程序如下:(hello.cs)

 

namespace Hello

{

       class Hello

       {

              public static void Main(string[] args)

              {

                     System.Console.WriteLine("Your name please?");

                     string s = System.Console.ReadLine();

                     Welcome(s);

                     Welcome(s);

              }

 

              public static void Welcome(string name)

              {

                     System.Console.WriteLine("Hello " + name);

              }

 

       }

}

打开命令提示符,进入sscli20目录,键入:

env dbg

进入Rotor的调试环境,如果你还没有BuildRotor的一个Debug版本,那么请参照本系列的第一篇文章来设置你的环境并Build出一个调试版本的Rotor。文章的链接在这里:

.NET Rotor源码研究1 – Building Rotorhttp://blog.csdn.net/ATField/archive/2006/12/31/1471465.aspx

如果已经Build出来了一个Rotorx86调试版本,那么可以开始动手编译hello.cs (假定hello.cs位于binaries.x86dbg.rotor目录下):

cd binaries.x86dbg.rotor

csc hello.cs

编译之后,启动调试器。这里我们不能直接调试hello.exe,否则.NET将会执行hello.exe,这里我们需要使用clix.exe来运行hello.exe,这样才可以让Rotor来运行hello.exe:

windbg clix hello.exe

请保证WinDbg已经被安装并且在其路径在Path变量中。

程序的加载

启动调试器,我们停在程序加载的位置,Call Stack如下(如果你没有Windows系统DLL所对应的Symbol,那么你看到的会有所不同,这里因为有Symbol,结果更加准确):

ntdll!DbgBreakPoint

ntdll!LdrpDoDebuggerBreak+0x31

ntdll!LdrpInitializeProcess+0xffc

ntdll!_LdrpInitialize+0xf5

ntdll!LdrInitializeThunk+0x10

在本系列的第二篇文章中曾经提到,用到PAL的程序的main实际是在PAL_startup_main,如果你还没有看到第二篇文章的话,连接在这里:

.NET Rotor源码研究2 - PAL http://blog.csdn.net/ATField/archive/2007/01/12/1481538.aspx

在调试器中输入:

 

bp clix!PAL_startup_main

g

第一条语句的作用是设置断点于clix.exePAL_startup_main函数,第二条语句命令WinDbg继续执行。执行g之后WinDbg很快在clixmain函数停下来,这里的main实际上就是PAL_startup_main,被#define过:

int __cdecl main(int argc, char **argv)

{

// 省略

nExitCode = Launch(pModuleName, pActualCmdLine);

}

 

DWORD Launch(WCHAR* pFileName, WCHAR* pCmdLine)

{

    // 省略

    nExitCode = _CorExeMain2(NULL, 0, pFileName, NULL, pCmdLine);

 

    return nExitCode;

}

这里有不少无关的代码,大部分是分析命令行,直接来到Launch函数调用,Launch函数负责启动ModuleName,也就是hello.exe,启动工作由_CorExeMain2执行。在WindbgF10F11仍然可以工作(当然命令行也可以)。一路执行到_CorExeMain2然后F11,会发现来到了sscoree_CorExeMain2函数,位于sscoree_shims.h之中:

 

SSCOREE_SHIM_RET (

                  __int32,

                  STDMANGLE(_CorExeMain2,20),

                  ( PBYTE   pUnmappedPE,

                    DWORD   cUnmappedPE,

                    LPWSTR  pImageNameIn,

                    LPWSTR  pLoadersFileName,

                    LPWSTR  pCmdLine),

                  ( pUnmappedPE,

                    cUnmappedPE,

                    pImageNameIn,

                    pLoadersFileName,

                    pCmdLine),

                  -1)

这个函数代码很奇怪,只是一些函数调用。仔细观察一下这个头文件,发现这个文件是很有规律的由下面内容组成:

 

SSCOREE_LIB_START (mscorwks)

 

SSCOREE_SHIM_RET (

                  HRESULT,

                  STDMANGLE(MetaDataGetDispenser,12),SSCOREE_LIB_END (mscorwks)

SSCOREE_LIB_END (mscorwks)

 

SSCOREE_LIB_START (mscorpe)

SSCOREE_LIB_END (mscorpe)

 

 

SSCOREE_LIB_START (mscordbi)

这个提示我们SSCOREE.dll会负责将列表中的函数转发到对应的DLL中的对应函数。实际上,这正是sscoree.dll所起到的作用之一,确定Rotor版本,加载对应版本的Rotor,并调用对应版本的Rotor的相应函数,因此sscoree(在.NET中则是mscoree)又被称为Shim。这个SSCOREE_SHIM_RET只是一个宏定义,如下:

#define SSCOREE_SHIM_BODY(FUNC,RET_COMMAND,SIG_RET,SIG_ARGS,ARGS)       /

do {                                                                    /

    SSCOREE_SHIM_CUSTOM_INIT                                            /

    FARPROC proc_addr = SscoreeShimGetProcAddress (                     /

                        SHIMSYM_ ## FUNC,                               /

                        #FUNC);                                         /

    _ASSERTE (proc_addr);                                               /

    if (proc_addr) {                                                    /

        RET_COMMAND ((SIG_RET (STDMETHODCALLTYPE *)SIG_ARGS)proc_addr)ARGS; /

    }                                                                   /

} while (0)

 

 

#define SSCOREE_SHIM_RET(SIG_RET,FUNC,SIG_ARGS,ARGS,ONERROR)            /

extern "C"                                                              /

SIG_RET STDMETHODCALLTYPE FUNC SIG_ARGS                                 /

{                                                                       /

    SSCOREE_SHIM_BODY (FUNC, return, SIG_RET, SIG_ARGS, ARGS);          /

    return ONERROR;                                                     /

}

 

#define SSCOREE_SHIM_NORET(FUNC,SIG_ARGS,ARGS)                          /

extern "C"                                                              /

void STDMETHODCALLTYPE FUNC SIG_ARGS                                    /

{                                                                       /

    SSCOREE_SHIM_BODY (FUNC, ; ,void, SIG_ARGS, ARGS);                  /

}     

可以看到在sscoree

抱歉!评论已关闭.