Как отлаживаемые переменные могут быть NULL, если исходный код доказывает обратное - PullRequest
0 голосов
/ 10 января 2019

Я отлаживаю полный дамп памяти (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 голосов
/ 10 января 2019

Значит ли это, что pArg действительно не равен NULL?

Нет, это не значит, что; pArg равно нулю. Окно часов говорит вам, регистры говорят вам.

Как вы можете видеть, pArg вживляется, а затем используется, поэтому для pArg невозможно быть 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, который все еще используется в следующей строке.

Я предполагаю, что вы наблюдали окно просмотра, когда счетчик программ находится в первой строке вашей функции. bRunning и pInternalManagerObject находятся вне области видимости. (Хотя они потенциально могут быть удалены из-за оптимизации). Обратите внимание, что если переменная будет удалена, вы не сможете увидеть ее, даже если она используется.

Мысли

  • Программа в обороне . сделать вызов assert (или любой макрос подтверждения, который использует кодовая база), чтобы проверить значение pArg (или любой другой указатель) перед разыменованием. Если это ошибка, которую вы могли разумно увидеть в производственной среде, сделайте еще один шаг: зарегистрируйте непредвиденное поведение и опустите функцию. http://www.cplusplus.com/reference/cassert/assert/
  • ПОЦЕЛУЙ : Хотя я бы похвалил любого, кто хочет испачкать свои "руки", в этом случае просто не нужно начинать взламывать разборку. В этом случае ответ прямо здесь. https://en.wikipedia.org/wiki/KISS_principle
  • Кроме того, вы получите лучший ответ на SO, если вопрос сформулирован так, чтобы его было легче читать. Не забудьте объяснить, что вы делаете и в чем проблема, прежде чем приступать к написанию кода. Объясните, с какой ошибкой вы сталкиваетесь (вместе с любыми сообщениями об ошибках), и задайте вопрос https://stackoverflow.com/help/how-to-ask
...