diff --git a/src/binary/elf.cpp b/src/binary/elf.cpp index d97d2fa..924bbad 100644 --- a/src/binary/elf.cpp +++ b/src/binary/elf.cpp @@ -133,6 +133,43 @@ namespace detail { return nullopt; } + Result>, internal_error> elf::get_symtab_entries() { + return resolve_symtab_entries(get_symtab()); + } + Result>, internal_error> elf::get_dynamic_symtab_entries() { + return resolve_symtab_entries(get_dynamic_symtab()); + } + + Result>, internal_error> elf::resolve_symtab_entries( + const Result &, internal_error>& symtab + ) { + if(!symtab) { + return symtab.unwrap_error(); + } + if(!symtab.unwrap_value()) { + return nullopt; + } + const auto& info = symtab.unwrap_value().unwrap(); + optional&> strtab; + if(info.strtab_link != SHN_UNDEF) { + auto strtab_ = get_strtab(info.strtab_link); + if(strtab_.is_error()) { + return strtab_.unwrap_error(); + } + strtab = strtab_.unwrap_value(); + } + std::vector res; + for(const auto& entry : info.entries) { + res.push_back({ + strtab.unwrap().data() + entry.st_name, + entry.st_shndx, + entry.st_value, + entry.st_size + }); + } + return res; + } + template::value, int>::type> T elf::byteswap_if_needed(T value) { if(cpptrace::detail::is_little_endian() == is_little_endian) { diff --git a/src/binary/elf.hpp b/src/binary/elf.hpp index 298222a..63af72d 100644 --- a/src/binary/elf.hpp +++ b/src/binary/elf.hpp @@ -84,6 +84,20 @@ namespace detail { private: optional lookup_symbol(frame_ptr pc, const optional& maybe_symtab); + public: + struct symbol_entry { + std::string st_name; + uint16_t st_shndx; + uint64_t st_value; + uint64_t st_size; + }; + Result>, internal_error> get_symtab_entries(); + Result>, internal_error> get_dynamic_symtab_entries(); + private: + Result>, internal_error> resolve_symtab_entries( + const Result &, internal_error>& + ); + private: template::value, int>::type = 0> T byteswap_if_needed(T value); diff --git a/src/utils/result.hpp b/src/utils/result.hpp index 21acaa2..2bbd879 100644 --- a/src/utils/result.hpp +++ b/src/utils/result.hpp @@ -28,8 +28,8 @@ namespace detail { std::fprintf(stderr, "%s\n", unwrap_error().what()); } } - Result(value_type& value) : value_(value_type(value)), active(member::value) {} - Result(E& error) : error_(E(error)), active(member::error) { + Result(const value_type& value) : value_(value_type(value)), active(member::value) {} + Result(const E& error) : error_(E(error)), active(member::error) { if(!should_absorb_trace_exceptions()) { std::fprintf(stderr, "%s\n", unwrap_error().what()); } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8ccb62c..bb02cd7 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -44,3 +44,4 @@ function(binary TARGET) endfunction() add_subdirectory(dwarfdump) +add_subdirectory(symbol_tables) diff --git a/tools/symbol_tables/CMakeLists.txt b/tools/symbol_tables/CMakeLists.txt new file mode 100644 index 0000000..529407f --- /dev/null +++ b/tools/symbol_tables/CMakeLists.txt @@ -0,0 +1 @@ +binary(symbol_tables) diff --git a/tools/symbol_tables/main.cpp b/tools/symbol_tables/main.cpp new file mode 100644 index 0000000..b3e0099 --- /dev/null +++ b/tools/symbol_tables/main.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "binary/elf.hpp" +#include "binary/mach-o.hpp" + +using namespace std::literals; +using namespace cpptrace::detail; + +template<> struct fmt::formatter : ostream_formatter {}; + +#if IS_LINUX +void dump_symtab_result(const Result>, internal_error>& res) { + if(!res) { + fmt::println(stderr, "Error loading: {}", res.unwrap_error().what()); + } + const auto& entries_ = res.unwrap_value(); + if(!entries_) { + fmt::println("Empty symbol table"); + } + const auto& entries = entries_.unwrap(); + fmt::println("{:16} {:16} {:4} {}", "value", "size", "shdx", "symbol"); + for(const auto& entry : entries) { + fmt::println("{:016x} {:016x} {:04x} {}", entry.st_value, entry.st_size, entry.st_shndx, entry.st_name); + } +} + +void dump_symbols(const std::filesystem::path& path) { + auto elf_ = elf::open_elf(path); + if(!elf_) { + fmt::println(stderr, "Error reading file: {}", elf_.unwrap_error().what()); + } + auto& elf = elf_.unwrap_value(); + fmt::println("Symtab:"); + dump_symtab_result(elf.get_symtab_entries()); + fmt::println("Dynamic symtab:"); + dump_symtab_result(elf.get_dynamic_symtab_entries()); +} +#elif IS_APPLE +void dump_symbols(const std::filesystem::path& path) { + fmt::println("Not implemented yet (TODO)"); +} +#else +void dump_symbols(const std::filesystem::path& path) { + fmt::println("Unable to dump symbol table on this platform"); +} +#endif + +int main(int argc, char** argv) CPPTRACE_TRY { + bool show_help = false; + std::filesystem::path path; + auto cli = lyra::cli() + | lyra::help(show_help) + | lyra::arg(path, "binary path")("binary to dump symbol tables for").required(); + if(auto result = cli.parse({ argc, argv }); !result) { + fmt::println(stderr, "Error in command line: {}", result.message()); + fmt::println("{}", cli); + return 1; + } + if(show_help) { + fmt::println("{}", cli); + return 0; + } + if(!std::filesystem::exists(path)) { + fmt::println(stderr, "Error: Path doesn't exist {}", path); + return 1; + } + if(!std::filesystem::is_regular_file(path)) { + fmt::println(stderr, "Error: Path isn't a regular file {}", path); + return 1; + } + dump_symbols(path); +} CPPTRACE_CATCH(const std::exception& e) { + fmt::println(stderr, "Caught exception {}: {}", cpptrace::demangle(typeid(e).name()), e.what()); + cpptrace::from_current_exception().print(); +}