aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/utils/CMakeLists.txt27
-rw-r--r--src/utils/addr2name.cpp119
-rw-r--r--src/utils/command_line.hpp73
-rw-r--r--src/utils/enum_symbols.cpp99
-rw-r--r--src/utils/name2addr.cpp86
-rw-r--r--src/utils/pdb_descr.hpp61
7 files changed, 468 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e33a267..a78f35a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,7 @@
find_package(Boost CONFIG REQUIRED)
file(GLOB_RECURSE winapi_debug_include CONFIGURE_DEPENDS "../include/*.hpp")
-file(GLOB_RECURSE winapi_debug_src CONFIGURE_DEPENDS "*.cpp")
+file(GLOB winapi_debug_src CONFIGURE_DEPENDS "*.cpp")
add_library(winapi_debug ${winapi_debug_include} ${winapi_debug_src})
target_compile_definitions(winapi_debug PUBLIC _NO_CVCONST_H)
target_include_directories(winapi_debug PUBLIC ../include/)
@@ -19,3 +19,5 @@ if(MSVC_VERSION EQUAL 1900)
# DbgHelp warnings on Visual Studio 2015:
target_compile_options(winapi_debug PUBLIC /wd4091)
endif()
+
+add_subdirectory(utils)
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
new file mode 100644
index 0000000..51ab17d
--- /dev/null
+++ b/src/utils/CMakeLists.txt
@@ -0,0 +1,27 @@
+find_package(Boost CONFIG REQUIRED COMPONENTS program_options)
+
+function(add_util name src)
+ add_executable("${name}" ${src})
+ target_link_libraries("${name}" PRIVATE winapi_debug)
+ target_link_libraries("${name}" PRIVATE Boost::disable_autolinking Boost::program_options)
+ install(TARGETS "${name}" RUNTIME DESTINATION bin)
+ if(MSVC)
+ install(FILES "$<TARGET_PDB_FILE:${name}>" DESTINATION bin OPTIONAL)
+ endif()
+endfunction()
+
+add_util(enum_symbols enum_symbols.cpp command_line.hpp pdb_descr.hpp)
+add_util(name2addr name2addr.cpp)
+add_util(addr2name addr2name.cpp command_line.hpp pdb_descr.hpp)
+
+if(MSVC)
+ find_package(Python3 REQUIRED COMPONENTS Interpreter)
+ add_test(NAME enum_symbols COMMAND Python3::Interpreter
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/tools/ctest-driver.py"
+ run
+ --pass-regex test_ns::baz test_ns::bar test_ns::foo test_ns::var
+ --
+ "$<TARGET_FILE:enum_symbols>"
+ --pdb "$<TARGET_PDB_FILE:test_lib>"
+ --mask "test_ns:*")
+endif()
diff --git a/src/utils/addr2name.cpp b/src/utils/addr2name.cpp
new file mode 100644
index 0000000..98b53b9
--- /dev/null
+++ b/src/utils/addr2name.cpp
@@ -0,0 +1,119 @@
+// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-debug" project.
+// For details, see https://github.com/egor-tensin/winapi-debug.
+// Distributed under the MIT License.
+
+#include "command_line.hpp"
+#include "pdb_descr.hpp"
+
+#include <winapi/debug.hpp>
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <format>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace {
+
+class Addr2Name : public SettingsParser {
+public:
+ explicit Addr2Name(int argc, char** argv) : SettingsParser{argc, argv} {
+ namespace po = boost::program_options;
+
+ visible.add_options()(
+ "pdb", po::value<std::vector<PDB>>(&pdbs)->value_name("ADDR,PATH"), "load a PDB file");
+ visible.add_options()(
+ "lines,l", po::bool_switch(&lines), "try to resolve source files & line numbers");
+ hidden.add_options()(
+ "address",
+ po::value<std::vector<winapi::Address>>(&addresses)->value_name("ADDR"),
+ "add an address to resolve");
+ positional.add("address", -1);
+ }
+
+ const char* get_short_description() const override {
+ return "[-h|--help] [--pdb ADDR,PATH]... [-l|--lines] [--] [ADDR]...";
+ }
+
+ std::vector<PDB> pdbs;
+ std::vector<winapi::Address> addresses;
+ bool lines = false;
+};
+
+std::string format_symbol(const winapi::Module& module, const winapi::Symbol& symbol) {
+ const auto symbol_str = std::format("{}!{}", module.get_name(), symbol.get_name());
+ const auto displacement = symbol.get_displacement();
+ if (!displacement)
+ return symbol_str;
+ return std::format("{}+{}", symbol_str, winapi::address::format(displacement));
+}
+
+std::string format_line_info(const winapi::LineInfo& line_info) {
+ return std::format("[{} @ {}]", line_info.file_path, line_info.line_number);
+}
+
+void dump_error(const std::exception& e) {
+ std::cerr << std::format("error: {}\n", e.what());
+}
+
+void resolve_symbol(const winapi::PostMortem& analysis,
+ winapi::Address address,
+ bool lines = false) {
+ try {
+ const auto symbol = analysis.resolve_symbol(address);
+ const auto& module = analysis.module_with_offline_base(symbol.get_offline_base());
+
+ std::ostringstream msg;
+ msg << format_symbol(module, symbol);
+
+ if (lines) {
+ try {
+ const auto line_info = analysis.resolve_line(address);
+ msg << ' ' << format_line_info(line_info);
+ } catch (const std::exception& e) {
+ dump_error(e);
+ }
+ }
+
+ std::cout << msg.str() << '\n';
+ } catch (const std::exception& e) {
+ dump_error(e);
+ std::cout << winapi::address::format(address) << '\n';
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ try {
+ Addr2Name settings{argc, argv};
+
+ 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;
+ }
+
+ winapi::PostMortem analysis;
+
+ for (const auto& pdb : settings.pdbs)
+ analysis.add_pdb(pdb.online_base, pdb.path);
+
+ for (const auto& address : settings.addresses)
+ resolve_symbol(analysis, address, settings.lines);
+ } catch (const std::exception& e) {
+ dump_error(e);
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/utils/command_line.hpp b/src/utils/command_line.hpp
new file mode 100644
index 0000000..d3178f1
--- /dev/null
+++ b/src/utils/command_line.hpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-debug" project.
+// For details, see https://github.com/egor-tensin/winapi-debug.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <filesystem>
+#include <format>
+#include <iostream>
+#include <ostream>
+#include <string>
+
+class SettingsParser {
+public:
+ explicit SettingsParser(int, char** argv) : prog_name{extract_filename(argv[0])} {
+ visible.add_options()("help,h", "show this message and exit");
+ }
+
+ virtual ~SettingsParser() = default;
+
+ virtual const char* get_short_description() const {
+ return "[--option VALUE]...";
+ }
+
+ virtual void parse(int argc, char* argv[]) {
+ boost::program_options::options_description all;
+ all.add(hidden).add(visible);
+ boost::program_options::variables_map vm;
+ boost::program_options::store(boost::program_options::command_line_parser{argc, argv}
+ .options(all)
+ .positional(positional)
+ .run(),
+ vm);
+ if (vm.count("help"))
+ exit_with_usage = true;
+ else
+ boost::program_options::notify(vm);
+ }
+
+ bool exit_with_usage = false;
+
+ void usage() const {
+ std::cout << *this;
+ }
+
+ void usage_error(const std::exception& e) const {
+ std::cerr << std::format("usage error: {}\n", e.what());
+ std::cerr << *this;
+ }
+
+protected:
+ boost::program_options::options_description hidden;
+ boost::program_options::options_description visible;
+ boost::program_options::positional_options_description positional;
+
+private:
+ static std::string extract_filename(char* path) {
+ return std::filesystem::path{path}.filename().string();
+ }
+
+ const std::string prog_name;
+
+ friend std::ostream& operator<<(std::ostream& os, const SettingsParser& parser) {
+ const auto short_descr = parser.get_short_description();
+ os << std::format("usage: {} {}\n", parser.prog_name, short_descr);
+ os << parser.visible;
+ return os;
+ }
+};
diff --git a/src/utils/enum_symbols.cpp b/src/utils/enum_symbols.cpp
new file mode 100644
index 0000000..645bc13
--- /dev/null
+++ b/src/utils/enum_symbols.cpp
@@ -0,0 +1,99 @@
+// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-debug" project.
+// For details, see https://github.com/egor-tensin/winapi-debug.
+// Distributed under the MIT License.
+
+#include "command_line.hpp"
+
+#include <winapi/debug.hpp>
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace {
+
+class EnumSymbols : public SettingsParser {
+public:
+ explicit EnumSymbols(int argc, char** argv) : SettingsParser{argc, argv} {
+ namespace po = boost::program_options;
+
+ visible.add_options()("pdb",
+ po::value<std::vector<std::string>>(&pdbs)->value_name("PATH"),
+ "load a PDB file");
+ visible.add_options()(
+ "functions",
+ po::value<winapi::symbol::Tag>(&tag)->implicit_value(function_tag)->zero_tokens(),
+ "only list functions");
+ visible.add_options()(
+ "mask", po::value<std::string>(&symbol_mask)->value_name("MASK"), "symbol mask");
+ }
+
+ const char* get_short_description() const override {
+ return "[-h|--help] [--pdb PATH]... [--functions] [--mask MASK]";
+ }
+
+ std::vector<std::string> pdbs;
+
+ bool type_specified() const {
+ return tag != reserved_tag;
+ }
+
+ winapi::Symbol::Type get_type() const {
+ return static_cast<winapi::Symbol::Type>(tag);
+ }
+
+ std::string get_mask() const {
+ return symbol_mask;
+ }
+
+private:
+ static constexpr auto reserved_tag =
+ static_cast<winapi::symbol::Tag>(winapi::Symbol::Type::RESERVED);
+ static constexpr auto function_tag =
+ static_cast<winapi::symbol::Tag>(winapi::Symbol::Type::Function);
+
+ winapi::symbol::Tag tag = reserved_tag;
+ std::string symbol_mask{winapi::DbgHelp::all_symbols};
+};
+
+constexpr winapi::symbol::Tag EnumSymbols::reserved_tag;
+constexpr winapi::symbol::Tag EnumSymbols::function_tag;
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ try {
+ EnumSymbols settings{argc, argv};
+
+ 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;
+ }
+
+ const auto dbghelp = winapi::DbgHelp::post_mortem();
+
+ for (const auto& pdb : settings.pdbs) {
+ const auto id = dbghelp.load_pdb(pdb);
+
+ dbghelp.enum_symbols(id, settings.get_mask(), [&](const winapi::SymbolInfo& symbol) {
+ if (!settings.type_specified() || settings.get_type() == symbol.get_type())
+ std::cout << symbol.get_name() << '\n';
+ });
+ }
+ } catch (const std::exception& e) {
+ std::cerr << std::format("error: {}\n", e.what());
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/utils/name2addr.cpp b/src/utils/name2addr.cpp
new file mode 100644
index 0000000..d253aa9
--- /dev/null
+++ b/src/utils/name2addr.cpp
@@ -0,0 +1,86 @@
+// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-debug" project.
+// For details, see https://github.com/egor-tensin/winapi-debug.
+// Distributed under the MIT License.
+
+#include "command_line.hpp"
+#include "pdb_descr.hpp"
+
+#include <winapi/debug.hpp>
+
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace {
+
+class Name2Addr : public SettingsParser {
+public:
+ explicit Name2Addr(int argc, char** argv) : SettingsParser{argc, argv} {
+ namespace po = boost::program_options;
+
+ visible.add_options()(
+ "pdb", po::value<std::vector<PDB>>(&pdbs)->value_name("ADDR,PATH"), "load a PDB file");
+ hidden.add_options()("name",
+ po::value<std::vector<std::string>>(&names)->value_name("NAME"),
+ "add a name to resolve");
+ positional.add("name", -1);
+ }
+
+ const char* get_short_description() const override {
+ return "[-h|--help] [--pdb ADDR,PATH]... [--] [NAME]...";
+ }
+
+ std::vector<PDB> pdbs;
+ std::vector<std::string> names;
+};
+
+void dump_error(const std::exception& e) {
+ std::cerr << std::format("error: {}\n", e.what());
+}
+
+void resolve_symbol(const winapi::PostMortem& analysis, std::string_view name) {
+ try {
+ const auto address = analysis.resolve_symbol(name).get_online_address();
+ std::cout << winapi::address::format(address) << '\n';
+ } catch (const std::exception& e) {
+ dump_error(e);
+ std::cout << name << '\n';
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ try {
+ Name2Addr settings{argc, argv};
+
+ 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;
+ }
+
+ winapi::PostMortem analysis;
+
+ for (const auto& pdb : settings.pdbs)
+ analysis.add_pdb(pdb.online_base, pdb.path);
+
+ for (const auto& name : settings.names)
+ resolve_symbol(analysis, name);
+ } catch (const std::exception& e) {
+ dump_error(e);
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/utils/pdb_descr.hpp b/src/utils/pdb_descr.hpp
new file mode 100644
index 0000000..c2e720a
--- /dev/null
+++ b/src/utils/pdb_descr.hpp
@@ -0,0 +1,61 @@
+// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-debug" project.
+// For details, see https://github.com/egor-tensin/winapi-debug.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include <winapi/debug.hpp>
+
+#include <boost/program_options.hpp>
+
+#include <string>
+#include <vector>
+
+struct PDB {
+ winapi::Address online_base;
+ std::string path;
+
+ static PDB parse(const std::string& src) {
+ static constexpr auto sep = ',';
+
+ const auto sep_pos = src.find(sep);
+ if (sep_pos == std::string::npos)
+ throw boost::program_options::invalid_option_value{src};
+
+ winapi::Address online_base;
+ if (!winapi::address::parse(online_base, src.substr(0, sep_pos)))
+ throw boost::program_options::invalid_option_value{src};
+
+ return {online_base, src.substr(sep_pos + 1)};
+ }
+
+ static winapi::Address parse_address(const std::string& src) {
+ winapi::Address dest;
+ if (!winapi::address::parse(dest, src))
+ throw boost::program_options::invalid_option_value{src};
+ return dest;
+ }
+};
+
+template <typename charT>
+void validate(boost::any& dest,
+ const std::vector<std::basic_string<charT>>& src_tokens,
+ PDB*,
+ int) {
+ namespace po = boost::program_options;
+ po::validators::check_first_occurrence(dest);
+ const auto& src_token = po::validators::get_single_string(src_tokens);
+ dest = boost::any{PDB::parse(src_token)};
+}
+
+template <typename charT>
+void validate(boost::any& dest,
+ const std::vector<std::basic_string<charT>>& src_tokens,
+ winapi::Address*,
+ int) {
+ namespace po = boost::program_options;
+ po::validators::check_first_occurrence(dest);
+ const auto& src_token = po::validators::get_single_string(src_tokens);
+ dest = boost::any{PDB::parse_address(src_token)};
+}