From c67055bad3cdfc93e2ac57d87f36c6e0993af690 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Wed, 17 May 2017 06:00:20 +0300 Subject: initial commit --- src/dbghelp.cpp | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/error.cpp | 53 +++++++++++++++++ src/repo.cpp | 128 +++++++++++++++++++++++++++++++++++++++++ src/utils/file.cpp | 51 ++++++++++++++++ 4 files changed, 398 insertions(+) create mode 100644 src/dbghelp.cpp create mode 100644 src/error.cpp create mode 100644 src/repo.cpp create mode 100644 src/utils/file.cpp (limited to 'src') diff --git a/src/dbghelp.cpp b/src/dbghelp.cpp new file mode 100644 index 0000000..c319fee --- /dev/null +++ b/src/dbghelp.cpp @@ -0,0 +1,166 @@ +// Copyright (c) 2017 Egor Tensin +// This file is part of the "PDB repository" project. +// For details, see https://github.com/egor-tensin/pdb-repo. +// Distributed under the MIT License. + +#include "pdb/all.hpp" + +#include +#include + +#include +#include + +#include +#include +#include + +namespace pdb +{ + namespace + { + void enable_debug_output() + { + SymSetOptions(SymGetOptions() | SYMOPT_DEBUG | SYMOPT_UNDNAME); + } + + void initialize(HANDLE id) + { + enable_debug_output(); + const auto ret = SymInitialize(id, NULL, FALSE); + if (!ret) + throw error::windows(GetLastError()); + } + + void clean_up(HANDLE id) + { + const auto ret = SymCleanup(id); + if (!ret) + throw error::windows(GetLastError()); + } + + Address gen_next_offline_base(std::size_t pdb_size) + { + static Address id = 0x10000000; + const auto next = id; + id += pdb_size; + return next; + } + + BOOL CALLBACK enum_symbols_callback( + SYMBOL_INFO *info, + ULONG, + VOID *raw_callback_ptr) + { + const auto callback_ptr = reinterpret_cast(raw_callback_ptr); + const auto& callback = *callback_ptr; + callback(SymbolInfo{*info}); + return TRUE; + } + } + + DbgHelp::DbgHelp() + { + initialize(id); + } + + ModuleInfo DbgHelp::load_pdb(const std::string& path) const + { + const auto size = file::get_size(path); + + if (size > std::numeric_limits::max()) + throw std::range_error{"PDB file size is too large"}; + + const auto offline_base = SymLoadModule64( + id, + NULL, + path.c_str(), + NULL, + gen_next_offline_base(size), + static_cast(size)); + + if (!offline_base) + throw error::windows(GetLastError()); + + return get_module_info(offline_base); + } + + ModuleInfo DbgHelp::get_module_info(Address offline_base) const + { + ModuleInfo info; + + const auto ret = SymGetModuleInfo64( + id, + offline_base, + &static_cast(info)); + + if (!ret) + throw error::windows(GetLastError()); + + return info; + } + + void DbgHelp::enum_symbols(const ModuleInfo& module, const OnSymbol& callback) const + { + const auto ret = SymEnumSymbols( + id, + module.get_offline_base(), + NULL, + &enum_symbols_callback, + const_cast(&callback)); + + if (!ret) + throw error::windows(GetLastError()); + } + + SymbolInfo DbgHelp::resolve_symbol(Address online) const + { + DWORD64 displacement = 0; + SymbolInfo symbol; + + const auto ret = SymFromAddr( + id, + online, + &displacement, + &static_cast(symbol)); + + if (!ret) + throw error::windows(GetLastError()); + + return symbol; + } + + SymbolInfo DbgHelp::resolve_symbol(const std::string& name) const + { + SymbolInfo symbol; + + const auto ret = SymFromName( + id, + name.c_str(), + &static_cast(symbol)); + + if (!ret) + throw error::windows(GetLastError()); + + return symbol; + } + + void DbgHelp::close() + { + if (!closed) + { + clean_up(id); + closed = true; + } + } + + DbgHelp::~DbgHelp() + { + try + { + close(); + } + catch (...) + { } + } +} diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 0000000..10b668b --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Egor Tensin +// This file is part of the "PDB repository" project. +// For details, see https://github.com/egor-tensin/pdb-repo. +// Distributed under the MIT License. + +#include "pdb/all.hpp" + +#include + +#include + +namespace pdb +{ + namespace error + { + namespace + { + std::string trim_trailing_newline(const std::string& s) + { + const auto last_pos = s.find_last_not_of("\r\n"); + if (std::string::npos == last_pos) + return {}; + return s.substr(0, last_pos + 1); + } + } + + std::string CategoryWindows::message(int code) const + { + char* buf; + + const auto nbwritten = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&buf), + 0, + NULL); + + if (0 == nbwritten) + { + LocalFree(buf); + return "Couldn't format the error message"; + } + + std::string msg{buf, nbwritten}; + LocalFree(buf); + return trim_trailing_newline(msg); + } + } +} diff --git a/src/repo.cpp b/src/repo.cpp new file mode 100644 index 0000000..0b28d5e --- /dev/null +++ b/src/repo.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2017 Egor Tensin +// This file is part of the "PDB repository" project. +// For details, see https://github.com/egor-tensin/pdb-repo. +// Distributed under the MIT License. + +#include "pdb/all.hpp" + +#include +#include +#include +#include + +namespace pdb +{ + namespace + { + template + const Module& guess_module( + const std::map& modules, + Address pivot) + { + if (modules.empty()) + throw std::range_error{"there're no modules to choose from"}; + + auto it = modules.lower_bound(pivot); + + if (it == modules.cend()) + { + --it; + return it->second; + } + + if (it->first > pivot) + { + if (it == modules.cbegin()) + throw std::range_error{"couldn't choose a module"}; + --it; + return it->second; + } + + return it->second; + } + + Address address_online_to_offline(const Module& module, Address online) + { + return module.get_offline_base() + online - module.get_online_base(); + } + + Address address_offline_to_online(const Module& module, Address offline) + { + return module.get_online_base() + offline - module.get_offline_base(); + } + } + + Address Repo::add_pdb(Address online_base, const std::string& path) + { + Module module{online_base, dbghelp.load_pdb(path)}; + const auto offline_base = module.get_offline_base(); + const auto it = online_modules.emplace(online_base, std::move(module)); + offline_modules.emplace(offline_base, it.first->second); + return offline_base; + } + + void Repo::enum_symbols(const OnSymbol& callback) const + { + for (const auto& it : offline_modules) + enum_symbols(it.second, callback); + } + + void Repo::enum_symbols(Address offline_base, const OnSymbol& callback) const + { + const auto it = offline_modules.find(offline_base); + if (it == offline_modules.cend()) + throw std::runtime_error{"unknown module"}; + enum_symbols(it->second, callback); + } + + void Repo::enum_symbols(const Module& module, const OnSymbol& callback) const + { + dbghelp.enum_symbols(module, [&] (const SymbolInfo& raw) + { + callback(symbol_from_buffer(module, raw)); + }); + } + + Symbol Repo::resolve_symbol(Address online) const + { + return symbol_from_buffer(dbghelp.resolve_symbol(address_online_to_offline(online))); + } + + Symbol Repo::resolve_symbol(const std::string& name) const + { + return symbol_from_buffer(dbghelp.resolve_symbol(name)); + } + + Symbol Repo::symbol_from_buffer(const SymbolInfo& raw) const + { + const auto it = offline_modules.find(raw.get_offline_base()); + if (it == offline_modules.cend()) + throw std::runtime_error{"symbol's module is unknown"}; + return symbol_from_buffer(it->second, raw); + } + + Symbol Repo::symbol_from_buffer(const Module& module, const SymbolInfo& raw) const + { + return {pdb::address_offline_to_online(module, raw.get_offline_address()), raw}; + } + + Address Repo::address_online_to_offline(Address online) const + { + return pdb::address_online_to_offline(module_from_online_address(online), online); + } + + Address Repo::address_offline_to_online(Address offline) const + { + return pdb::address_offline_to_online(module_from_offline_address(offline), offline); + } + + const Module& Repo::module_from_online_address(Address online) const + { + return guess_module(online_modules, online); + } + + const Module& Repo::module_from_offline_address(Address offline) const + { + return guess_module(offline_modules, offline); + } +} diff --git a/src/utils/file.cpp b/src/utils/file.cpp new file mode 100644 index 0000000..4150685 --- /dev/null +++ b/src/utils/file.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2017 Egor Tensin +// This file is part of the "PDB repository" project. +// For details, see https://github.com/egor-tensin/pdb-repo. +// Distributed under the MIT License. + +#include "pdb/all.hpp" + +#include + +#include + +#include + +#include +#include + +namespace pdb +{ + namespace file + { + std::size_t get_size(const std::string& path) + { + const Handle handle{CreateFileA( + path.c_str(), + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL)}; + + if (handle.get() == INVALID_HANDLE_VALUE) + throw error::windows(GetLastError()); + + LARGE_INTEGER size; + + if (!GetFileSizeEx(handle.get(), &size)) + throw error::windows(GetLastError()); + + try + { + const msl::utilities::SafeInt safe_size{size.QuadPart}; + return static_cast(safe_size); + } + catch (const msl::utilities::SafeIntException&) + { + throw std::range_error{"invalid file size"}; + } + } + } +} -- cgit v1.2.3