我试图枚举已加载的DLL中的符号。对于那些感兴趣的人,这是CPPCoverage project的一部分,对于某些功能,我需要符号数据。
问题分类
当进程启动或加载DLL时,需要为已计划的某些新功能枚举符号。
基本上,创建了一个流程,dbghelp
用于获取符号信息。接下来,使用SymEnumSymbols
迭代符号。发生这种情况有两个时刻:
CREATE_PROCESS_DEBUG_EVENT
)LOAD_DLL_DEBUG_EVENT
)在(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
}
答案 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
链接时,一切都会正常。