代码之家  ›  专栏  ›  技术社区  ›  Dominique

当源代码证明不正确时,调试变量如何为空

  •  -1
  • Dominique  · 技术社区  · 7 年前

    我正在调试一个完整的内存转储( procdump -ma ... ,我正在研究调用堆栈,对应于以下源代码:

    unsigned int __stdcall ExecutionThread(void* pArg)
    {
        __try
        {
            BOOL bRunning = TRUE;
            CInternalManagerObject* pInternalManagerObject = (CInternalManagerObject*) pArg;
    
            pInternalManagerObject->Init();
    
            CInternaStartlManagerObject* pInternaStartlManagerObject = pInternalManagerObject->GetInternaStartlManagerObject();
    
            while(bRunning)
            {
                bRunning = pInternalManagerObject->Poll(pInternaStartlManagerObject);
    
                if (CSLGlobal::IsValidHandle(_Module.m_hNeverEvent))
                    WaitForSingleObject(_Module.m_hNeverEvent, 15);
            } <<<<<<<<<<<<<<<<============== here is the call stack pointer
    
            pInternalManagerObject->DeInit();
    

    正如你所看到的, pArg 正在被打字然后被使用,所以这是不可能的 帕格 成为 NULL 但这正是观察窗告诉我的。除此之外,似乎不知道内部变量(也如观察窗口中所述)。

    监视窗口内容:

    pArg                    0x0000000000000000  void *
    bRunning                identifier "bRunning" is undefined  
    pInternalManagerObject  identifier "pInternalManagerObject" is undefined  
    

    我能理解 bRunning 被优化掉,因为这个变量不再被使用,但这不适用于 pInternalManagerObject ,在下面的行中仍然使用。

    符号似乎加载得很好。

    我使用Visual Studio Professional 2017版本15.8.8查看此。

    有没有人知道是什么导致了这种奇怪的行为?我可以做些什么来获得一个内部变量值正确的转储文件?

    生成的程序集代码的问题后编辑

    生成的程序集是:

        27: 
        28: unsigned int __stdcall ExecutionThread(void* pArg)
        29: {
    00007FF69C7A1690 48 89 5C 24 08       mov         qword ptr [rsp+8],rbx  
    00007FF69C7A1695 48 89 74 24 10       mov         qword ptr [rsp+10h],rsi  
    00007FF69C7A169A 57                   push        rdi  
    00007FF69C7A169B 48 83 EC 20          sub         rsp,20h  
    00007FF69C7A169F 48 8B F9             mov         rdi,rcx  
        30:     __try
        31:     {
        32:         BOOL bRunning = TRUE;
    00007FF69C7A16A2 BB 01 00 00 00       mov         ebx,1  
        33:         CInternalManagerObject* pInternalManagerObject = (CInternalManagerObject*) pArg;
        34: 
        35:         pInternalManagerObject->Init();
    00007FF69C7A16A7 E8 64 EA FD FF       call        CInternalManagerObject::Init (07FF69C780110h)  
        36:         
        37:         CBaseManager* pBaseManager = pInternalManagerObject->GetBaseManager();
    00007FF69C7A16AC 48 8B CF             mov         rcx,rdi  
    00007FF69C7A16AF E8 0C E9 FD FF       call        CInternalManagerObject::GetBaseManager (07FF69C77FFC0h)  
    00007FF69C7A16B4 48 8B F0             mov         rsi,rax  
        40:         {
        41:             bRunning = pInternalManagerObject->Poll(pBaseManager);
    00007FF69C7A16B7 48 8B CF             mov         rcx,rdi  
        38: 
        39:         while(bRunning)
    00007FF69C7A16BA 85 DB                test        ebx,ebx  
    00007FF69C7A16BC 74 2E                je          ExecutionThread+5Ch (07FF69C7A16ECh)  
        40:         {
        41:             bRunning = pInternalManagerObject->Poll(pBaseManager);
    00007FF69C7A16BE 48 8B D6             mov         rdx,rsi  
        40:         {
        41:             bRunning = pInternalManagerObject->Poll(pBaseManager);
    00007FF69C7A16C1 E8 7A ED FD FF       call        CInternalManagerObject::Poll (07FF69C780440h)  
    00007FF69C7A16C6 8B D8                mov         ebx,eax  
        42: 
        43:             if (CSLGlobal::IsValidHandle(_Module.m_hNeverEvent))
    00007FF69C7A16C8 48 8D 0D C1 13 0E 00 lea         rcx,[_Module+550h (07FF69C882A90h)]  
    00007FF69C7A16CF E8 3C F2 FB FF       call        __Skyline_Global::CSLGlobal::IsValidHandle (07FF69C760910h)  
    00007FF69C7A16D4 85 C0                test        eax,eax  
    00007FF69C7A16D6 74 12                je          ExecutionThread+5Ah (07FF69C7A16EAh)  
        44:                 WaitForSingleObject(_Module.m_hNeverEvent, 15);
    00007FF69C7A16D8 BA 0F 00 00 00       mov         edx,0Fh  
    00007FF69C7A16DD 48 8B 0D AC 13 0E 00 mov         rcx,qword ptr [_Module+550h (07FF69C882A90h)]  
    00007FF69C7A16E4 FF 15 16 0B 08 00    call        qword ptr [__imp_WaitForSingleObject (07FF69C822200h)]  
        45:         }
    00007FF69C7A16EA EB CB                jmp         ExecutionThread+27h (07FF69C7A16B7h)  
        46: 
        47:         pInternalManagerObject->DeInit();
    00007FF69C7A16EC E8 FF E7 FD FF       call        CInternalManagerObject::DeInit (07FF69C77FEF0h)  
        48:     }
    

    我想这意味着 帕格 可以在寄存器中找到 RDI .

    这个 Register 窗口提供以下信息:

    RAX = 0000000000000000
    RBX = 0000000000000001
    RCX = 0000000000000000
    RDX = 0000000000000000
    RSI = 00000072A1E83220
    RDI = 00000072A14A9990
    ...
    

    在前面提到的地方查看内存后,我发现十六进制值如下:

    0x00000072A14A9990  98 59 82 9c f6 7f 00 00 01 00 00 00 00 00 08 00 28 d2 28 62 f9 7f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 0e 78 a2 72 00  ˜Y.œö...........(Ò(bù...................................P.x¢r.
    0x00000072A14A99CE  00 00 ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 5c 07 00 00 00 00 00 00 d0 07 00 02 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ..ÿÿÿÿ............\.......Ð.......ÿÿÿÿÿÿÿÿÿÿÿÿ................
    0x00000072A14A9A0C  00 00 00 00 d0 07 00 02 00 00 00 00 38 59 82 9c f6 7f 00 00 f0 90 60 a2 72 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 59 82 9c f6 7f 00 00 00 00
    

    这是不是意味着 帕格 不是 无效的 的确?(对不起,但我在装配调试方面没有经验)

    1 回复  |  直到 7 年前
        1
  •  1
  •   Rab    7 年前

    这是否意味着parg不是空的?

    不,这并不意味着; pArg 是空的。手表窗口告诉你,寄存器告诉你。

    如你所见,parg正在被打字,然后被使用,所以 parg不可能为空。

    这不正确,演员们不是这样做的。如果变量为空,则转换的结果将为空。

    https://en.cppreference.com/w/c/language/cast

    我想这意味着parg的正确值可以在 注册RDI。

    不; 帕格 安装在 rcx ; mov 从右向左工作。

    mov         rcx,rdi
    RCX = 0000000000000000
    

    https://c9x.me/x86/html/file_module_x86_id_176.html

    我可以理解早午餐被优化了,因为这个变量不再被使用,但是对于PinternalManagerObject来说这是不正确的, 在下面的行中仍然使用。

    我的猜测是,当程序计数器位于函数的第一行时,您已经观察到了监视窗口。 bRunning pInternalManagerObject 超出范围。(尽管由于优化,它们可能会被剥离)。注意,如果一个变量被去掉,即使它被使用,您也无法看到它。

    思想

    • 防御性计划 . 打电话给 assert (或代码库使用的任何断言宏)以检查 帕格 (或任何其他指针)取消引用之前。如果这是一个您在生产中可以合理看到的错误,请进一步执行一步:记录意外的行为,并提前退出函数。 http://www.cplusplus.com/reference/cassert/assert/
    • :在这种情况下,我要赞扬任何愿意把手弄脏的人,但没有必要开始拆开拆卸。在这种情况下,答案就在那里。 https://en.wikipedia.org/wiki/KISS_principle
    • 另外,如果问题的措词更容易阅读,那么你会得到更好的回答。在进入代码之前,请记住解释您正在做什么以及问题是什么。解释您面临的错误(以及任何错误输出),并提出问题。 https://stackoverflow.com/help/how-to-ask