I'm setting up our private symbol server. Everything works great. Observe the following Resharper log when I debug into one of our Nuget packages using symbol server:
Searching for 'ClassLibrary1.Class1' type sources in C:\SNIPPED\SymbolCache\ClassLibrary1.pdb\91180103b9def6ca85f41230aaf9a4611\ClassLibrary1.pdb
Downloader: https ://LOCAL_SYMBOL_SVR/app/sources/builds/id-1641/sources/files/ClassLibrary1/Class1.cs -> ok, 268 bytes
See the hash, 91180103b9def6ca85f41230aaf9a4611
? Notice that it is 33 digits.
I figured it might be stored in a PE header, but dumpbin.exe /all DLL
doesn't contain the hash in its output.
Where does that hash come from? Is it stored within the DLL somewhere? If so, how and where is it stored?
if PE build with debug information - must exist 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[];
};
the what you called "hash" this is really not hash formated from guidSig and age as %08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x
the dwSig
must equal to 'SDSR'
and szPdb
stored as utf8 string
code example:
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");
}