aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2017-05-17 06:00:20 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2017-05-17 06:00:20 +0300
commitc67055bad3cdfc93e2ac57d87f36c6e0993af690 (patch)
treeda8759c66f1af00415784e5824e6bfc2195aee92 /src
downloadwinapi-debug-c67055bad3cdfc93e2ac57d87f36c6e0993af690.tar.gz
winapi-debug-c67055bad3cdfc93e2ac57d87f36c6e0993af690.zip
initial commit
Diffstat (limited to 'src')
-rw-r--r--src/dbghelp.cpp166
-rw-r--r--src/error.cpp53
-rw-r--r--src/repo.cpp128
-rw-r--r--src/utils/file.cpp51
4 files changed, 398 insertions, 0 deletions
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 <Egor.Tensin@gmail.com>
+// 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 <Windows.h>
+#include <DbgHelp.h>
+
+#include <cstddef>
+#include <cstring>
+
+#include <limits>
+#include <stdexcept>
+#include <string>
+
+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<DbgHelp::OnSymbol*>(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<DWORD>::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<DWORD>(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<ModuleInfo::Raw&>(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<OnSymbol*>(&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_INFO&>(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_INFO&>(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 <Egor.Tensin@gmail.com>
+// 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 <Windows.h>
+
+#include <string>
+
+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<char*>(&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 <Egor.Tensin@gmail.com>
+// 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 <map>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+namespace pdb
+{
+ namespace
+ {
+ template <typename Value>
+ const Module& guess_module(
+ const std::map<Address, Value>& 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 <Egor.Tensin@gmail.com>
+// 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 <safeint.h>
+
+#include <Windows.h>
+
+#include <cstddef>
+
+#include <stdexcept>
+#include <string>
+
+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<decltype(size.QuadPart)> safe_size{size.QuadPart};
+ return static_cast<std::size_t>(safe_size);
+ }
+ catch (const msl::utilities::SafeIntException&)
+ {
+ throw std::range_error{"invalid file size"};
+ }
+ }
+ }
+}