diff --git a/src/binary/mach-o.cpp b/src/binary/mach-o.cpp index 7a8cda0..d25a41e 100644 --- a/src/binary/mach-o.cpp +++ b/src/binary/mach-o.cpp @@ -219,7 +219,7 @@ namespace detail { void mach_o::print_symbol_table_entry( const nlist_64& entry, - const std::unique_ptr& 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(nullptr)), + stringtab ? stringtab.unwrap_value().get() : nullptr, symtab.strsize, j ); @@ -364,10 +364,17 @@ namespace detail { return debug_map; } - Result, internal_error> mach_o::symbol_table() { + Result&, 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_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 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 diff --git a/src/binary/mach-o.hpp b/src/binary/mach-o.hpp index 62a8862..82a36c5 100644 --- a/src/binary/mach-o.hpp +++ b/src/binary/mach-o.hpp @@ -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>; + + 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; + bool tried_to_load_symbols = false; + optional> 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& 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>; - // produce information similar to dsymutil -dump-debug-map Result get_debug_map(); - Result, internal_error> symbol_table(); + Result&, 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); diff --git a/src/symbols/dwarf/debug_map_resolver.cpp b/src/symbols/dwarf/debug_map_resolver.cpp index 8cd8650..690a511 100644 --- a/src/symbols/dwarf/debug_map_resolver.cpp +++ b/src/symbols/dwarf/debug_map_resolver.cpp @@ -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 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(), diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index f9a652a..dcbfa3c 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -7,6 +7,7 @@ #include "utils/common.hpp" #include "utils/utils.hpp" #include "binary/elf.hpp" +#include "binary/mach-o.hpp" #include #include @@ -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()) {