4

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

问题的分解

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

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

  1. 进程启动时 ( CREATE_PROCESS_DEBUG_EVENT)
  2. 加载 DLL 时 ( LOAD_DLL_DEBUG_EVENT)

在 (2) 期间一切正常。但是,在 (1) 期间不能枚举符号。

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

更奇怪的是,调用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
}
4

2 回答 2

4

Brr,真是个笨蛋。我在 VS2017 中对此进行了复制,使用从 Win32 控制台项目模板构建的简单的无操作目标可执行文件。我尝试过的任何事情都无法说服 SymEnumSymbols() 枚举任何符号。接下来我扩展了代码,还捕获了 LOAD_DLL_DEBUG_EVENT 通知:

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);
    }
}

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

结论:PDB文件有问题

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

在 VS2015 中使用 Project > Properties > Linker > Debug > Generate Debug Info = "Optimize for debugging (/DEBUG)"。

在 VS2017 中使用 Project > Properties > Linker > Debug > Generate Debug Info = "Generate Debug Information Optimized for sharing and publishing (/DEBUG:FULL)"。

强调这些设置对目标项目很重要,而不是调试器项目。理想情况下,应该有一个 DbgHelp.dll 版本,它也可以从 PDB 的 fastlink 版本中读取调试信息。我找不到一个,SDK 8.1 和 SDK 10 附带的并没有解决问题。DevDiv 和 Windows 组不能一起工作的另一个例子。

于 2017-07-03T13:00:08.093 回答
1

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

经过一些谷歌搜索后,我在社区论坛上发现了这个通知: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快速链接 PDB。最新的 VS 2017 预发行版将在 VS 安装目录下安装一个新的 dbghelp.dll 以与 fastlink PDB 一起使用。

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

于 2017-07-03T12:46:32.253 回答