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

时间:2019-01-10 09:09:50

标签: c++ visual-studio debugging visual-studio-2017 dump

我正在调试完整的内存转储(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正在被强制转换然后被使用,因此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:     }

我想这意味着可以在寄存器pArg中找到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

这是否意味着pArg确实不是NULL? (对不起,但是我没有组装调试的经验)

1 个答案:

答案 0 :(得分:1)

  

这是否意味着pArg确实不是NULL?

不,这并不意味着; pArg为空。监视窗口告诉您,寄存器告诉您。

  

如您所见,pArg被类型转换,然后被使用,所以它是   pArg不能为NULL。

那是不正确的;那不是演员的作用。如果变量为null,则强制转换的结果为null。

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

  

我想这意味着可以在以下位置找到正确的pArg值   注册RDI。

否; pArg已安装到rcx上; mov从右到左起作用。

mov         rcx,rdi
RCX = 0000000000000000

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

  

我可以理解bRunning已被优化,因为不再使用此变量,但这对于pInternalManagerObject是不正确的,   在下一行中仍会使用。

我的猜测是,当程序计数器位于函数的第一行时,您已经观察到监视窗口。 bRunningpInternalManagerObject不在范围内。 (尽管它们可能由于优化而被删除)。请注意,如果删除了一个变量,即使使用该变量也将看不到它。

想法

  • 防御性程序。调用assert(或代码库使用的任何断言宏),以便在取消引用之前检查pArg(或任何其他指针)的值。如果您在生产中可以合理地看到此错误,请进一步执行以下操作:记录意外的行为并尽早使用该功能。 http://www.cplusplus.com/reference/cassert/assert/
  • 亲吻:虽然我要赞扬愿意在这种情况下弄脏“手”的任何人,但是没有必要开始揭开反汇编。在这种情况下,答案就在那里。 https://en.wikipedia.org/wiki/KISS_principle
  • 此外,如果以易于理解的方式表达问题,您将在SO上获得更好的答复。在编写代码之前,请记住先解释一下您在做什么,以及问题出在哪里。说明您面临的故障(以及任何错误输出),并提出问题。 https://stackoverflow.com/help/how-to-ask