2017-06-30 4 views
3

Ich versuche, Symbole aus einer DLL aufzulisten, die ich geladen habe. Für Interessierte ist dies ein Teil der CPPCoverage project, und für einige Funktionen brauche ich Symboldaten.SymEnumSymbols gibt ERROR_SUCCESS zurück, gibt aber keine Ergebnisse.

Aufteilung des Problems

Wenn der Prozess gestartet wird oder eine DLL geladen wird, müssen Symbole für einige der neuen Funktionen aufgezählt werden, die geplant wurde.

Grundsätzlich wird ein Prozess erstellt und dbghelp wird verwendet, um Symbolinformationen zu erhalten. Als nächstes werden Symbole unter Verwendung von SymEnumSymbols iteriert. Es gibt zwei Momente, in denen dies geschieht:

  1. Wenn der Prozess gestartet wird (CREATE_PROCESS_DEBUG_EVENT)
  2. Wenn eine DLL geladen wird (LOAD_DLL_DEBUG_EVENT)

Alles funktioniert gut bei (2). Symbole können jedoch während (1) nicht aufgezählt werden.

Verhalten ist, dass alles gut funktioniert, bis der SymEnumSymbols Anruf. Der Rückgabewert sagt mir, dass es einen Fehler gibt, aber GetLastError gibt SUCCESS zurück. Außerdem wird die Callback-Funktion nicht aufgerufen.

Um es noch komischer zu machen, funktioniert ein Anruf SymGetSymFromNamefunktioniert tatsächlich.

Minimal Testfall

static BOOL CALLBACK EnumerateSymbols(
          PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) 
{ 
    std::cout << "Symbol: " << pSymInfo->Name << std::endl; 
    return TRUE; 
} 

void Test() 
{ 
    SymSetOptions(SYMOPT_LOAD_ANYTHING); 

    STARTUPINFO si; 
    PROCESS_INFORMATION pi; 
    ZeroMemory(&si, sizeof(si)); 
    si.cb = sizeof(si); 
    ZeroMemory(&pi, sizeof(pi)); 

    auto str = "FullPathToSomeExeWithPDB.exe"; 
    auto result = CreateProcess(str, NULL, NULL, NULL, FALSE, 
           DEBUG_PROCESS, NULL, NULL, &si, &pi); 
    if (result == 0) 
    { 
     auto err = GetLastError(); 
     std::cout << "Error running process: " << err << std::endl; 
     return; 
    } 

    if (!SymInitialize(pi.hProcess, NULL, FALSE)) 
    { 
     auto err = GetLastError(); 
     std::cout << "Symbol initialization failed: " << err << std::endl; 
     return; 
    } 

    bool first = false; 
    DEBUG_EVENT debugEvent = { 0 }; 
    while (!first) 
    { 
     if (!WaitForDebugEvent(&debugEvent, INFINITE)) 
     { 
      auto err = GetLastError(); 
      std::cout << "Wait for debug event failed: " << err << std::endl; 
      return; 
     } 
     if (debugEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) 
     { 
      auto dllBase = SymLoadModuleEx(
       pi.hProcess, 
       debugEvent.u.CreateProcessInfo.hFile, 
       str, 
       NULL, 
       reinterpret_cast<DWORD64>(debugEvent.u.CreateProcessInfo.lpBaseOfImage), 
       0, 
       NULL, 
       0); 

      if (!dllBase) 
      { 
       auto err = GetLastError(); 
       std::cout << "Loading the module failed: " << err << std::endl; 
       return; 
      } 

      if (!SymEnumSymbols(pi.hProcess, dllBase, NULL, EnumerateSymbols, nullptr)) 
      { 
       auto err = GetLastError(); 
       std::cout << "Error: " << err << std::endl; 
      } 

      first = true; 
     } 
    } 
    // cleanup code is omitted 
} 
+0

Ok. Sie ** rufen 'GetLastError' zu spät auf. Sie müssen es ** sofort ** nennen, nachdem die Bedingung erfüllt wurde, wo dokumentiert wird, dass sinnvolle Werte zurückgegeben werden. Durchstreichen Sie es nicht mit ** anderem ** anderem Code. – IInspectable

+0

@Intensible Testfälle ... normalerweise würde ich nur eine Ausnahme werfen. Wie auch immer, ich habe es geändert, um es sofort anzurufen. Ändert nichts ... – atlaste

+0

