Visual Studio 从哪里获取我的 DLL 的散列来查找符号?
Where does Visual Studio get the hash for my DLL for looking up symbols?
我正在设置我们的私有符号服务器。一切都很好。当我使用符号服务器调试我们的 Nuget 包之一时,观察以下 Resharper 日志:
Searching for 'ClassLibrary1.Class1' type sources in C:\SNIPPED\SymbolCache\ClassLibrary1.pdb180103b9def6ca85f41230aaf9a4611\ClassLibrary1.pdb
Downloader: https ://LOCAL_SYMBOL_SVR/app/sources/builds/id-1641/sources/files/ClassLibrary1/Class1.cs -> ok, 268 bytes
看到哈希,91180103b9def6ca85f41230aaf9a4611
?请注意,它是 33 位数字。
我认为它可能存储在 PE header 中,但 dumpbin.exe /all DLL
在其输出中不包含哈希。
该哈希从何而来?它是否存储在 DLL 的某处?如果是这样,它是如何存储的,存储在哪里?
if PE 使用调试信息构建 - 必须存在 IMAGE_DEBUG_DIRECTORY
in it with IMAGE_DEBUG_TYPE_CODEVIEW
. so first debugger search for array of IMAGE_DEBUG_DIRECTORY
elements (it can be multiple). this array located at IMAGE_DIRECTORY_ENTRY_DEBUG
data directory. for IMAGE_DEBUG_TYPE_CODEVIEW
debug info now locate in format RSDS
struct RSDSI // RSDS debug info
{
DWORD dwSig; // RSDSI
GUID guidSig;
DWORD age;
char szPdb[];
};
你所说的 "hash" 这实际上不是从 guidSig 和 age 格式化为 %08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x
的散列
dwSig
必须等于 'SDSR'
和 szPdb
存储为 utf8 字符串
代码示例:
ULONG FormatPdbPath(PWSTR* ppdbPath, PCWSTR SymbolsPath, PCSTR PdbFileName, LPGUID Signature, DWORD Age)
{
ULONG UTF8StringByteCount = (ULONG)strlen(PdbFileName) + 1;
ULONG UnicodeStringLen = MultiByteToWideChar(CP_UTF8, 0, PdbFileName, UTF8StringByteCount, 0, 0);
if (!UnicodeStringLen)
{
return ERROR_GEN_FAILURE;
}
if (UnicodeStringLen >= MAXSHORT)
{
return ERROR_FILENAME_EXCED_RANGE;
}
PWSTR FileName = (PWSTR)alloca(UnicodeStringLen * sizeof(WCHAR));
UnicodeStringLen = MultiByteToWideChar(CP_UTF8, 0, PdbFileName, UTF8StringByteCount, FileName, UnicodeStringLen);
if (!UnicodeStringLen)
{
return ERROR_GEN_FAILURE;
}
if (PWSTR pdbPath = new WCHAR[2 * UnicodeStringLen + wcslen(SymbolsPath) + 42])
{
*ppdbPath = pdbPath;
swprintf(pdbPath, L"%s\%s\%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x\%s",
SymbolsPath, FileName,
Signature->Data1, Signature->Data2, Signature->Data3,
Signature->Data4[0], Signature->Data4[1], Signature->Data4[2], Signature->Data4[3],
Signature->Data4[4], Signature->Data4[5], Signature->Data4[6], Signature->Data4[7],
Age, FileName);
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
ULONG FormatPdbPath(PWSTR* ppdbPath, PCWSTR SymbolsPath, PCWSTR lpszName)
{
HMODULE hmod = LoadLibraryExW(lpszName, 0, LOAD_LIBRARY_AS_DATAFILE);
if (!hmod) return GetLastError();
ULONG status = ERROR_NOT_FOUND;
DWORD cb;
BOOLEAN bMappedAsImage = !((DWORD_PTR)hmod & (PAGE_SIZE - 1));
PIMAGE_DEBUG_DIRECTORY pidd = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData(hmod, bMappedAsImage, IMAGE_DIRECTORY_ENTRY_DEBUG, &cb);
if (pidd && cb && !(cb % sizeof(IMAGE_DEBUG_DIRECTORY)))
{
do
{
struct RSDSI // RSDS debug info
{
DWORD dwSig; // RSDSI
GUID guidSig;
DWORD age;
char szPdb[];
};
if (pidd->Type == IMAGE_DEBUG_TYPE_CODEVIEW && pidd->SizeOfData > sizeof(RSDSI))
{
if (DWORD PointerToRawData = bMappedAsImage ? pidd->AddressOfRawData : pidd->PointerToRawData)
{
RSDSI* lpcvh = (RSDSI*)RtlOffsetToPointer(PAGE_ALIGN(hmod), PointerToRawData);
if (lpcvh->dwSig == 'SDSR')
{
PCSTR szPdb = lpcvh->szPdb, c = strrchr(szPdb, L'\');
if (c)
{
szPdb = c + 1;
}
status = FormatPdbPath(ppdbPath, SymbolsPath, szPdb, &lpcvh->guidSig, lpcvh->age);
break;
}
}
}
} while (pidd++, cb -= sizeof(IMAGE_DEBUG_DIRECTORY));
}
FreeLibrary(hmod);
return status;
}
void test(PCWSTR SymbolsPath, PCWSTR lpszName)
{
PWSTR pdbPath;
if (!FormatPdbPath(&pdbPath, SymbolsPath, lpszName))
{
DbgPrint("%S\n", pdbPath);
delete [] pdbPath;
}
}
void test2(PCWSTR SymbolsPath = L"C:\SNIPPED\SymbolCache")
{
WCHAR myExe[MAX_PATH];
GetModuleFileNameW(0, myExe, RTL_NUMBER_OF(myExe));
test(SymbolsPath, myExe);
test(SymbolsPath, L"hal.dll");
test(SymbolsPath, L"drivers/ntfs.sys");
test(SymbolsPath, L"kernel32.dll");
}
我正在设置我们的私有符号服务器。一切都很好。当我使用符号服务器调试我们的 Nuget 包之一时,观察以下 Resharper 日志:
Searching for 'ClassLibrary1.Class1' type sources in C:\SNIPPED\SymbolCache\ClassLibrary1.pdb180103b9def6ca85f41230aaf9a4611\ClassLibrary1.pdb
Downloader: https ://LOCAL_SYMBOL_SVR/app/sources/builds/id-1641/sources/files/ClassLibrary1/Class1.cs -> ok, 268 bytes
看到哈希,91180103b9def6ca85f41230aaf9a4611
?请注意,它是 33 位数字。
我认为它可能存储在 PE header 中,但 dumpbin.exe /all DLL
在其输出中不包含哈希。
该哈希从何而来?它是否存储在 DLL 的某处?如果是这样,它是如何存储的,存储在哪里?
if PE 使用调试信息构建 - 必须存在 IMAGE_DEBUG_DIRECTORY
in it with IMAGE_DEBUG_TYPE_CODEVIEW
. so first debugger search for array of IMAGE_DEBUG_DIRECTORY
elements (it can be multiple). this array located at IMAGE_DIRECTORY_ENTRY_DEBUG
data directory. for IMAGE_DEBUG_TYPE_CODEVIEW
debug info now locate in format RSDS
struct RSDSI // RSDS debug info
{
DWORD dwSig; // RSDSI
GUID guidSig;
DWORD age;
char szPdb[];
};
你所说的 "hash" 这实际上不是从 guidSig 和 age 格式化为 %08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x
的散列
dwSig
必须等于 'SDSR'
和 szPdb
存储为 utf8 字符串
代码示例:
ULONG FormatPdbPath(PWSTR* ppdbPath, PCWSTR SymbolsPath, PCSTR PdbFileName, LPGUID Signature, DWORD Age)
{
ULONG UTF8StringByteCount = (ULONG)strlen(PdbFileName) + 1;
ULONG UnicodeStringLen = MultiByteToWideChar(CP_UTF8, 0, PdbFileName, UTF8StringByteCount, 0, 0);
if (!UnicodeStringLen)
{
return ERROR_GEN_FAILURE;
}
if (UnicodeStringLen >= MAXSHORT)
{
return ERROR_FILENAME_EXCED_RANGE;
}
PWSTR FileName = (PWSTR)alloca(UnicodeStringLen * sizeof(WCHAR));
UnicodeStringLen = MultiByteToWideChar(CP_UTF8, 0, PdbFileName, UTF8StringByteCount, FileName, UnicodeStringLen);
if (!UnicodeStringLen)
{
return ERROR_GEN_FAILURE;
}
if (PWSTR pdbPath = new WCHAR[2 * UnicodeStringLen + wcslen(SymbolsPath) + 42])
{
*ppdbPath = pdbPath;
swprintf(pdbPath, L"%s\%s\%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x\%s",
SymbolsPath, FileName,
Signature->Data1, Signature->Data2, Signature->Data3,
Signature->Data4[0], Signature->Data4[1], Signature->Data4[2], Signature->Data4[3],
Signature->Data4[4], Signature->Data4[5], Signature->Data4[6], Signature->Data4[7],
Age, FileName);
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
ULONG FormatPdbPath(PWSTR* ppdbPath, PCWSTR SymbolsPath, PCWSTR lpszName)
{
HMODULE hmod = LoadLibraryExW(lpszName, 0, LOAD_LIBRARY_AS_DATAFILE);
if (!hmod) return GetLastError();
ULONG status = ERROR_NOT_FOUND;
DWORD cb;
BOOLEAN bMappedAsImage = !((DWORD_PTR)hmod & (PAGE_SIZE - 1));
PIMAGE_DEBUG_DIRECTORY pidd = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData(hmod, bMappedAsImage, IMAGE_DIRECTORY_ENTRY_DEBUG, &cb);
if (pidd && cb && !(cb % sizeof(IMAGE_DEBUG_DIRECTORY)))
{
do
{
struct RSDSI // RSDS debug info
{
DWORD dwSig; // RSDSI
GUID guidSig;
DWORD age;
char szPdb[];
};
if (pidd->Type == IMAGE_DEBUG_TYPE_CODEVIEW && pidd->SizeOfData > sizeof(RSDSI))
{
if (DWORD PointerToRawData = bMappedAsImage ? pidd->AddressOfRawData : pidd->PointerToRawData)
{
RSDSI* lpcvh = (RSDSI*)RtlOffsetToPointer(PAGE_ALIGN(hmod), PointerToRawData);
if (lpcvh->dwSig == 'SDSR')
{
PCSTR szPdb = lpcvh->szPdb, c = strrchr(szPdb, L'\');
if (c)
{
szPdb = c + 1;
}
status = FormatPdbPath(ppdbPath, SymbolsPath, szPdb, &lpcvh->guidSig, lpcvh->age);
break;
}
}
}
} while (pidd++, cb -= sizeof(IMAGE_DEBUG_DIRECTORY));
}
FreeLibrary(hmod);
return status;
}
void test(PCWSTR SymbolsPath, PCWSTR lpszName)
{
PWSTR pdbPath;
if (!FormatPdbPath(&pdbPath, SymbolsPath, lpszName))
{
DbgPrint("%S\n", pdbPath);
delete [] pdbPath;
}
}
void test2(PCWSTR SymbolsPath = L"C:\SNIPPED\SymbolCache")
{
WCHAR myExe[MAX_PATH];
GetModuleFileNameW(0, myExe, RTL_NUMBER_OF(myExe));
test(SymbolsPath, myExe);
test(SymbolsPath, L"hal.dll");
test(SymbolsPath, L"drivers/ntfs.sys");
test(SymbolsPath, L"kernel32.dll");
}