aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt18
-rw-r--r--LICENSE.txt21
-rw-r--r--common.cmake90
-rw-r--r--include/pdb/address.hpp13
-rw-r--r--include/pdb/all.hpp15
-rw-r--r--include/pdb/dbghelp.hpp44
-rw-r--r--include/pdb/error.hpp42
-rw-r--r--include/pdb/handle.hpp29
-rw-r--r--include/pdb/module.hpp65
-rw-r--r--include/pdb/repo.hpp49
-rw-r--r--include/pdb/symbol.hpp81
-rw-r--r--include/pdb/utils/file.hpp18
-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
-rw-r--r--utils/CMakeLists.txt14
-rw-r--r--utils/addr2name.cpp130
-rw-r--r--utils/command_line.hpp86
-rw-r--r--utils/enum_symbols.cpp117
-rw-r--r--utils/name2addr.cpp132
-rw-r--r--utils/pdb_descr.hpp86
22 files changed, 1448 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..67194a1
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,18 @@
+project(pdb_repo CXX)
+
+include(common.cmake)
+
+file(GLOB_RECURSE pdb_repo_include "include/*.hpp")
+file(GLOB_RECURSE pdb_repo_src "src/*.cpp")
+add_library(pdb_repo ${pdb_repo_include} ${pdb_repo_src})
+target_compile_definitions(pdb_repo PRIVATE NOMINMAX PUBLIC _NO_CVCONST_H)
+target_include_directories(pdb_repo PUBLIC include/)
+target_link_libraries(pdb_repo PRIVATE DbgHelp)
+
+if(MSVC_VERSION EQUAL 1900)
+ # These annoying DbgHelp.h warnings:
+ # https://connect.microsoft.com/VisualStudio/feedback/details/888527/warnings-on-dbghelp-h
+ target_compile_options(pdb_repo PUBLIC /wd4091)
+endif()
+
+add_subdirectory(utils)
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..67ff5f1
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/common.cmake b/common.cmake
new file mode 100644
index 0000000..474deb4
--- /dev/null
+++ b/common.cmake
@@ -0,0 +1,90 @@
+# It's a CMake code snippet I use in all of my CMake projects.
+# It makes targets link the runtime statically by default + strips debug
+# symbols in release builds.
+
+get_directory_property(parent_directory PARENT_DIRECTORY)
+set(is_root_project $<NOT:parent_directory>)
+
+set(USE_STATIC_RUNTIME "${is_root_project}" CACHE BOOL "Link the runtime statically")
+set(STRIP_SYMBOL_TABLE "${is_root_project}" CACHE BOOL "Strip symbol tables")
+
+if(is_root_project)
+ if(MSVC)
+ add_compile_options(/MP /W4)
+ elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
+ add_compile_options(-Wall -Wextra)
+ endif()
+endif()
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+function(use_static_runtime_msvc target)
+ get_target_property(target_type "${target}" TYPE)
+ if(target_type STREQUAL INTERFACE_LIBRARY)
+ else()
+ target_compile_options("${target}" PRIVATE
+ $<$<CONFIG:Debug>:/MTd>
+ $<$<NOT:$<CONFIG:Debug>>:/MT>)
+ endif()
+endfunction()
+
+function(use_static_runtime_gcc target)
+ get_target_property(target_type "${target}" TYPE)
+ if(target_type STREQUAL EXECUTABLE)
+ target_link_libraries("${target}" PRIVATE -static)
+ endif()
+endfunction()
+
+function(use_static_runtime target)
+ if(MSVC)
+ use_static_runtime_msvc("${target}")
+ elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
+ use_static_runtime_gcc("${target}")
+ else()
+ message(WARNING "Unrecognized toolset")
+ endif()
+endfunction()
+
+function(strip_symbol_table_gcc target)
+ get_target_property(target_type "${target}" TYPE)
+ set(release_build $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>)
+ if(target_type STREQUAL INTERFACE_LIBRARY)
+ else()
+ target_link_libraries("${target}" PRIVATE $<${release_build}:-s>)
+ endif()
+endfunction()
+
+function(strip_symbol_table target)
+ if(MSVC)
+ elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
+ strip_symbol_table_gcc("${target}")
+ else()
+ message(WARNING "Unrecognized toolset")
+ endif()
+endfunction()
+
+function(apply_common_settings target)
+ if(TARGET "${target}")
+ get_target_property(target_imported "${target}" IMPORTED)
+ if(target_imported STREQUAL NOTFOUND OR NOT target_imported)
+ if(STRIP_SYMBOL_TABLE)
+ strip_symbol_table("${target}")
+ endif()
+ if(USE_STATIC_RUNTIME)
+ use_static_runtime("${target}")
+ endif()
+ endif()
+ endif()
+endfunction()
+
+macro(add_executable target)
+ _add_executable(${ARGV})
+ apply_common_settings("${target}")
+endmacro()
+
+macro(add_library target)
+ _add_library(${ARGV})
+ apply_common_settings("${target}")
+endmacro()
diff --git a/include/pdb/address.hpp b/include/pdb/address.hpp
new file mode 100644
index 0000000..53b25e8
--- /dev/null
+++ b/include/pdb/address.hpp
@@ -0,0 +1,13 @@
+// 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.
+
+#pragma once
+
+#include <Windows.h>
+
+namespace pdb
+{
+ typedef DWORD64 Address;
+}
diff --git a/include/pdb/all.hpp b/include/pdb/all.hpp
new file mode 100644
index 0000000..84be09e
--- /dev/null
+++ b/include/pdb/all.hpp
@@ -0,0 +1,15 @@
+// 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.
+
+#pragma once
+
+#include "address.hpp"
+#include "dbghelp.hpp"
+#include "error.hpp"
+#include "handle.hpp"
+#include "module.hpp"
+#include "repo.hpp"
+#include "symbol.hpp"
+#include "utils/file.hpp"
diff --git a/include/pdb/dbghelp.hpp b/include/pdb/dbghelp.hpp
new file mode 100644
index 0000000..7b018bc
--- /dev/null
+++ b/include/pdb/dbghelp.hpp
@@ -0,0 +1,44 @@
+// 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.
+
+#pragma once
+
+#include "address.hpp"
+#include "module.hpp"
+#include "symbol.hpp"
+
+#include <Windows.h>
+
+#include <functional>
+#include <string>
+
+namespace pdb
+{
+ class DbgHelp
+ {
+ public:
+ DbgHelp();
+ ~DbgHelp();
+
+ ModuleInfo load_pdb(const std::string& path) const;
+
+ typedef std::function<void (const SymbolInfo&)> OnSymbol;
+ void enum_symbols(const ModuleInfo&, const OnSymbol&) const;
+
+ SymbolInfo resolve_symbol(Address) const;
+ SymbolInfo resolve_symbol(const std::string&) const;
+
+ void close();
+
+ private:
+ ModuleInfo get_module_info(Address offline_base) const;
+
+ const HANDLE id = GetCurrentProcess();
+ bool closed = false;
+
+ DbgHelp(const DbgHelp&) = delete;
+ DbgHelp& operator=(const DbgHelp&) = delete;
+ };
+}
diff --git a/include/pdb/error.hpp b/include/pdb/error.hpp
new file mode 100644
index 0000000..6640907
--- /dev/null
+++ b/include/pdb/error.hpp
@@ -0,0 +1,42 @@
+// 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.
+
+#pragma once
+
+#include <Windows.h>
+
+#include <string>
+#include <system_error>
+
+namespace pdb
+{
+ namespace error
+ {
+ class CategoryWindows : public std::error_category
+ {
+ public:
+ CategoryWindows() = default;
+
+ const char* name() const noexcept { return "Windows"; }
+
+ std::string message(int) const;
+ };
+
+ inline const CategoryWindows& category_windows()
+ {
+ static const CategoryWindows instance;
+ return instance;
+ }
+
+ inline std::system_error windows(DWORD code)
+ {
+ static_assert(sizeof(DWORD) == sizeof(int), "Aren't DWORDs the same size as ints?");
+
+ return std::system_error{
+ static_cast<int>(code),
+ category_windows()};
+ }
+ }
+}
diff --git a/include/pdb/handle.hpp b/include/pdb/handle.hpp
new file mode 100644
index 0000000..52fb805
--- /dev/null
+++ b/include/pdb/handle.hpp
@@ -0,0 +1,29 @@
+// 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.
+
+#pragma once
+
+#include <Windows.h>
+
+#include <cassert>
+
+#include <memory>
+
+namespace pdb
+{
+ struct CloseHandle
+ {
+ void operator()(HANDLE raw) const
+ {
+ if (raw == NULL || raw == INVALID_HANDLE_VALUE)
+ return;
+ const auto ret = ::CloseHandle(raw);
+ assert(ret);
+ UNREFERENCED_PARAMETER(ret);
+ }
+ };
+
+ typedef std::unique_ptr<void, CloseHandle> Handle;
+}
diff --git a/include/pdb/module.hpp b/include/pdb/module.hpp
new file mode 100644
index 0000000..118a53b
--- /dev/null
+++ b/include/pdb/module.hpp
@@ -0,0 +1,65 @@
+// 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.
+
+#pragma once
+
+#include "address.hpp"
+
+#include <Windows.h>
+#include <DbgHelp.h>
+
+#include <cstring>
+
+#include <string>
+
+namespace pdb
+{
+ class ModuleInfo
+ {
+ public:
+ typedef IMAGEHLP_MODULE64 Raw;
+
+ ModuleInfo()
+ : raw{prepare_buffer()}
+ { }
+
+ explicit ModuleInfo(const Raw& raw)
+ : raw{raw}
+ { }
+
+ explicit operator Raw&() { return raw; }
+
+ explicit operator const Raw&() const { return raw; }
+
+ Address get_offline_base() const { return raw.BaseOfImage; }
+
+ std::string get_name() const { return raw.ModuleName; }
+
+ private:
+ static Raw prepare_buffer()
+ {
+ Raw raw;
+ std::memset(&raw, 0, sizeof(raw));
+ raw.SizeOfStruct = sizeof(raw);
+ return raw;
+ }
+
+ Raw raw;
+ };
+
+ class Module : public ModuleInfo
+ {
+ public:
+ Module(Address online_base, const ModuleInfo& info)
+ : ModuleInfo{info}
+ , online_base{online_base}
+ { }
+
+ Address get_online_base() const { return online_base; }
+
+ private:
+ const Address online_base;
+ };
+}
diff --git a/include/pdb/repo.hpp b/include/pdb/repo.hpp
new file mode 100644
index 0000000..239bd7e
--- /dev/null
+++ b/include/pdb/repo.hpp
@@ -0,0 +1,49 @@
+// 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.
+
+#pragma once
+
+#include "address.hpp"
+#include "dbghelp.hpp"
+#include "module.hpp"
+#include "symbol.hpp"
+
+#include <functional>
+#include <map>
+#include <string>
+
+namespace pdb
+{
+ class Repo
+ {
+ public:
+ Repo() = default;
+
+ Address add_pdb(Address online_base, const std::string& path);
+
+ typedef std::function<void (const Symbol&)> OnSymbol;
+ void enum_symbols(const OnSymbol&) const;
+ void enum_symbols(Address offline_base, const OnSymbol&) const;
+ void enum_symbols(const Module&, const OnSymbol&) const;
+
+ Symbol resolve_symbol(Address) const;
+ Symbol resolve_symbol(const std::string&) const;
+
+ private:
+ Symbol symbol_from_buffer(const SymbolInfo&) const;
+ Symbol symbol_from_buffer(const Module&, const SymbolInfo&) const;
+
+ const Module& module_from_online_address(Address) const;
+ const Module& module_from_offline_address(Address) const;
+
+ Address address_offline_to_online(Address) const;
+ Address address_online_to_offline(Address) const;
+
+ const DbgHelp dbghelp;
+
+ std::map<Address, Module> online_modules;
+ std::map<Address, const Module&> offline_modules;
+ };
+}
diff --git a/include/pdb/symbol.hpp b/include/pdb/symbol.hpp
new file mode 100644
index 0000000..9e11d04
--- /dev/null
+++ b/include/pdb/symbol.hpp
@@ -0,0 +1,81 @@
+// 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.
+
+#pragma once
+
+#include "module.hpp"
+
+#include <Windows.h>
+#include <DbgHelp.h>
+
+#include <cstring>
+
+#include <string>
+
+namespace pdb
+{
+ class SymbolInfo
+ {
+ public:
+ typedef SYMBOL_INFO Raw;
+
+ SymbolInfo()
+ : raw{*reinterpret_cast<Raw*>(buffer)}
+ {
+ raw.SizeOfStruct = sizeof(Raw);
+ raw.MaxNameLen = MAX_SYM_NAME;
+ }
+
+ SymbolInfo(const Raw& raw)
+ : SymbolInfo{}
+ {
+ std::memcpy(buffer, &raw, raw.SizeOfStruct + raw.NameLen - 1);
+ }
+
+ explicit operator Raw&() { return raw; }
+
+ explicit operator const Raw&() const { return raw; }
+
+ std::string get_name() const { return {raw.Name, raw.NameLen}; }
+
+ Address get_offline_base() const { return raw.ModBase; }
+
+ Address get_offline_address() const { return raw.Address; }
+
+ typedef ULONG Tag;
+
+ Tag get_tag() const { return raw.Tag; }
+
+ enum class Type : Tag
+ {
+ Function = SymTagFunction,
+ RESERVED = SymTagMax,
+ };
+
+ Type get_type() const { return static_cast<Type>(get_tag()); }
+
+ bool is_function() const { return get_type() == Type::Function; }
+
+ private:
+ unsigned char buffer[sizeof(Raw) + MAX_SYM_NAME - 1];
+
+ protected:
+ Raw& raw;
+ };
+
+ class Symbol : public SymbolInfo
+ {
+ public:
+ Symbol(Address online_address, const SymbolInfo& info)
+ : SymbolInfo{info}
+ , online_address{online_address}
+ { }
+
+ Address get_online_address() const { return online_address; }
+
+ private:
+ const Address online_address;
+ };
+}
diff --git a/include/pdb/utils/file.hpp b/include/pdb/utils/file.hpp
new file mode 100644
index 0000000..5b83f2b
--- /dev/null
+++ b/include/pdb/utils/file.hpp
@@ -0,0 +1,18 @@
+// 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.
+
+#pragma once
+
+#include <cstddef>
+
+#include <string>
+
+namespace pdb
+{
+ namespace file
+ {
+ std::size_t get_size(const std::string&);
+ }
+}
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"};
+ }
+ }
+ }
+}
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644
index 0000000..60f5b9f
--- /dev/null
+++ b/utils/CMakeLists.txt
@@ -0,0 +1,14 @@
+find_package(Boost REQUIRED COMPONENTS filesystem program_options system)
+
+add_executable(enum_symbols enum_symbols.cpp command_line.hpp pdb_descr.hpp)
+target_compile_definitions(enum_symbols PRIVATE _NO_CVCONST_H)
+target_include_directories(enum_symbols SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
+target_link_libraries(enum_symbols PRIVATE pdb_repo ${Boost_LIBRARIES})
+
+add_executable(name2addr name2addr.cpp)
+target_include_directories(name2addr SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
+target_link_libraries(name2addr PRIVATE pdb_repo ${Boost_LIBRARIES})
+
+add_executable(addr2name addr2name.cpp command_line.hpp pdb_descr.hpp)
+target_include_directories(addr2name SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
+target_link_libraries(addr2name PRIVATE pdb_repo ${Boost_LIBRARIES})
diff --git a/utils/addr2name.cpp b/utils/addr2name.cpp
new file mode 100644
index 0000000..a01f61e
--- /dev/null
+++ b/utils/addr2name.cpp
@@ -0,0 +1,130 @@
+// 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 "command_line.hpp"
+#include "pdb_descr.hpp"
+
+#include "pdb/all.hpp"
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace
+{
+ class Addr2Name : public SettingsParser
+ {
+ public:
+ explicit Addr2Name(const std::string& argv0)
+ : SettingsParser{argv0, build_options(), build_args()}
+ { }
+
+ bool exit_with_usage() const { return help_flag; }
+
+ const char* get_short_description() const override
+ {
+ return "[-h|--help] [--pdb ADDR,PATH]... [--] [ADDR]...";
+ }
+
+ std::vector<PDB> pdbs;
+
+ std::vector<pdb::Address> addresses;
+
+ private:
+ Options build_options()
+ {
+ namespace program_options = boost::program_options;
+ Options descr{"options"};
+ descr.add_options()
+ ("help,h",
+ program_options::bool_switch(&help_flag),
+ "show this message and exit")
+ ("pdb",
+ program_options::value<std::vector<PDB>>(&pdbs)
+ ->value_name("ADDR,PATH"),
+ "load a PDB file")
+ ("address",
+ program_options::value<std::vector<pdb::Address>>(&addresses)
+ ->value_name("ADDR"),
+ "add an address to resolve");
+ return descr;
+ }
+
+ static Arguments build_args()
+ {
+ Arguments descr;
+ descr.add("address", -1);
+ return descr;
+ }
+
+ bool help_flag = false;
+ };
+
+ std::string format_address(pdb::Address address)
+ {
+ std::ostringstream oss;
+ oss << std::showbase << std::hex << address;
+ return oss.str();
+ }
+
+ void dump_error(const std::exception& e)
+ {
+ std::cerr << "error: " << e.what() << '\n';
+ }
+
+ void resolve_symbol(const pdb::Repo& repo, pdb::Address address)
+ {
+ try
+ {
+ std::cout << repo.resolve_symbol(address).get_name() << '\n';
+ }
+ catch (const std::exception& e)
+ {
+ dump_error(e);
+ std::cout << format_address(address) << '\n';
+ }
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ const Addr2Name settings{argv[0]};
+
+ try
+ {
+ settings.parse(argc, argv);
+ }
+ catch (const boost::program_options::error& e)
+ {
+ settings.usage_error(e);
+ return 1;
+ }
+
+ if (settings.exit_with_usage())
+ {
+ settings.usage();
+ return 0;
+ }
+
+ pdb::Repo repo;
+
+ for (const auto& pdb : settings.pdbs)
+ repo.add_pdb(pdb.online_base, pdb.path);
+
+ for (const auto& address : settings.addresses)
+ resolve_symbol(repo, address);
+ }
+ catch (const std::exception& e)
+ {
+ dump_error(e);
+ return 1;
+ }
+ return 0;
+}
diff --git a/utils/command_line.hpp b/utils/command_line.hpp
new file mode 100644
index 0000000..79fb673
--- /dev/null
+++ b/utils/command_line.hpp
@@ -0,0 +1,86 @@
+// 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.
+
+#pragma once
+
+#include <boost/filesystem.hpp>
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <ostream>
+#include <string>
+
+namespace
+{
+ class SettingsParser
+ {
+ public:
+ typedef boost::program_options::options_description Options;
+ typedef boost::program_options::positional_options_description Arguments;
+
+ explicit SettingsParser(const std::string& argv0)
+ : prog_name{extract_filename(argv0)}
+ { }
+
+ SettingsParser(const std::string& argv0, const Options& options)
+ : prog_name{extract_filename(argv0)}
+ , options{options}
+ { }
+
+ SettingsParser(const std::string& argv0, const Options& options, const Arguments& args)
+ : prog_name{extract_filename(argv0)}
+ , options{options}
+ , args{args}
+ { }
+
+ virtual ~SettingsParser() = default;
+
+ virtual const char* get_short_description() const { return "[OPTION]..."; }
+
+ void parse(int argc, char* argv[]) const
+ {
+ boost::program_options::variables_map vm;
+ boost::program_options::store(
+ boost::program_options::command_line_parser{argc, argv}
+ .options(options)
+ .positional(args)
+ .run(),
+ vm);
+ boost::program_options::notify(vm);
+ }
+
+ void usage() const
+ {
+ std::cout << *this;
+ }
+
+ void usage_error(const std::exception& e) const
+ {
+ std::cerr << "usage error: " << e.what() << '\n';
+ std::cerr << *this;
+ }
+
+ private:
+ static std::string extract_filename(const std::string& path)
+ {
+ return boost::filesystem::path{path}.filename().string();
+ }
+
+ const std::string prog_name;
+ const Options options;
+ const Arguments args;
+
+ friend std::ostream& operator<<(std::ostream&, const SettingsParser&);
+ };
+
+ std::ostream& operator<<(std::ostream& os, const SettingsParser& cmd_parser)
+ {
+ const auto short_descr = cmd_parser.get_short_description();
+ os << "usage: " << cmd_parser.prog_name << ' ' << short_descr << '\n';
+ os << cmd_parser.options;
+ return os;
+ }
+}
diff --git a/utils/enum_symbols.cpp b/utils/enum_symbols.cpp
new file mode 100644
index 0000000..d7fd417
--- /dev/null
+++ b/utils/enum_symbols.cpp
@@ -0,0 +1,117 @@
+// 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 "command_line.hpp"
+#include "pdb_descr.hpp"
+
+#include "pdb/all.hpp"
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace
+{
+ class EnumSymbols : public SettingsParser
+ {
+ public:
+ explicit EnumSymbols(const std::string& argv0)
+ : SettingsParser{argv0, build_options()}
+ { }
+
+ bool exit_with_usage() const { return help_flag; }
+
+ const char* get_short_description() const override
+ {
+ return "[-h|--help] [--pdb ADDR,PATH]... [--functions]";
+ }
+
+ std::vector<PDB> pdbs;
+
+ bool type_specified() const
+ {
+ return tag != reserved_tag;
+ }
+
+ pdb::Symbol::Type get_type() const
+ {
+ return static_cast<pdb::Symbol::Type>(tag);
+ }
+
+ private:
+ Options build_options()
+ {
+ namespace program_options = boost::program_options;
+ Options descr{"options"};
+ descr.add_options()
+ ("help,h",
+ program_options::bool_switch(&help_flag),
+ "show this message and exit")
+ ("pdb",
+ program_options::value<std::vector<PDB>>(&pdbs)
+ ->value_name("ADDR,PATH"),
+ "load a PDB file")
+ ("functions",
+ program_options::value<pdb::Symbol::Tag>(&tag)
+ ->implicit_value(function_tag)
+ ->zero_tokens(),
+ "only list functions");
+ return descr;
+ }
+
+ bool help_flag = false;
+
+ static const auto reserved_tag = static_cast<pdb::Symbol::Tag>(pdb::Symbol::Type::RESERVED);
+ static const auto function_tag = static_cast<pdb::Symbol::Tag>(pdb::Symbol::Type::Function);
+
+ pdb::Symbol::Tag tag = reserved_tag;
+ };
+}
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ const EnumSymbols settings{argv[0]};
+
+ try
+ {
+ settings.parse(argc, argv);
+ }
+ catch (const boost::program_options::error& e)
+ {
+ settings.usage_error(e);
+ return 1;
+ }
+
+ if (settings.exit_with_usage())
+ {
+ settings.usage();
+ return 0;
+ }
+
+ pdb::Repo repo;
+
+ for (const auto& pdb : settings.pdbs)
+ {
+ const auto id = repo.add_pdb(pdb.online_base, pdb.path);
+
+ repo.enum_symbols(id, [&] (const pdb::Symbol& symbol)
+ {
+ if (!settings.type_specified() || settings.get_type() == symbol.get_type())
+ std::cout << symbol.get_name() << '\n';
+ });
+ }
+ }
+ catch (const std::exception& e)
+ {
+ std::cerr << "error: " << e.what() << '\n';
+ return 1;
+ }
+ return 0;
+}
diff --git a/utils/name2addr.cpp b/utils/name2addr.cpp
new file mode 100644
index 0000000..055b469
--- /dev/null
+++ b/utils/name2addr.cpp
@@ -0,0 +1,132 @@
+// 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 "command_line.hpp"
+#include "pdb_descr.hpp"
+
+#include "pdb/all.hpp"
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace
+{
+ class Name2Addr : public SettingsParser
+ {
+ public:
+ explicit Name2Addr(const std::string& argv0)
+ : SettingsParser{argv0, build_options(), build_args()}
+ { }
+
+ bool exit_with_usage() const { return help_flag; }
+
+ const char* get_short_description() const override
+ {
+ return "[-h|--help] [--pdb ADDR,PATH]... [--] [NAME]...";
+ }
+
+ std::vector<PDB> pdbs;
+
+ std::vector<std::string> names;
+
+ private:
+ Options build_options()
+ {
+ namespace program_options = boost::program_options;
+ Options descr{"options"};
+ descr.add_options()
+ ("help,h",
+ program_options::bool_switch(&help_flag),
+ "show this message and exit")
+ ("pdb",
+ program_options::value<std::vector<PDB>>(&pdbs)
+ ->value_name("ADDR,PATH"),
+ "load a PDB file")
+ ("name",
+ program_options::value<std::vector<std::string>>(&names)
+ ->value_name("NAME"),
+ "add a name to resolve");
+ return descr;
+ }
+
+ static Arguments build_args()
+ {
+ Arguments descr;
+ descr.add("name", -1);
+ return descr;
+ }
+
+ bool help_flag = false;
+ };
+
+ std::string format_address(pdb::Address address)
+ {
+ std::ostringstream oss;
+ oss << std::showbase << std::hex << address;
+ return oss.str();
+ }
+
+ void dump_error(const std::exception& e)
+ {
+ std::cerr << "error: " << e.what() << '\n';
+ }
+
+ void resolve_symbol(const pdb::Repo& repo, const std::string& name)
+ {
+ try
+ {
+ const auto address = repo.resolve_symbol(name).get_online_address();
+ std::cout << format_address(address) << '\n';
+ }
+ catch (const std::exception& e)
+ {
+ dump_error(e);
+ std::cout << name << '\n';
+ }
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ const Name2Addr settings{argv[0]};
+
+ try
+ {
+ settings.parse(argc, argv);
+ }
+ catch (const boost::program_options::error& e)
+ {
+ settings.usage_error(e);
+ return 1;
+ }
+
+ if (settings.exit_with_usage())
+ {
+ settings.usage();
+ return 0;
+ }
+
+ pdb::Repo repo;
+
+ for (const auto& pdb : settings.pdbs)
+ repo.add_pdb(pdb.online_base, pdb.path);
+
+ for (const auto& name : settings.names)
+ resolve_symbol(repo, name);
+ }
+ catch (const std::exception& e)
+ {
+ dump_error(e);
+ return 1;
+ }
+ return 0;
+}
diff --git a/utils/pdb_descr.hpp b/utils/pdb_descr.hpp
new file mode 100644
index 0000000..c57a0e9
--- /dev/null
+++ b/utils/pdb_descr.hpp
@@ -0,0 +1,86 @@
+// 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.
+
+#pragma once
+
+#include "pdb/all.hpp"
+
+#include <boost/program_options.hpp>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace
+{
+ struct PDB
+ {
+ PDB(pdb::Address online_base, const std::string& path)
+ : online_base{online_base}
+ , path{path}
+ { }
+
+ pdb::Address online_base;
+ std::string path;
+
+ static PDB parse(std::string src)
+ {
+ static constexpr auto sep = ',';
+ const auto sep_pos = src.find(sep);
+ if (sep_pos == std::string::npos)
+ boost::throw_exception(boost::program_options::invalid_option_value{src});
+ pdb::Address online_base;
+ if (!parse_address(online_base, src.substr(0, sep_pos)))
+ boost::throw_exception(boost::program_options::invalid_option_value{src});
+ return {online_base, src.substr(sep_pos + 1)};
+ }
+
+ static bool parse_address(pdb::Address& dest, const std::string& src)
+ {
+ std::istringstream iss{src};
+ iss >> std::hex;
+ char c;
+ return iss >> dest && !iss.get(c);
+ }
+
+ static pdb::Address parse_address(const std::string& src)
+ {
+ pdb::Address dest;
+ if (!parse_address(dest, src))
+ boost::throw_exception(boost::program_options::invalid_option_value{src});
+ return dest;
+ }
+ };
+}
+
+namespace boost
+{
+ namespace program_options
+ {
+ template <typename charT>
+ void validate(
+ boost::any& dest,
+ const std::vector<std::basic_string<charT>>& src_tokens,
+ PDB*,
+ int)
+ {
+ validators::check_first_occurrence(dest);
+ const auto& src_token = validators::get_single_string(src_tokens);
+ dest = any{PDB::parse(src_token)};
+ }
+
+ template <typename charT>
+ void validate(
+ boost::any& dest,
+ const std::vector<std::basic_string<charT>>& src_tokens,
+ pdb::Address*,
+ int)
+ {
+ validators::check_first_occurrence(dest);
+ const auto& src_token = validators::get_single_string(src_tokens);
+ dest = any{PDB::parse_address(src_token)};
+ }
+ }
+}