visual-studiodebuggingdebug-symbolssymbol-server

Where does Visual Studio get the hash for my DLL for looking up symbols?


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?


Solution

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