cpptrace/src/binary/mach-o.hpp
2025-02-19 22:49:55 -06:00

148 lines
4.1 KiB
C++

#ifndef MACHO_HPP
#define MACHO_HPP
#include "utils/common.hpp"
#include "utils/utils.hpp"
#if IS_APPLE
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <mach-o/arch.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
namespace cpptrace {
namespace detail {
bool file_is_mach_o(const std::string& object_path) noexcept;
struct load_command_entry {
std::uint32_t file_offset;
std::uint32_t cmd;
std::uint32_t cmdsize;
};
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;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
std::uint32_t filetype;
std::uint32_t n_load_commands;
std::uint32_t sizeof_load_commands;
std::uint32_t flags;
std::size_t bits = 0; // 32 or 64 once load_mach is called
std::size_t load_base = 0;
std::size_t fat_index = std::numeric_limits<std::size_t>::max();
std::vector<load_command_entry> load_commands;
struct symtab_info_data {
symtab_command symtab;
std::unique_ptr<char[]> stringtab;
Result<const char*, internal_error> get_string(std::size_t index) const;
};
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,
std::uint32_t magic
) :
file(std::move(file)),
object_path(object_path),
magic(magic) {}
Result<monostate, internal_error> load();
public:
static NODISCARD Result<mach_o, internal_error> open_mach_o(const std::string& object_path);
mach_o(mach_o&&) = default;
~mach_o() = default;
Result<std::uintptr_t, internal_error> get_text_vmaddr();
std::size_t get_fat_index() const;
void print_segments() const;
Result<std::reference_wrapper<optional<symtab_info_data>>, internal_error> get_symtab_info();
void print_symbol_table_entry(
const nlist_64& entry,
const char* stringtab,
std::size_t stringsize,
std::size_t j
) const;
void print_symbol_table();
// produce information similar to dsymutil -dump-debug-map
Result<debug_map, internal_error> get_debug_map();
Result<const std::vector<symbol_entry>&, internal_error> symbol_table();
optional<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);
private:
template<std::size_t Bits>
Result<monostate, internal_error> load_mach();
Result<monostate, internal_error> load_fat_mach();
template<std::size_t Bits>
Result<segment_command_64, internal_error> load_segment_command(std::uint32_t offset) const;
Result<symtab_command, internal_error> load_symbol_table_command(std::uint32_t offset) const;
template<std::size_t Bits>
Result<nlist_64, internal_error> load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const;
Result<std::unique_ptr<char[]>, internal_error> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const;
bool should_swap() const;
};
Result<bool, internal_error> macho_is_fat(const std::string& object_path);
NODISCARD Result<maybe_owned<mach_o>, internal_error> open_mach_o_cached(const std::string& object_path);
}
}
#endif
#endif