How do you use SymLoadModuleEx to load a PDB file?

5.3k views Asked by At

I'm trying to call SymLoadModuleEx to load the symbols from a PDB file and then use SymFromAddr to look up symbols from that PDB. However, I can't figure out what to pass for the parameters BaseOfDll and DllSize -- the documentation explicitly says that when loading a PDB file, these parameters can't be 0, and indeed attempting to pass 0 results in it failing with ERROR_INVALID_PARAMETER.

Here's what my code looks like:

SymSetOptions(SYMOPT_LOAD_LINES);
HANDLE hprocess = GetCurrentProcess();
if (!SymInitialize(hprocess, NULL, FALSE))
    die("SymInitialize");

if(SymLoadModuleEx(hprocess, NULL, "full path to some PDB file.pdb", NULL,
                   0,  // What to pass here?
                   0,  // What to pass here?
                   NULL, 0) == 0)
{
    die("SymLoadModuleEx");
}

How do you figure out what BaseOfDll and DllSize to pass in when loading a PDB file? The PDB file in question is the symbol file for a different program executable (not a DLL), and just for the sake of argument, assume that you don't have access to the original EXE from which the PDB was generated.

Alternatively, is there a better method of looking up the symbols corresponding to a given address from a PDB file?

3

There are 3 answers

0
Chris Schmich On BEST ANSWER

dbghelp.dll and the Sym* methods here make use of the Debug Interface Access (DIA) SDK.1
DIA itself is COM-based and much more flexible than what DbgHelp offers.

Specifically, to load a known PDB and lookup a symbol based on an address, you can do the following:

  1. CoCreate a DIA data source (see the "Example" section here).
  2. Use IDiaDataSource::loadDataFromPdb to load a specific PDB (DLL size and base address are not needed).
  3. Use IDiaDataSource::openSession to get the IDiaSession for your data source.
  4. Depending on if you have an absolute virtual address (VA) or relative virtual address (RVA), you can use findSymbolByVA or findSymbolByRVA, respectively, to get the IDiaSymbol associated with that address.
  5. Finally, you can use IDiaSymbol::get_name to get the function name containing the address you specified.

None of this requires the original image; only the PDB is required. Assuming you're using Visual Studio, the headers and libraries for DIA are available under (for example):
C:\Program Files (x86)\Microsoft Visual Studio 10.0\DIA SDK.

0
Vyacheslav Putsenko On

I don't have permitions to comment, so will do this in separate answer.

  1. Yes, DbgHelp is a wrapper around DIA, only in terms of static lib. DIA is statically linked into DbgHelp.dll. DbgHelp directly calls Dia's COM class factory (IClassFactory) implementation bypassing COM. I'm talking about 6.1.7601.17514 version. So DbgHelp.dll is self-contained (in combination with symcrv.dll and srcsrv.dll)
  2. Dia COM objects are shipped with Visual Studio (same location with dbgeng.dll), so solution we not work on envs where VS is not installed (but you still could try to use msdia120.dll as private assembly pointing to it via ActivateActCtx, but also need to deploy dependencies, if they present)
  3. DbgHelp is compact and recommended to distribute private copies with your application by Microsoft. See "The redistribution policies for these included DLLs were specifically designed to make it as easy as possible for people to include these files in their own packages and release"

  4. I didn't find the way how to download PDB files using DIA interfaces. Sym API allows this. it delegates calls to SymSrv.dll.

So original question is still actual.

0
user2673718 On

The Dia2Dump sample works well for me in resolving relative virtual address (eip-program load address) to pdb for unresolved function pointers. This is the way I try it:

    DWORD64  dwAddress = _wcstoui64(argv[i], NULL, 16);
    DWORD64 dwRVA  = dwAddress - dwLoadAddress;
    long displacement = 0;
    IDiaSymbol* pFunc = 0;
    error = (DWORD)g_pDiaSession->findSymbolByRVAEx(dwRVA, SymTagFunction, &pFunc, &displacement );

    if (!error && pFunc)
    {
        BSTR bstrName;

        if (pFunc->get_name(&bstrName) != S_OK) {
            wprintf(L"(???)\n\n");
        }

        else {
            wprintf(L"%s \n\n", bstrName);
            if (displacement)
                wprintf(L"+ 0x%x \n\n", displacement);
            else
                wprintf(L" \n\n");
            SysFreeString(bstrName);
        }
    }

For example: Function: [00447B60][0001:00446B60] ServerConfig::getSSLConfig(public: struct ssl_config __cdecl ServerConfig::getSSLConfig(void) __ptr64)

Here the RVA is 00447B60 [ eip - Process Load Address ] the Segment is 0001 the offset is 00446B60