SymEnumSymbols返回ERROR_SUCCESS但没有结果

时间:2017-06-30 08:47:31

标签: c++ winapi dbghelp

我试图枚举已加载的DLL中的符号。对于那些感兴趣的人,这是CPPCoverage project的一部分,对于某些功能,我需要符号数据。

问题分类

当进程启动或加载DLL时,需要为已计划的某些新功能枚举符号。

基本上,创建了一个流程,dbghelp用于获取符号信息。接下来,使用SymEnumSymbols迭代符号。发生这种情况有两个时刻:

  1. 进程开始时(CREATE_PROCESS_DEBUG_EVENT
  2. 加载DLL时(LOAD_DLL_DEBUG_EVENT
  3. 在(2)期间一切正常。但是,在(1)期间无法枚举符号。

    行为是一切正常,直到SymEnumSymbols调用。返回值告诉我出现错误,但GetLastError返回SUCCESS。此外,不会调用回调函数。

    为了让它变得更奇怪,对SymGetSymFromName 的调用实际上有效。

    最小的测试用例

    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
    }
    

2 个答案:

答案 0 :(得分:3)

Brr,非常棒的。我在VS2017中获得了一个repro,使用从Win32 Console项目模板构建的简单的do-nothing目标可执行文件。我试过的任何东西都不能说服SymEnumSymbols()枚举任何符号。我接下来扩展了代码,同时捕获了LOAD_DLL_DEBUG_EVENT通知:

UPDATE YourTable 
   SET COUNT_COLUMN = (SELECT MAX(COUNT_COLUMN) + 0.5 
                         FROM YourTable
                       )
 WHERE "Your condition for the current record";

除了在SymInitialize()中正确设置符号搜索路径外,它还能很好地正确列出ntdll.dll等中的符号。

结论: PDB文件存在问题

付清了。从VS2015开始,微软一直在修改PDB文件生成。他们添加了/DEBUG:FASTLINK option。请注意,链接的文档具有误导性,它也是VS2015中的默认值。操作系统的DbgHelp.dll版本无法正确枚举生成的PDB文件。 GetLastError()代码非常具有误导性,我花了太多时间在它上面,我认为它只是表示“我没有成功枚举”。请注意如何为其他DbgHelp api函数(如SymSetContext和SymLoadModuleEx)记录此代码。

在VS2015中使用Project&gt;属性&gt;链接器&gt;调试&gt;生成调试信息=“优化调试(/ DEBUG)”。

在VS2017中使用Project&gt;属性&gt;链接器&gt;调试&gt;生成调试信息=“生成为共享和发布而优化的调试信息(/ DEBUG:FULL)”。

强调这些设置对目标项目很重要,而不是调试器项目。理想情况下,DbgHelp.dll版本也可以从PDB的快速链接版本读取调试信息。我找不到一个,SDK 8.1和SDK 10附带的那些没有解决问题。另一个DevDiv和Windows小组没有合作的案例。

答案 1 :(得分:1)

在@SimonMournier发表评论后,我进行了很多其他测试。最终,我能够弄清楚这里的问题是什么。事实证明,Visual Studio中的链接器标志/DEBUG:FastLink实际上导致了问题。

在谷歌发布之后,我在社区论坛上发现了这个通知:https://developercommunity.visualstudio.com/content/problem/4631/dia-sdk-still-doesnt-support-debugfastlink.html

  

[...] Windows调试器团队已被告知使用VS 2017 PDB / DIA静态库构建新的dbghelp.dll,Windows SDK(或调试器工具包)的下一个公开发行版将包含能够使用的dbghelp.dll处理fastlink PDB。最新的VS 2017预发布版将在VS安装目录下安装一个新的dbghelp.dll,该目录适用于fastlink PDB。

因此,简而言之,它只是不能使用Visual Studio 2015,因为DIA根本不支持它。当我们升级到VS2017时,它会自动修复。此外,当与/DEBUG链接时,一切都会正常。