Implement symbol table lookup for mach-o and fix mingw build

This commit is contained in:
Jeremy Rifkin 2025-01-27 23:12:30 -06:00
parent 68c9d33e94
commit 08306c12a5
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
4 changed files with 78 additions and 27 deletions

View File

@ -219,7 +219,7 @@ namespace detail {
void mach_o::print_symbol_table_entry(
const nlist_64& entry,
const std::unique_ptr<char[]>& stringtab,
const char* stringtab,
std::size_t stringsize,
std::size_t j
) const {
@ -248,7 +248,7 @@ namespace detail {
stringtab == nullptr
? "Stringtab error"
: entry.n_un.n_strx < stringsize
? stringtab.get() + entry.n_un.n_strx
? stringtab + entry.n_un.n_strx
: "String index out of bounds"
);
}
@ -286,7 +286,7 @@ namespace detail {
}
print_symbol_table_entry(
entry.unwrap_value(),
std::move(stringtab).value_or(std::unique_ptr<char[]>(nullptr)),
stringtab ? stringtab.unwrap_value().get() : nullptr,
symtab.strsize,
j
);
@ -364,10 +364,17 @@ namespace detail {
return debug_map;
}
Result<std::vector<mach_o::symbol_entry>, internal_error> mach_o::symbol_table() {
Result<const std::vector<mach_o::symbol_entry>&, internal_error> mach_o::symbol_table() {
if(symbols) {
return symbols.unwrap();
}
if(tried_to_load_symbols) {
return internal_error("previous symbol table load failed");
}
tried_to_load_symbols = true;
std::vector<symbol_entry> symbol_table;
// we have a bunch of symbols in our binary we need to pair up with symbols from various .o files
// first collect symbols and the objects they come from
std::vector<symbol_entry> symbols;
auto symtab_info_res = get_symtab_info();
if(!symtab_info_res) {
return std::move(symtab_info_res).unwrap_error();
@ -394,13 +401,42 @@ namespace detail {
if(!str) {
return std::move(str).unwrap_error();
}
symbols.push_back({
symbol_table.push_back({
entry.n_value,
str.unwrap_value()
});
}
}
return symbols;
std::sort(
symbol_table.begin(),
symbol_table.end(),
[] (const symbol_entry& a, const symbol_entry& b) { return a.address < b.address; }
);
symbols = std::move(symbol_table);
return symbols.unwrap();
}
std::string mach_o::lookup_symbol(frame_ptr pc) {
auto symtab_ = symbol_table();
if(!symtab_) {
return "";
}
const auto& symtab = symtab_.unwrap_value();;
auto it = first_less_than_or_equal(
symtab.begin(),
symtab.end(),
pc,
[] (frame_ptr pc, const symbol_entry& entry) {
return pc < entry.address;
}
);
if(it == symtab.end()) {
return "";
}
ASSERT(pc >= it->address);
// TODO: We subtracted one from the address so name + diff won't show up in the objdump, decide if desirable
// to have an easier offset to lookup
return microfmt::format("{} + {}", it->name, pc - it->address);
}
// produce information similar to dsymutil -dump-debug-map

View File

@ -28,6 +28,23 @@ namespace detail {
};
class mach_o {
public:
struct debug_map_entry {
uint64_t source_address;
uint64_t size;
std::string name;
};
struct symbol_entry {
uint64_t address;
std::string name;
};
// map from object file to a vector of symbols to resolve
using debug_map = std::unordered_map<std::string, std::vector<debug_map_entry>>;
private:
file_wrapper file;
std::string object_path;
std::uint32_t magic;
@ -53,6 +70,9 @@ namespace detail {
bool tried_to_load_symtab = false;
optional<symtab_info_data> symtab_info;
bool tried_to_load_symbols = false;
optional<std::vector<symbol_entry>> symbols;
mach_o(
file_wrapper file,
const std::string& object_path,
@ -80,31 +100,19 @@ namespace detail {
void print_symbol_table_entry(
const nlist_64& entry,
const std::unique_ptr<char[]>& stringtab,
const char* stringtab,
std::size_t stringsize,
std::size_t j
) const;
void print_symbol_table();
struct debug_map_entry {
uint64_t source_address;
uint64_t size;
std::string name;
};
struct symbol_entry {
uint64_t address;
std::string name;
};
// map from object file to a vector of symbols to resolve
using debug_map = std::unordered_map<std::string, std::vector<debug_map_entry>>;
// produce information similar to dsymutil -dump-debug-map
Result<debug_map, internal_error> get_debug_map();
Result<std::vector<symbol_entry>, internal_error> symbol_table();
Result<const std::vector<symbol_entry>&, internal_error> symbol_table();
std::string lookup_symbol(frame_ptr pc);
// produce information similar to dsymutil -dump-debug-map
static void print_debug_map(const debug_map& debug_map);

View File

@ -50,7 +50,7 @@ namespace libdwarf {
if(!obj) {
return this->symbols.unwrap();
}
auto symbol_table = obj.unwrap_value().symbol_table();
const auto& symbol_table = obj.unwrap_value().symbol_table();
if(!symbol_table) {
return this->symbols.unwrap();
}
@ -100,7 +100,7 @@ namespace libdwarf {
uint64_t size;
std::string name;
nullable<uint64_t> target_address; // T(-1) is used as a sentinel
std::size_t object_index;
std::size_t object_index; // index into target_objects
};
class debug_map_resolver : public symbol_resolver {
@ -137,6 +137,7 @@ namespace libdwarf {
}
}
// sort for binary lookup later
// TODO: Redundant?
std::sort(
symbols.begin(),
symbols.end(),

View File

@ -7,6 +7,7 @@
#include "utils/common.hpp"
#include "utils/utils.hpp"
#include "binary/elf.hpp"
#include "binary/mach-o.hpp"
#include <cstdint>
#include <cstdio>
@ -93,9 +94,12 @@ namespace libdwarf {
for(const auto& group : collate_frames(frames, trace)) {
try {
const auto& object_name = group.first;
// TODO PERF: Potentially a duplicate elf open and parse with module base stuff
// TODO: What about mach-o
// TODO PERF: Potentially a duplicate open and parse with module base stuff (and debug map resolver)
#if IS_LINUX
auto object = elf::open_elf(object_name);
#elif IS_APPLE
auto object = mach_o::open_mach_o(object_name);
#endif
auto resolver = get_resolver(object_name);
for(const auto& entry : group.second) {
const auto& dlframe = entry.first.get();
@ -110,9 +114,11 @@ namespace libdwarf {
throw;
}
}
#if IS_LINUX || IS_APPLE
if(frame.frame.symbol.empty() && object.has_value()) {
frame.frame.symbol = object.unwrap_value().lookup_symbol(dlframe.object_address);
}
#endif
}
} catch(...) { // NOSONAR
if(!should_absorb_trace_exceptions()) {