Ich nehme an, dass [CREATE_PROCESS_DEBUG_EVENT] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms679302.aspx) ausgelöst wird, bevor irgendwelche Module geladen wurden : * "Das System generiert dieses Debugging-Ereignis, bevor der Prozess im Benutzermodus ausgeführt wird" *. [Warum bekomme ich ERROR_INVALID_HANDLE von GetModuleFileNameEx, wenn ich weiß, dass das Prozesshandle gültig ist?] (Https://blogs.msdn.microsoft.com/oldnewthing/20150716-00/?p=45131) scheint relevant. – IInspectable

Antwort

2

Brr, ziemlich stümper. Ich habe dafür in VS2017 eine Repro erstellt, die eine einfache Do-Nothing-Zielprogrammdatei verwendet, die aus der Win32 Console-Projektvorlage erstellt wurde. Nichts, was ich versuchte, konnte SymEnumSymbols() dazu bringen, irgendwelche Symbole aufzuzählen. I erweitert als nächstes auf dem Code, auch die LOAD_DLL_DEBUG_EVENT Benachrichtigung Trapping:

if (debugEvent.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) { 
    auto base = SymLoadModule64(pi.hProcess, debugEvent.u.LoadDll.hFile, NULL, NULL, NULL, 0); 
    if (!base) { 
     auto err = GetLastError(); 
     std::cout << err << std::endl; 
    } 
    else { 
     CloseHandle(debugEvent.u.LoadDll.hFile); 
     SymEnumSymbols(pi.hProcess, base, NULL, EnumerateSymbols, nullptr); 
    } 
} 

Zusammen mit der Einstellung der Symbolsuchpfad richtig in SymInitialize(), die gerade fein gearbeitet und aufgelistet richtig die Symbole in ntdll.dll usw.

Fazit: es ist etwas falsch mit dem HVE

diese Datei-off bezahlt. Microsoft hat mit der PDB-Dateigeneration angefangen, die in VS2015 beginnt. Sie fügten die /DEBUG:FASTLINK option hinzu. Beachten Sie, dass die verknüpften Dokumente irreführend sind. Dies ist auch der Standardwert in VS2015. Die resultierende PDB-Datei kann nicht ordnungsgemäß von der Betriebssystemversion von DbgHelp.dll aufgelistet werden. Der GetLastError() - Code war ziemlich irreführend und ich habe viel zu viel Zeit darauf verwendet, ich denke, er zeigt nur an, dass "ich erfolgreich nichts aufgezählt hat". Beachten Sie, dass dieser Code für andere DbgHelp-API-Funktionen wie SymSetContext und SymLoadModuleEx dokumentiert ist.

In VS2015 verwenden Sie Projekt> Eigenschaften> Linker> Debug> Debug Debuggen Info = "Optimieren für das Debuggen (/ DEBUG)".

Verwenden Sie in VS2017 Projekt> Eigenschaften> Linker> Debug> Debug generieren Info = "Debug-Informationen generieren, die für das Teilen und Veröffentlichen optimiert sind (/ DEBUG: FULL)".

Hervorheben, dass diese Einstellungen im Zielprojekt und nicht im Debugger-Projekt eine Rolle spielen. Idealerweise würde es eine DbgHelp.dll-Version geben, die auch Debug-Informationen von der Fastlink-Version der PDB lesen könnte. Ich konnte keine finden, die mit SDK 8.1 und SDK 10 kamen, lösten das Problem nicht. Ein weiterer Fall, in dem die Gruppen DevDiv und Windows nicht zusammenarbeiten.

+0

Danke. Vor ein paar Minuten kam ich zu demselben Schluss. Was mir auffällt, ist, dass VS2017 es nicht unterstützt - ich hoffte, dass das jetzt behoben sein würde. Der Beitrag von 25-02, den ich oben verlinkt habe, besagt: "Die letzte Vorabversion von VS 2017 würde eine neue dbghelp.dll unter dem VS-Installationsverzeichnis installieren, das mit Fastlink-PDBs funktioniert." - Also ich denke, das ist das für jetzt ... – atlaste

+1

Das ist einfach nicht wahr, keine neue dbghelp.dll. YongKang Zhu bestätigt dies, indem er feststellt, dass DIA auch nicht aufzählen kann. Ich habe nicht dokumentiert, dass das Auswählen eines bestimmten Symbols funktioniert hat, indem "Main" für das 3. Argument übergeben wurde, anstatt NULL zu vermeiden. –

1

Nach dem Kommentar von @SimonMournier lief ich viele andere Tests. Irgendwann konnte ich herausfinden, worum es hier geht. Wie sich herausstellt, verursacht das Linker-Flag /DEBUG:FastLink in Visual Studio tatsächlich das Problem.

Nach einigen google'ing ich diesen Hinweis auf den Community-Foren gefunden: https://developercommunity.visualstudio.com/content/problem/4631/dia-sdk-still-doesnt-support-debugfastlink.html

[...] Windows-Debuggers Team informiert wurde ein neues dbghelp.dll mit VS 2017 PDB/DIA statisch zu bauen Bibliotheken und die nächste öffentliche Version von Windows SDK (oder Debugger-Kits) enthalten dbghelp.dll, das mit Fastlink-PDBs umgehen kann. Die neueste Vorabversion von VS 2017 würde eine neue dbghelp.dll im VS-Installationsverzeichnis installieren, die mit Fastlink-PDBs funktioniert.

Also, kurz gesagt, es wird einfach nicht mit Visual Studio 2015 funktionieren, weil DIA es einfach nicht unterstützt. Wenn wir auf VS2017 upgraden, wird es automatisch repariert. Auch, wenn Sie mit /DEBUG verknüpfen, wird alles gut funktionieren.

Verwandte Themen