Add elf symtab dumping tool

This commit is contained in:
Jeremy Rifkin 2025-02-02 21:37:22 -06:00
parent b724d1328c
commit 2b7d47d627
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
6 changed files with 137 additions and 2 deletions

View File

@ -133,6 +133,43 @@ namespace detail {
return nullopt; return nullopt;
} }
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::get_symtab_entries() {
return resolve_symtab_entries(get_symtab());
}
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::get_dynamic_symtab_entries() {
return resolve_symtab_entries(get_dynamic_symtab());
}
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::resolve_symtab_entries(
const Result<const optional<elf::symtab_info> &, internal_error>& symtab
) {
if(!symtab) {
return symtab.unwrap_error();
}
if(!symtab.unwrap_value()) {
return nullopt;
}
const auto& info = symtab.unwrap_value().unwrap();
optional<const std::vector<char>&> 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<symbol_entry> 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<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type> template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type>
T elf::byteswap_if_needed(T value) { T elf::byteswap_if_needed(T value) {
if(cpptrace::detail::is_little_endian() == is_little_endian) { if(cpptrace::detail::is_little_endian() == is_little_endian) {

View File

@ -84,6 +84,20 @@ namespace detail {
private: private:
optional<std::string> lookup_symbol(frame_ptr pc, const optional<symtab_info>& maybe_symtab); optional<std::string> lookup_symbol(frame_ptr pc, const optional<symtab_info>& maybe_symtab);
public:
struct symbol_entry {
std::string st_name;
uint16_t st_shndx;
uint64_t st_value;
uint64_t st_size;
};
Result<optional<std::vector<symbol_entry>>, internal_error> get_symtab_entries();
Result<optional<std::vector<symbol_entry>>, internal_error> get_dynamic_symtab_entries();
private:
Result<optional<std::vector<symbol_entry>>, internal_error> resolve_symtab_entries(
const Result<const optional<symtab_info> &, internal_error>&
);
private: private:
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T byteswap_if_needed(T value); T byteswap_if_needed(T value);

View File

@ -28,8 +28,8 @@ namespace detail {
std::fprintf(stderr, "%s\n", unwrap_error().what()); std::fprintf(stderr, "%s\n", unwrap_error().what());
} }
} }
Result(value_type& value) : value_(value_type(value)), active(member::value) {} Result(const value_type& value) : value_(value_type(value)), active(member::value) {}
Result(E& error) : error_(E(error)), active(member::error) { Result(const E& error) : error_(E(error)), active(member::error) {
if(!should_absorb_trace_exceptions()) { if(!should_absorb_trace_exceptions()) {
std::fprintf(stderr, "%s\n", unwrap_error().what()); std::fprintf(stderr, "%s\n", unwrap_error().what());
} }

View File

@ -44,3 +44,4 @@ function(binary TARGET)
endfunction() endfunction()
add_subdirectory(dwarfdump) add_subdirectory(dwarfdump)
add_subdirectory(symbol_tables)

View File

@ -0,0 +1 @@
binary(symbol_tables)

View File

@ -0,0 +1,82 @@
#include <lyra/lyra.hpp>
#include <fmt/format.h>
#include <fmt/std.h>
#include <fmt/ostream.h>
#include <cpptrace/cpptrace.hpp>
#include <cpptrace/from_current.hpp>
#include <filesystem>
#include "binary/elf.hpp"
#include "binary/mach-o.hpp"
using namespace std::literals;
using namespace cpptrace::detail;
template<> struct fmt::formatter<lyra::cli> : ostream_formatter {};
#if IS_LINUX
void dump_symtab_result(const Result<optional<std::vector<elf::symbol_entry>>, 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();
}