【问题标题】:Determining source code filename and line for function using Visual Studio PDB使用 Visual Studio PDB 确定函数的源代码文件名和行
【发布时间】:2021-09-25 11:17:33
【问题描述】:

我正在尝试使用函数定义的地址和由 Visual Studio 为定义函数的模块生成的 pdb 数据库文件,以编程方式确定函数定义的源代码的文件名和行。

例如,我在模块shared.dll 中定义了一个函数Lua::asset::get_supported_export_file_extensions,我想确定它的源代码位置。

为了得到函数的相对虚拟地址(rva),我从绝对虚拟函数地址中减去模块基地址,如下所示:

static MODULEENTRY32 GetModuleInfo(std::uint32_t ProcessID, const char* ModuleName)
{
    void* hSnap = nullptr;
    MODULEENTRY32 Mod32 = {0};

    if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessID)) == INVALID_HANDLE_VALUE)
        return Mod32;

    Mod32.dwSize = sizeof(MODULEENTRY32);
    while (Module32Next(hSnap, &Mod32))
    {
        if (!strcmp(ModuleName, Mod32.szModule))
        {
            CloseHandle(hSnap);
            return Mod32;
        }
    }

    CloseHandle(hSnap);
    return {0};
}
int main(int argc,char *argv[])
{
    auto dllInfo = GetModuleInfo(GetCurrentProcessId(),"shared.dll");
    auto rva = (DWORD)((uint64_t)&Lua::asset::get_supported_export_file_extensions -(uint64_t)dllInfo.modBaseAddr);
    [...]
}

这给了我函数的 rva 0x44508

为了确认地址,我尝试使用 Windows SDK 中的dbh debugging tool 来查找函数的地址(以及模块的基地址):

shared [1000000]: enum shared!*get_supported_export_file_extensions*

 index            address     name
     1            1e376c0 :   `Lua::asset::get_supported_export_file_extensions'::`1'::dtor$0
     3            18409c0 :   Lua::asset::get_supported_export_file_extensions

shared [1000000]: scope 18409c0

   name : path\to\object_files\lasset.obj
   addr :        0
   size : 0
  flags : 0
   type : 0
modbase :  1000000
  value :        0
    reg : 0
  scope : SymTagNull (0)
    tag : SymTagCompiland (2)
  index : 6

如果我从函数地址中减去基地址,我希望它给我相同的 rva,但它给我的 rva 为 0x18409c0 -0x1000000 = 0x8409c0

为方便起见,我将地址称为:

0x44508 = calculated address
0x8409c0 = dbh address

然后我使用Debug Interface Access SDK 在 pdb 中查找这两个地址以确定我得到不同结果的原因:

static BOOL find_function_in_pdb(DWORD rva,enum SymTagEnum tag)
{
    std::string pdbFilePath = "path/to/shared.pdb";
    CComPtr<IDiaDataSource> pSource;
    if(FAILED(CoInitializeEx(NULL,COINIT_MULTITHREADED)))
        return FALSE;
    auto hr = CoCreateInstance(
        CLSID_DiaSource,
        NULL,
        CLSCTX_INPROC_SERVER,
        __uuidof(IDiaDataSource),
        (void **) &pSource
    );
    if(FAILED(hr))
        return FALSE;

    wchar_t wszFilename[_MAX_PATH];
    mbstowcs(wszFilename,pdbFilePath.data(),sizeof(wszFilename) /sizeof(wszFilename[0]));
    if(FAILED(pSource->loadDataFromPdb(wszFilename)))
        return FALSE;

    IDiaSession *session;
    IDiaSymbol *globalSymbol = nullptr;
    IDiaEnumTables *enumTables = nullptr;
    IDiaEnumSymbolsByAddr *enumSymbolsByAddr = nullptr;
    if(FAILED(pSource->openSession(&session))) 
        return FALSE;

    if(FAILED(session->get_globalScope(&globalSymbol)))
        return FALSE;

    if(FAILED(session->getEnumTables(&enumTables)))
        return FALSE;

    if(FAILED(session->getSymbolsByAddr(&enumSymbolsByAddr)))
        return FALSE;

    IDiaSymbol *symbol;
    if(session->findSymbolByRVA(rva,tag,&symbol) == S_OK)
    {
        BSTR name;
        symbol->get_name(&name);
        std::cout<<"Name: "<<ConvertBSTRToMBS(name)<<std::endl;

        ULONGLONG length = 0;
        if(symbol->get_length(&length) == S_OK)
        {
            IDiaEnumLineNumbers *lineNums[100];
            if(session->findLinesByRVA(rva,length,lineNums) == S_OK)
            {
                auto &l = lineNums[0];
                CComPtr<IDiaLineNumber> line;
                IDiaLineNumber *lineNum;
                ULONG fetched = 0;
                for(uint8_t i=0;i<5;++i) {
                if(l->Next(i,&lineNum,&fetched) == S_OK && fetched == 1)
                {
                    DWORD l;
                    IDiaSourceFile *srcFile;
                    if(lineNum->get_sourceFile(&srcFile) == S_OK)
                    {
                        BSTR fileName;
                        srcFile->get_fileName(&fileName);
                        std::cout<<"File: "<<ConvertBSTRToMBS(fileName)<<std::endl;
                    }
                    if(lineNum->get_lineNumber(&l) == S_OK)
                        std::cout<<"Line: "<<+l<<std::endl;
                    
                }
                }
            }
        }
    }

    return TRUE;
}

int main(int argc,char *argv[])
{
    find_function_in_pdb(0x44508 /* calculated address */,SymTagEnum::SymTagPublicSymbol);
    find_function_in_pdb(0x8409c0 /* dbh address */,SymTagEnum::SymTagFunction);
    [...]
}

它确实找到了两个地址,并且都指向一个名称与我的函数匹配的符号,但是计算地址处的符号是 SymTagPublicSymbol,而 dbh 地址处的符号是 SymTagFunction

我猜这意味着计算出的地址是针对公共符号的,而 dbh 地址是针对私有符号的? (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/public-and-private-symbols)

到目前为止一切都很好,问题是公共符号没有有任何与之相关的源代码信息,但私有符号有。假设我到目前为止是正确的(我不太确定),我的问题归结为:

如何从公共符号/地址中获取私有符号/地址?我需要一个可以以编程方式实现的解决方案。

【问题讨论】:

    标签: c++ visual-studio debugging winapi pdb-files


    【解决方案1】:

    经过更多的实验,我找到了解决方案:

    IDiaSymbol *publicSymbol;
    DWORD publicRva = 0x44508;
    if(session->findSymbolByRVA(publicRva,SymTagEnum::SymTagPublicSymbol,&publicSymbol) == S_OK)
    {
        DWORD privateRva;
        IDiaSymbol *privateSymbol;
        if(
            publicSymbol->get_targetRelativeVirtualAddress(&privateRva) == S_OK &&
            session->findSymbolByRVA(privateRva,SymTagEnum::SymTagFunction,&privateSymbol) == S_OK
        )
        {
            // Do stuff with private symbol
        }
    }
    

    get_targetRelativeVirtualAddress 给了我0x8409c0,包含源代码信息的私有符号的地址。

    至于为什么会这样,我不知道。根据文档,get_targetRelativeVirtualAddress 应该只对SymTagThunk 符号有效,并返回“thunk 目标”的 rva。我不认为公共符号 一个 thunk 目标,但它可以正常工作并提供我所需要的。

    【讨论】:

      猜你喜欢
      • 2017-11-07
      • 1970-01-01
      • 2012-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-21
      • 1970-01-01
      • 2010-11-27
      相关资源
      最近更新 更多