使用DIA接口获取符号的偏移地址
#include <windows.h>
// DIA headers must come after windows headers.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#endif
#include <dia2.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include <diacreate.h>
//
#include <wrl/client.h>
#include <iostream>
#include "base/at_exit.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/types/expected.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_com_initializer.h"
namespace switches {
constexpr char kSymbolName[] = "symbol";
constexpr char kSymbolServer[] = "srv";
constexpr char kPdb[] = "pdb";
constexpr char kImage[] = "image";
} // namespace switches
namespace {
// clang-format off
constexpr wchar_t kDefaultSymbolSearchPath[] =
L"SRV*D:\\SYMBOL.NT*https://msdl.microsoft.com/download/symbols"
L";SRV*D:\\SYMBOL.NT*https://chromium-browser-symsrv.commondatastorage.googleapis.com";
// clang-format on
const std::wstring& SymbolServer() {
static base::NoDestructor<std::wstring> srv([]{
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kSymbolServer)) {
return std::wstring(
command_line->GetSwitchValueNative(switches::kSymbolServer));
}
return std::wstring(kDefaultSymbolSearchPath);
}());
return *srv;
}
HRESULT CreateDiaSessionFromPdb(const std::wstring& pdb_path,
IDiaSession** dia_session) {
Microsoft::WRL::ComPtr<IDiaDataSource> dia_data_source;
HRESULT hr = ::NoRegCoCreate(L"msdia140.dll", __uuidof(DiaSource),
IID_PPV_ARGS(&dia_data_source));
if (FAILED(hr)) {
return hr;
}
hr = dia_data_source->loadDataFromPdb(pdb_path.c_str());
if (FAILED(hr)) {
return hr;
}
hr = dia_data_source->openSession(dia_session);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
HRESULT CreateDiaSessionForImage(const std::wstring& image_path,
IDiaSession** dia_session) {
Microsoft::WRL::ComPtr<IDiaDataSource> dia_data_source;
HRESULT hr = ::NoRegCoCreate(L"msdia140.dll", __uuidof(DiaSource),
IID_PPV_ARGS(&dia_data_source));
if (FAILED(hr)) {
return hr;
}
hr = dia_data_source->loadDataForExe(image_path.c_str(),
SymbolServer().c_str(), nullptr);
if (FAILED(hr)) {
return hr;
}
hr = dia_data_source->openSession(dia_session);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
struct RAVResult {
RAVResult(ULONGLONG rav, std::vector<std::string> lines)
: rav(rav), lines(std::move(lines)) {}
RAVResult(const RAVResult&) = default;
RAVResult& operator=(const RAVResult&) = default;
~RAVResult() = default;
ULONGLONG rav;
std::vector<std::string> lines;
};
base::expected<std::vector<std::string>, HRESULT> GetSymbolLocation(
IDiaSession* session,
IDiaSymbol* symbol) {
DWORD rva = 0;
HRESULT hr = symbol->get_relativeVirtualAddress(&rva);
if (FAILED(hr)) {
return base::unexpected(hr);
}
Microsoft::WRL::ComPtr<IDiaEnumLineNumbers> lines;
ULONGLONG length = 0;
hr = symbol->get_length(&length);
if (FAILED(hr)) {
return base::unexpected(hr);
}
hr = session->findLinesByRVA(rva, (DWORD)length, &lines);
if (FAILED(hr)) {
return base::unexpected(hr);
}
std::vector<std::string> result;
ULONG c = 0;
Microsoft::WRL::ComPtr<IDiaLineNumber> line;
while (SUCCEEDED(lines->Next(1, line.ReleaseAndGetAddressOf(), &c)) &&
c == 1) {
DWORD line_number;
base::win::ScopedBstr filename;
if (FAILED(line->get_lineNumber(&line_number))) {
continue;
}
Microsoft::WRL::ComPtr<IDiaSourceFile> source_file;
if (FAILED(line->get_sourceFile(source_file.ReleaseAndGetAddressOf()))) {
continue;
}
if (FAILED(source_file->get_fileName(filename.Receive()))) {
continue;
}
if (filename.Get()) {
result.push_back(base::StringPrintf(
"%s(%d)", base::SysWideToUTF8(filename.Get()).c_str(), line_number));
}
}
return base::ok(std::move(result));
}
base::expected<std::vector<RAVResult>, HRESULT> GetRAVForSymbol(IDiaSession* session,
const std::wstring& symbol) {
Microsoft::WRL::ComPtr<IDiaSymbol> global_symbol;
HRESULT hr = session->get_globalScope(global_symbol.GetAddressOf());
if (FAILED(hr)) {
return base::unexpected(hr);
}
Microsoft::WRL::ComPtr<IDiaEnumSymbols> dia_enum_symbols;
hr = global_symbol->findChildrenEx(SymTagFunction, symbol.c_str(), nsNone,
dia_enum_symbols.GetAddressOf());
if (FAILED(hr)) {
return base::unexpected(hr);
}
std::vector<RAVResult> results;
ULONG celt = 0;
Microsoft::WRL::ComPtr<IDiaSymbol> dia_symbol;
while (SUCCEEDED(dia_enum_symbols->Next(
1, dia_symbol.ReleaseAndGetAddressOf(), &celt)) &&
celt == 1) {
ULONGLONG rav;
dia_symbol->get_virtualAddress(&rav);
auto lines = GetSymbolLocation(session, dia_symbol.Get());
results.emplace_back(rav, lines.has_value() ? std::move(lines.value())
: std::vector<std::string>{});
}
return base::ok(std::move(results));
}
void PrintUsage() {
std::cout << "Usage: symbol.exe --" << switches::kSymbolName
<< "=<symbol> (--" << switches::kPdb << "=<pdb_path> | --"
<< switches::kImage << "=<image_path>) [--"
<< switches::kSymbolServer << "=<server_path>]" << std::endl
<< std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --" << switches::kSymbolName
<< ": The name of the symbol to find." << std::endl;
std::cout << " --" << switches::kPdb
<< ": Path to the PDB file. Mutually exclusive with --"
<< switches::kImage << "." << std::endl;
std::cout << " --" << switches::kImage
<< ": Path to the image (exe/dll). Mutually exclusive with --"
<< switches::kPdb << "." << std::endl;
std::cout << " --" << switches::kSymbolServer
<< ": Optional. Symbol server path to use with --"
<< switches::kImage << "." << std::endl;
}
}
int main(int argc, char** argv) {
base::AtExitManager at_exit;
base::CommandLine::Init(0, nullptr);
base::win::ScopedCOMInitializer scoped_com_initializer;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if ((!command_line->HasSwitch(switches::kImage) &&
!command_line->HasSwitch(switches::kPdb)) ||
!command_line->HasSwitch(switches::kSymbolName)) {
PrintUsage();
return -1;
}
Microsoft::WRL::ComPtr<IDiaSession> dia_session;
if (command_line->HasSwitch(switches::kPdb)) {
if (FAILED(CreateDiaSessionFromPdb(
command_line->GetSwitchValueNative(switches::kPdb),
dia_session.ReleaseAndGetAddressOf()))) {
return -1;
}
} else {
CHECK(command_line->HasSwitch(switches::kImage));
if (FAILED(CreateDiaSessionForImage(
command_line->GetSwitchValueNative(switches::kImage),
dia_session.ReleaseAndGetAddressOf()))) {
return -1;
}
}
CHECK(dia_session);
CHECK(command_line->HasSwitch(switches::kSymbolName));
auto result = GetRAVForSymbol(
dia_session.Get(),
command_line->GetSwitchValueNative(switches::kSymbolName));
if (result.has_value()) {
for (const auto& rav : result.value()) {
std::cout << "RAV: " << rav.rav;
if (!rav.lines.empty()) {
std::cout << "\n\t" << rav.lines[0];
}
std::cout<< std::endl;
}
} else {
return result.error();
}
return 0;
}