Add a Result type and replace some exceptions with it (#109)
This commit is contained in:
parent
47e7ee79eb
commit
acaa4f42e6
@ -25,7 +25,7 @@ namespace detail {
|
||||
}
|
||||
|
||||
template<std::size_t Bits>
|
||||
static std::uintptr_t elf_get_module_image_base_from_program_table(
|
||||
static Result<std::uintptr_t, internal_error> elf_get_module_image_base_from_program_table(
|
||||
const std::string& object_path,
|
||||
std::FILE* file,
|
||||
bool is_little_endian
|
||||
@ -33,33 +33,62 @@ namespace detail {
|
||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
||||
using Header = typename std::conditional<Bits == 32, Elf32_Ehdr, Elf64_Ehdr>::type;
|
||||
using PHeader = typename std::conditional<Bits == 32, Elf32_Phdr, Elf64_Phdr>::type;
|
||||
Header file_header = load_bytes<Header>(file, 0);
|
||||
VERIFY(file_header.e_ehsize == sizeof(Header), "ELF file header size mismatch" + object_path);
|
||||
auto loaded_header = load_bytes<Header>(file, 0);
|
||||
if(loaded_header.is_error()) {
|
||||
return std::move(loaded_header).unwrap_error();
|
||||
}
|
||||
const Header& file_header = loaded_header.unwrap_value();
|
||||
if(file_header.e_ehsize != sizeof(Header)) {
|
||||
return internal_error("ELF file header size mismatch" + object_path);
|
||||
}
|
||||
// PT_PHDR will occur at most once
|
||||
// Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
|
||||
// It should occur at the beginning but may as well loop just in case
|
||||
for(int i = 0; i < file_header.e_phnum; i++) {
|
||||
PHeader program_header = load_bytes<PHeader>(file, file_header.e_phoff + file_header.e_phentsize * i);
|
||||
auto loaded_ph = load_bytes<PHeader>(file, file_header.e_phoff + file_header.e_phentsize * i);
|
||||
if(loaded_ph.is_error()) {
|
||||
return std::move(loaded_ph).unwrap_error();
|
||||
}
|
||||
const PHeader& program_header = loaded_ph.unwrap_value();
|
||||
if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) {
|
||||
return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian) -
|
||||
elf_byteswap_if_needed(program_header.p_offset, is_little_endian);
|
||||
}
|
||||
}
|
||||
// Apparently some objects like shared objects can end up missing this file. 0 as a base seems correct.
|
||||
// Apparently some objects like shared objects can end up missing this header. 0 as a base seems correct.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::uintptr_t elf_get_module_image_base(const std::string& object_path) {
|
||||
static Result<std::uintptr_t, internal_error> elf_get_module_image_base(const std::string& object_path) {
|
||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
||||
if(file == nullptr) {
|
||||
throw file_error("Unable to read object file " + object_path);
|
||||
return internal_error("Unable to read object file " + object_path);
|
||||
}
|
||||
// Initial checks/metadata
|
||||
auto magic = load_bytes<std::array<char, 4>>(file, 0);
|
||||
VERIFY(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}), "File is not ELF " + object_path);
|
||||
bool is_64 = load_bytes<std::uint8_t>(file, 4) == 2;
|
||||
bool is_little_endian = load_bytes<std::uint8_t>(file, 5) == 1;
|
||||
VERIFY(load_bytes<std::uint8_t>(file, 6) == 1, "Unexpected ELF endianness " + object_path);
|
||||
if(magic.is_error()) {
|
||||
return std::move(magic).unwrap_error();
|
||||
}
|
||||
if(magic.unwrap_value() != (std::array<char, 4>{0x7F, 'E', 'L', 'F'})) {
|
||||
return internal_error("File is not ELF " + object_path);
|
||||
}
|
||||
auto ei_class = load_bytes<std::uint8_t>(file, 4);
|
||||
if(ei_class.is_error()) {
|
||||
return std::move(ei_class).unwrap_error();
|
||||
}
|
||||
bool is_64 = ei_class.unwrap_value() == 2;
|
||||
auto ei_data = load_bytes<std::uint8_t>(file, 5);
|
||||
if(ei_data.is_error()) {
|
||||
return std::move(ei_data).unwrap_error();
|
||||
}
|
||||
bool is_little_endian = ei_data.unwrap_value() == 1;
|
||||
auto ei_version = load_bytes<std::uint8_t>(file, 6);
|
||||
if(ei_version.is_error()) {
|
||||
return std::move(ei_version).unwrap_error();
|
||||
}
|
||||
if(ei_version.unwrap_value() != 1) {
|
||||
return internal_error("Unexpected ELF version " + object_path);
|
||||
}
|
||||
// get image base
|
||||
if(is_64) {
|
||||
return elf_get_module_image_base_from_program_table<64>(object_path, file, is_little_endian);
|
||||
|
||||
@ -45,14 +45,14 @@ namespace detail {
|
||||
}
|
||||
|
||||
inline bool file_is_mach_o(const std::string& object_path) noexcept {
|
||||
try {
|
||||
FILE* file = std::fopen(object_path.c_str(), "rb");
|
||||
if(file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
||||
return is_mach_o(magic);
|
||||
} catch(...) {
|
||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
||||
if(file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
||||
if(magic) {
|
||||
return is_mach_o(magic.unwrap_value());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -108,7 +108,7 @@ namespace detail {
|
||||
};
|
||||
|
||||
class mach_o {
|
||||
std::FILE* file = nullptr;
|
||||
file_wrapper file;
|
||||
std::string object_path;
|
||||
std::uint32_t magic;
|
||||
cpu_type_t cputype;
|
||||
@ -127,11 +127,11 @@ namespace detail {
|
||||
struct symtab_info_data {
|
||||
symtab_command symtab;
|
||||
std::unique_ptr<char[]> stringtab;
|
||||
const char* get_string(std::size_t index) const {
|
||||
Result<const char*, internal_error> get_string(std::size_t index) const {
|
||||
if(stringtab && index < symtab.strsize) {
|
||||
return stringtab.get() + index;
|
||||
} else {
|
||||
throw std::runtime_error("can't retrieve symbol from symtab");
|
||||
return internal_error("can't retrieve symbol from symtab");
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -139,45 +139,69 @@ namespace detail {
|
||||
bool tried_to_load_symtab = false;
|
||||
optional<symtab_info_data> symtab_info;
|
||||
|
||||
public:
|
||||
mach_o(const std::string& object_path) : object_path(object_path) {
|
||||
file = std::fopen(object_path.c_str(), "rb");
|
||||
if(file == nullptr) {
|
||||
throw file_error("Unable to read object file " + object_path);
|
||||
}
|
||||
magic = load_bytes<std::uint32_t>(file, 0);
|
||||
VERIFY(is_mach_o(magic), "File is not Mach-O " + object_path);
|
||||
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() {
|
||||
if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
||||
load_fat_mach();
|
||||
return load_fat_mach();
|
||||
} else {
|
||||
fat_index = 0;
|
||||
if(is_magic_64(magic)) {
|
||||
load_mach<64>();
|
||||
return load_mach<64>();
|
||||
} else {
|
||||
load_mach<32>();
|
||||
return load_mach<32>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~mach_o() {
|
||||
if(file) {
|
||||
std::fclose(file);
|
||||
public:
|
||||
static inline NODISCARD Result<mach_o, internal_error> open_mach_o(const std::string& object_path) {
|
||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
||||
if(file == nullptr) {
|
||||
return internal_error("Unable to read object file {}", object_path);
|
||||
}
|
||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
||||
if(!magic) {
|
||||
return magic.unwrap_error();
|
||||
}
|
||||
if(!is_mach_o(magic.unwrap_value())) {
|
||||
return internal_error("File is not mach-o {}", object_path);
|
||||
}
|
||||
mach_o obj(std::move(file), object_path, magic.unwrap_value());
|
||||
auto result = obj.load();
|
||||
if(result.is_error()) {
|
||||
return result.unwrap_error();
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
std::uintptr_t get_text_vmaddr() {
|
||||
mach_o(mach_o&&) = default;
|
||||
~mach_o() = default;
|
||||
|
||||
Result<std::uintptr_t, internal_error> get_text_vmaddr() {
|
||||
for(const auto& command : load_commands) {
|
||||
if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) {
|
||||
auto segment = command.cmd == LC_SEGMENT_64
|
||||
? load_segment_command<64>(command.file_offset)
|
||||
: load_segment_command<32>(command.file_offset);
|
||||
if(std::strcmp(segment.segname, "__TEXT") == 0) {
|
||||
return segment.vmaddr;
|
||||
if(segment.is_error()) {
|
||||
segment.drop_error();
|
||||
}
|
||||
if(std::strcmp(segment.unwrap_value().segname, "__TEXT") == 0) {
|
||||
return segment.unwrap_value().vmaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// somehow no __TEXT section was found...
|
||||
throw std::runtime_error("Couldn't find __TEXT section while parsing Mach-O object");
|
||||
return internal_error("Couldn't find __TEXT section while parsing Mach-O object");
|
||||
}
|
||||
|
||||
std::size_t get_fat_index() const {
|
||||
@ -189,10 +213,16 @@ namespace detail {
|
||||
int i = 0;
|
||||
for(const auto& command : load_commands) {
|
||||
if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) {
|
||||
auto segment = command.cmd == LC_SEGMENT_64
|
||||
auto segment_load = command.cmd == LC_SEGMENT_64
|
||||
? load_segment_command<64>(command.file_offset)
|
||||
: load_segment_command<32>(command.file_offset);
|
||||
fprintf(stderr, "Load command %d\n", i);
|
||||
if(segment_load.is_error()) {
|
||||
fprintf(stderr, " error\n");
|
||||
segment_load.drop_error();
|
||||
continue;
|
||||
}
|
||||
auto& segment = segment_load.unwrap_value();
|
||||
fprintf(stderr, " cmd %u\n", segment.cmd);
|
||||
fprintf(stderr, " cmdsize %u\n", segment.cmdsize);
|
||||
fprintf(stderr, " segname %s\n", segment.segname);
|
||||
@ -213,8 +243,16 @@ namespace detail {
|
||||
for(const auto& command : load_commands) {
|
||||
if(command.cmd == LC_SYMTAB) {
|
||||
symtab_info_data info;
|
||||
info.symtab = load_symbol_table_command(command.file_offset);
|
||||
info.stringtab = load_string_table(info.symtab.stroff, info.symtab.strsize);
|
||||
auto symtab = load_symbol_table_command(command.file_offset);
|
||||
if(!symtab) {
|
||||
// TODO
|
||||
}
|
||||
info.symtab = symtab.unwrap_value();
|
||||
auto string = load_string_table(info.symtab.stroff, info.symtab.strsize);
|
||||
if(!string) {
|
||||
// TODO
|
||||
}
|
||||
info.stringtab = std::move(string).unwrap_value();
|
||||
symtab_info = std::move(info);
|
||||
break;
|
||||
}
|
||||
@ -263,8 +301,14 @@ namespace detail {
|
||||
int i = 0;
|
||||
for(const auto& command : load_commands) {
|
||||
if(command.cmd == LC_SYMTAB) {
|
||||
auto symtab = load_symbol_table_command(command.file_offset);
|
||||
auto symtab_load = load_symbol_table_command(command.file_offset);
|
||||
fprintf(stderr, "Load command %d\n", i);
|
||||
if(symtab_load.is_error()) {
|
||||
fprintf(stderr, " error\n");
|
||||
symtab_load.drop_error();
|
||||
continue;
|
||||
}
|
||||
auto& symtab = symtab_load.unwrap_value();
|
||||
fprintf(stderr, " cmd %llu\n", to_ull(symtab.cmd));
|
||||
fprintf(stderr, " cmdsize %llu\n", to_ull(symtab.cmdsize));
|
||||
fprintf(stderr, " symoff 0x%llu\n", to_ull(symtab.symoff));
|
||||
@ -272,11 +316,24 @@ namespace detail {
|
||||
fprintf(stderr, " stroff 0x%llu\n", to_ull(symtab.stroff));
|
||||
fprintf(stderr, " strsize %llu\n", to_ull(symtab.strsize));
|
||||
auto stringtab = load_string_table(symtab.stroff, symtab.strsize);
|
||||
if(!stringtab) {
|
||||
stringtab.drop_error();
|
||||
}
|
||||
for(std::size_t j = 0; j < symtab.nsyms; j++) {
|
||||
nlist_64 entry = bits == 32
|
||||
auto entry = bits == 32
|
||||
? load_symtab_entry<32>(symtab.symoff, j)
|
||||
: load_symtab_entry<64>(symtab.symoff, j);
|
||||
print_symbol_table_entry(entry, stringtab, symtab.strsize, j);
|
||||
if(!entry) {
|
||||
fprintf(stderr, "error loading symtab entry\n");
|
||||
entry.drop_error();
|
||||
continue;
|
||||
}
|
||||
print_symbol_table_entry(
|
||||
entry.unwrap_value(),
|
||||
std::move(stringtab).value_or(std::unique_ptr<char[]>(nullptr)),
|
||||
symtab.strsize,
|
||||
j
|
||||
);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
@ -308,9 +365,13 @@ namespace detail {
|
||||
std::string current_module;
|
||||
optional<debug_map_entry> current_function;
|
||||
for(std::size_t j = 0; j < symtab.nsyms; j++) {
|
||||
nlist_64 entry = bits == 32
|
||||
auto load_entry = bits == 32
|
||||
? load_symtab_entry<32>(symtab.symoff, j)
|
||||
: load_symtab_entry<64>(symtab.symoff, j);
|
||||
if(!load_entry) {
|
||||
// TODO
|
||||
}
|
||||
auto& entry = load_entry.unwrap_value();
|
||||
// entry.n_type & N_STAB indicates symbolic debug info
|
||||
if(!(entry.n_type & N_STAB)) {
|
||||
continue;
|
||||
@ -320,15 +381,24 @@ namespace detail {
|
||||
// pass - these encode path and filename for the module, if applicable
|
||||
break;
|
||||
case N_OSO:
|
||||
// sets the module
|
||||
current_module = symtab_info.get_string(entry.n_un.n_strx);
|
||||
{
|
||||
// sets the module
|
||||
auto str = symtab_info.get_string(entry.n_un.n_strx);
|
||||
if(!str) {
|
||||
// TODO
|
||||
}
|
||||
current_module = str.unwrap_value();
|
||||
}
|
||||
break;
|
||||
case N_BNSYM: break; // pass
|
||||
case N_ENSYM: break; // pass
|
||||
case N_FUN:
|
||||
{
|
||||
const char* str = symtab_info.get_string(entry.n_un.n_strx);
|
||||
if(str[0] == 0) {
|
||||
auto str = symtab_info.get_string(entry.n_un.n_strx);
|
||||
if(!str) {
|
||||
// TODO
|
||||
}
|
||||
if(str.unwrap_value()[0] == 0) {
|
||||
// end of function scope
|
||||
if(!current_function) { /**/ }
|
||||
current_function.unwrap().size = entry.n_value;
|
||||
@ -336,7 +406,7 @@ namespace detail {
|
||||
} else {
|
||||
current_function = debug_map_entry{};
|
||||
current_function.unwrap().source_address = entry.n_value;
|
||||
current_function.unwrap().name = str;
|
||||
current_function.unwrap().name = str.unwrap_value();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -353,16 +423,24 @@ namespace detail {
|
||||
const auto& symtab = symtab_info.symtab;
|
||||
// TODO: Take timestamp into account?
|
||||
for(std::size_t j = 0; j < symtab.nsyms; j++) {
|
||||
nlist_64 entry = bits == 32
|
||||
auto load_entry = bits == 32
|
||||
? load_symtab_entry<32>(symtab.symoff, j)
|
||||
: load_symtab_entry<64>(symtab.symoff, j);
|
||||
if(!load_entry) {
|
||||
// TODO
|
||||
}
|
||||
auto& entry = load_entry.unwrap_value();
|
||||
if(entry.n_type & N_STAB) {
|
||||
continue;
|
||||
}
|
||||
if((entry.n_type & N_TYPE) == N_SECT) {
|
||||
auto str = symtab_info.get_string(entry.n_un.n_strx);
|
||||
if(!str) {
|
||||
// TODO
|
||||
}
|
||||
symbols.push_back({
|
||||
entry.n_value,
|
||||
symtab_info.get_string(entry.n_un.n_strx)
|
||||
str.unwrap_value()
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -390,12 +468,16 @@ namespace detail {
|
||||
|
||||
private:
|
||||
template<std::size_t Bits>
|
||||
void load_mach() {
|
||||
Result<monostate, internal_error> load_mach() {
|
||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
||||
bits = Bits;
|
||||
using Mach_Header = typename std::conditional<Bits == 32, mach_header, mach_header_64>::type;
|
||||
std::size_t header_size = sizeof(Mach_Header);
|
||||
Mach_Header header = load_bytes<Mach_Header>(file, load_base);
|
||||
auto load_header = load_bytes<Mach_Header>(file, load_base);
|
||||
if(!load_header) {
|
||||
return load_header.unwrap_error();
|
||||
}
|
||||
Mach_Header& header = load_header.unwrap_value();
|
||||
magic = header.magic;
|
||||
if(should_swap()) {
|
||||
swap_mach_header(header);
|
||||
@ -412,19 +494,28 @@ namespace detail {
|
||||
// iterate load commands
|
||||
std::uint32_t actual_offset = load_commands_offset;
|
||||
for(std::uint32_t i = 0; i < ncmds; i++) {
|
||||
load_command cmd = load_bytes<load_command>(file, actual_offset);
|
||||
auto load_cmd = load_bytes<load_command>(file, actual_offset);
|
||||
if(!load_cmd) {
|
||||
return load_cmd.unwrap_error();
|
||||
}
|
||||
load_command& cmd = load_cmd.unwrap_value();
|
||||
if(should_swap()) {
|
||||
swap_load_command(&cmd, NX_UnknownByteOrder);
|
||||
}
|
||||
load_commands.push_back({ actual_offset, cmd.cmd, cmd.cmdsize });
|
||||
actual_offset += cmd.cmdsize;
|
||||
}
|
||||
return monostate{};
|
||||
}
|
||||
|
||||
void load_fat_mach() {
|
||||
Result<monostate, internal_error> load_fat_mach() {
|
||||
std::size_t header_size = sizeof(fat_header);
|
||||
std::size_t arch_size = sizeof(fat_arch);
|
||||
fat_header header = load_bytes<fat_header>(file, 0);
|
||||
auto load_header = load_bytes<fat_header>(file, 0);
|
||||
if(!load_header) {
|
||||
return load_header.unwrap_error();
|
||||
}
|
||||
fat_header& header = load_header.unwrap_value();
|
||||
if(should_swap()) {
|
||||
swap_fat_header(&header, NX_UnknownByteOrder);
|
||||
}
|
||||
@ -458,7 +549,11 @@ namespace detail {
|
||||
fat_arches.reserve(header.nfat_arch);
|
||||
off_t arch_offset = (off_t)header_size;
|
||||
for(std::size_t i = 0; i < header.nfat_arch; i++) {
|
||||
fat_arch arch = load_bytes<fat_arch>(file, arch_offset);
|
||||
auto load_arch = load_bytes<fat_arch>(file, arch_offset);
|
||||
if(!load_arch) {
|
||||
return load_arch.unwrap_error();
|
||||
}
|
||||
fat_arch& arch = load_arch.unwrap_value();
|
||||
if(should_swap()) {
|
||||
swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
|
||||
}
|
||||
@ -474,24 +569,31 @@ namespace detail {
|
||||
);
|
||||
if(best) {
|
||||
off_t mach_header_offset = (off_t)best->offset;
|
||||
std::uint32_t magic = load_bytes<std::uint32_t>(file, mach_header_offset);
|
||||
auto magic = load_bytes<std::uint32_t>(file, mach_header_offset);
|
||||
if(!magic) {
|
||||
return magic.unwrap_error();
|
||||
}
|
||||
load_base = mach_header_offset;
|
||||
fat_index = best - fat_arches.data();
|
||||
if(is_magic_64(magic)) {
|
||||
if(is_magic_64(magic.unwrap_value())) {
|
||||
load_mach<64>();
|
||||
} else {
|
||||
load_mach<32>();
|
||||
}
|
||||
return;
|
||||
return monostate{};
|
||||
}
|
||||
// If this is reached... something went wrong. The cpu we're on wasn't found.
|
||||
throw std::runtime_error("Couldn't find appropriate architecture in fat Mach-O");
|
||||
return internal_error("Couldn't find appropriate architecture in fat Mach-O");
|
||||
}
|
||||
|
||||
template<std::size_t Bits>
|
||||
segment_command_64 load_segment_command(std::uint32_t offset) const {
|
||||
Result<segment_command_64, internal_error> load_segment_command(std::uint32_t offset) const {
|
||||
using Segment_Command = typename std::conditional<Bits == 32, segment_command, segment_command_64>::type;
|
||||
Segment_Command segment = load_bytes<Segment_Command>(file, offset);
|
||||
auto load_segment = load_bytes<Segment_Command>(file, offset);
|
||||
if(!load_segment) {
|
||||
return load_segment.unwrap_error();
|
||||
}
|
||||
Segment_Command& segment = load_segment.unwrap_value();
|
||||
ASSERT(segment.cmd == LC_SEGMENT_64 || segment.cmd == LC_SEGMENT);
|
||||
if(should_swap()) {
|
||||
swap_segment_command(segment);
|
||||
@ -513,8 +615,12 @@ namespace detail {
|
||||
return common;
|
||||
}
|
||||
|
||||
symtab_command load_symbol_table_command(std::uint32_t offset) const {
|
||||
symtab_command symtab = load_bytes<symtab_command>(file, offset);
|
||||
Result<symtab_command, internal_error> load_symbol_table_command(std::uint32_t offset) const {
|
||||
auto load_symtab = load_bytes<symtab_command>(file, offset);
|
||||
if(!load_symtab) {
|
||||
return load_symtab.unwrap_error();
|
||||
}
|
||||
symtab_command& symtab = load_symtab.unwrap_value();
|
||||
ASSERT(symtab.cmd == LC_SYMTAB);
|
||||
if(should_swap()) {
|
||||
swap_symtab_command(&symtab, NX_UnknownByteOrder);
|
||||
@ -523,10 +629,14 @@ namespace detail {
|
||||
}
|
||||
|
||||
template<std::size_t Bits>
|
||||
nlist_64 load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const {
|
||||
Result<nlist_64, internal_error> load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const {
|
||||
using Nlist = typename std::conditional<Bits == 32, struct nlist, struct nlist_64>::type;
|
||||
uint32_t offset = load_base + symbol_base + index * sizeof(Nlist);
|
||||
Nlist entry = load_bytes<Nlist>(file, offset);
|
||||
auto load_entry = load_bytes<Nlist>(file, offset);
|
||||
if(!load_entry) {
|
||||
return load_entry.unwrap_error();
|
||||
}
|
||||
Nlist& entry = load_entry.unwrap_value();
|
||||
if(should_swap()) {
|
||||
swap_nlist(entry);
|
||||
}
|
||||
@ -540,10 +650,14 @@ namespace detail {
|
||||
return common;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const {
|
||||
Result<std::unique_ptr<char[]>, internal_error> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const {
|
||||
std::unique_ptr<char[]> buffer(new char[byte_count + 1]);
|
||||
VERIFY(std::fseek(file, load_base + offset, SEEK_SET) == 0, "fseek error");
|
||||
VERIFY(std::fread(buffer.get(), sizeof(char), byte_count, file) == byte_count, "fread error");
|
||||
if(std::fseek(file, load_base + offset, SEEK_SET) != 0) {
|
||||
return internal_error("fseek error while loading mach-o symbol table");
|
||||
}
|
||||
if(std::fread(buffer.get(), sizeof(char), byte_count, file) != byte_count) {
|
||||
return internal_error("fread error while loading mach-o symbol table");
|
||||
}
|
||||
buffer[byte_count] = 0; // just out of an abundance of caution
|
||||
return buffer;
|
||||
}
|
||||
@ -553,13 +667,17 @@ namespace detail {
|
||||
}
|
||||
};
|
||||
|
||||
inline bool macho_is_fat(const std::string& object_path) {
|
||||
inline Result<bool, internal_error> macho_is_fat(const std::string& object_path) {
|
||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
||||
if(file == nullptr) {
|
||||
throw file_error("Unable to read object file " + object_path);
|
||||
return internal_error("Unable to read object file {}", object_path);
|
||||
}
|
||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
||||
if(!magic) {
|
||||
return magic.unwrap_error();
|
||||
} else {
|
||||
return is_fat_magic(magic.unwrap_value());
|
||||
}
|
||||
std::uint32_t magic = load_bytes<std::uint32_t>(file, 0);
|
||||
return is_fat_magic(magic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
#if IS_LINUX
|
||||
inline std::uintptr_t get_module_image_base(const std::string& object_path) {
|
||||
inline Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, std::uintptr_t> cache;
|
||||
@ -34,14 +34,18 @@ namespace detail {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = elf_get_module_image_base(object_path);
|
||||
cache.insert(it, {object_path, base});
|
||||
// TODO: Cache the error
|
||||
if(base.is_error()) {
|
||||
return base.unwrap_error();
|
||||
}
|
||||
cache.insert(it, {object_path, base.unwrap_value()});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
#elif IS_APPLE
|
||||
inline std::uintptr_t get_module_image_base(const std::string& object_path) {
|
||||
inline Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path) {
|
||||
// We have to parse the Mach-O to find the offset of the text section.....
|
||||
// I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
|
||||
// now that there is only one, and I'm using only the first section entry within that load command.
|
||||
@ -52,15 +56,23 @@ namespace detail {
|
||||
if(it == cache.end()) {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = mach_o(object_path).get_text_vmaddr();
|
||||
cache.insert(it, {object_path, base});
|
||||
auto obj = mach_o::open_mach_o(object_path);
|
||||
// TODO: Cache the error
|
||||
if(!obj) {
|
||||
return obj.unwrap_error();
|
||||
}
|
||||
auto base = obj.unwrap_value().get_text_vmaddr();
|
||||
if(!base) {
|
||||
return base.unwrap_error();
|
||||
}
|
||||
cache.insert(it, {object_path, base.unwrap_value()});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
#else // Windows
|
||||
inline std::uintptr_t get_module_image_base(const std::string& object_path) {
|
||||
inline Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, std::uintptr_t> cache;
|
||||
@ -69,7 +81,11 @@ namespace detail {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = pe_get_module_image_base(object_path);
|
||||
cache.insert(it, {object_path, base});
|
||||
// TODO: Cache the error
|
||||
if(!base) {
|
||||
return base.unwrap_error();
|
||||
}
|
||||
cache.insert(it, {object_path, base.unwrap_value()});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
|
||||
@ -58,9 +58,14 @@ namespace detail {
|
||||
frame.object_address = 0;
|
||||
if(dladdr(reinterpret_cast<void*>(address), &info)) { // thread safe
|
||||
frame.object_path = info.dli_fname;
|
||||
frame.object_address = address
|
||||
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
||||
+ get_module_image_base(info.dli_fname);
|
||||
auto base = get_module_image_base(info.dli_fname);
|
||||
if(base.has_value()) {
|
||||
frame.object_address = address
|
||||
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
||||
+ base.unwrap_value();
|
||||
} else {
|
||||
base.drop_error();
|
||||
}
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
@ -99,9 +104,14 @@ namespace detail {
|
||||
&handle
|
||||
)) {
|
||||
frame.object_path = get_module_name(handle);
|
||||
frame.object_address = address
|
||||
- reinterpret_cast<std::uintptr_t>(handle)
|
||||
+ get_module_image_base(frame.object_path);
|
||||
auto base = get_module_image_base(frame.object_path);
|
||||
if(base.has_value()) {
|
||||
frame.object_address = address
|
||||
- reinterpret_cast<std::uintptr_t>(handle)
|
||||
+ base.unwrap_value();
|
||||
} else {
|
||||
base.drop_error();
|
||||
}
|
||||
} else {
|
||||
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
||||
}
|
||||
@ -119,9 +129,13 @@ namespace detail {
|
||||
}
|
||||
|
||||
inline object_frame resolve_safe_object_frame(const safe_object_frame& frame) {
|
||||
auto base = get_module_image_base(frame.object_path);
|
||||
if(base.is_error()) {
|
||||
throw base.unwrap_error(); // This throw is intentional
|
||||
}
|
||||
return {
|
||||
frame.raw_address,
|
||||
frame.address_relative_to_object_start + get_module_image_base(frame.object_path),
|
||||
frame.address_relative_to_object_start + base.unwrap_value(),
|
||||
frame.object_path
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ namespace detail {
|
||||
}
|
||||
}
|
||||
|
||||
inline std::uintptr_t pe_get_module_image_base(const std::string& object_path) {
|
||||
inline Result<std::uintptr_t, internal_error> pe_get_module_image_base(const std::string& object_path) {
|
||||
// https://drive.google.com/file/d/0B3_wGJkuWLytbnIxY1J5WUs4MEk/view?pli=1&resourcekey=0-n5zZ2UW39xVTH8ZSu6C2aQ
|
||||
// https://0xrick.github.io/win-internals/pe3/
|
||||
// Endianness should always be little for dos and pe headers
|
||||
@ -34,21 +34,40 @@ namespace detail {
|
||||
errno_t ret = fopen_s(&file_ptr, object_path.c_str(), "rb");
|
||||
auto file = raii_wrap(std::move(file_ptr), file_deleter);
|
||||
if(ret != 0 || file == nullptr) {
|
||||
throw file_error("Unable to read object file " + object_path);
|
||||
throw internal_error("Unable to read object file {}", object_path);
|
||||
}
|
||||
auto magic = load_bytes<std::array<char, 2>>(file, 0);
|
||||
VERIFY(std::memcmp(magic.data(), "MZ", 2) == 0, "File is not a PE file " + object_path);
|
||||
DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c
|
||||
DWORD nt_header_offset = e_lfanew;
|
||||
if(!magic) {
|
||||
return magic.unwrap_error();
|
||||
}
|
||||
if(std::memcmp(magic.unwrap_value().data(), "MZ", 2) != 0) {
|
||||
return internal_error("File is not a PE file {}", object_path);
|
||||
}
|
||||
auto e_lfanew = load_bytes<DWORD>(file, 0x3c); // dos header + 0x3c
|
||||
if(!e_lfanew) {
|
||||
return e_lfanew.unwrap_error();
|
||||
}
|
||||
DWORD nt_header_offset = pe_byteswap_if_needed(e_lfanew.unwrap_value());
|
||||
auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0
|
||||
VERIFY(std::memcmp(signature.data(), "PE\0\0", 4) == 0, "File is not a PE file " + object_path);
|
||||
WORD size_of_optional_header = pe_byteswap_if_needed(
|
||||
load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10
|
||||
);
|
||||
VERIFY(size_of_optional_header != 0);
|
||||
WORD optional_header_magic = pe_byteswap_if_needed(
|
||||
load_bytes<WORD>(file, nt_header_offset + 0x18) // optional header + 0x0
|
||||
);
|
||||
if(!signature) {
|
||||
return signature.unwrap_error();
|
||||
}
|
||||
if(std::memcmp(signature.unwrap_value().data(), "PE\0\0", 4) != 0) {
|
||||
return internal_error("File is not a PE file {}", object_path);
|
||||
}
|
||||
auto size_of_optional_header_raw = load_bytes<WORD>(file, nt_header_offset + 4 + 0x10); // file header + 0x10
|
||||
if(!size_of_optional_header_raw) {
|
||||
return size_of_optional_header_raw.unwrap_error();
|
||||
}
|
||||
WORD size_of_optional_header = pe_byteswap_if_needed(size_of_optional_header_raw.unwrap_value());
|
||||
if(size_of_optional_header == 0) {
|
||||
return internal_error("Unexpected optional header size for PE file");
|
||||
}
|
||||
auto optional_header_magic_raw = load_bytes<WORD>(file, nt_header_offset + 0x18); // optional header + 0x0
|
||||
if(!optional_header_magic_raw) {
|
||||
return optional_header_magic_raw.unwrap_error();
|
||||
}
|
||||
WORD optional_header_magic = pe_byteswap_if_needed(optional_header_magic_raw.unwrap_value());
|
||||
VERIFY(
|
||||
optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC,
|
||||
"PE file does not match expected bit-mode " + object_path
|
||||
@ -56,19 +75,19 @@ namespace detail {
|
||||
// finally get image base
|
||||
if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
// 32 bit
|
||||
return to<std::uintptr_t>(
|
||||
pe_byteswap_if_needed(
|
||||
load_bytes<DWORD>(file, nt_header_offset + 0x18 + 0x1c) // optional header + 0x1c
|
||||
)
|
||||
);
|
||||
auto bytes = load_bytes<DWORD>(file, nt_header_offset + 0x18 + 0x1c); // optional header + 0x1c
|
||||
if(!bytes) {
|
||||
return bytes.unwrap_error();
|
||||
}
|
||||
return to<std::uintptr_t>(pe_byteswap_if_needed(bytes.unwrap_value()));
|
||||
} else {
|
||||
// 64 bit
|
||||
// I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD
|
||||
return to<std::uintptr_t>(
|
||||
pe_byteswap_if_needed(
|
||||
load_bytes<unsigned __int64>(file, nt_header_offset + 0x18 + 0x18) // optional header + 0x18
|
||||
)
|
||||
);
|
||||
auto bytes = load_bytes<unsigned __int64>(file, nt_header_offset + 0x18 + 0x18); // optional header + 0x18
|
||||
if(!bytes) {
|
||||
return bytes.unwrap_error();
|
||||
}
|
||||
return to<std::uintptr_t>(pe_byteswap_if_needed(bytes.unwrap_value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,9 +70,8 @@ namespace dbghelp {
|
||||
if(FAILABLE) {
|
||||
return (T)-1;
|
||||
} else {
|
||||
throw std::logic_error(
|
||||
std::string("SymGetTypeInfo failed: ")
|
||||
+ std::system_error(GetLastError(), std::system_category()).what()
|
||||
throw internal_error(
|
||||
"SymGetTypeInfo failed: {}", std::system_error(GetLastError(), std::system_category()).what()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -85,9 +84,8 @@ namespace dbghelp {
|
||||
if(
|
||||
!SymGetTypeInfo(proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), &info)
|
||||
) {
|
||||
throw std::logic_error(
|
||||
std::string("SymGetTypeInfo failed: ")
|
||||
+ std::system_error(GetLastError(), std::system_category()).what()
|
||||
throw internal_error(
|
||||
"SymGetTypeInfo failed: {}", std::system_error(GetLastError(), std::system_category()).what()
|
||||
);
|
||||
}
|
||||
// special case to properly free a buffer and convert string to narrow chars, only used for
|
||||
@ -247,15 +245,15 @@ namespace dbghelp {
|
||||
children
|
||||
)
|
||||
) {
|
||||
throw std::logic_error(
|
||||
std::string("SymGetTypeInfo failed: ")
|
||||
+ std::system_error(GetLastError(), std::system_category()).what()
|
||||
throw internal_error(
|
||||
"SymGetTypeInfo failed: {}",
|
||||
std::system_error(GetLastError(), std::system_category()).what()
|
||||
);
|
||||
}
|
||||
// get children type
|
||||
std::string extent = "(";
|
||||
if(children->Start != 0) {
|
||||
throw std::logic_error("Error: children->Start == 0");
|
||||
throw internal_error("Error: children->Start == 0");
|
||||
}
|
||||
for(std::size_t i = 0; i < n_children; i++) {
|
||||
extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase);
|
||||
@ -428,7 +426,7 @@ namespace dbghelp {
|
||||
get_syminit_manager().init(proc);
|
||||
} else {
|
||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
||||
throw std::logic_error("Cpptrace SymInitialize failed");
|
||||
throw internal_error("Cpptrace SymInitialize failed");
|
||||
}
|
||||
}
|
||||
for(const auto frame : frames) {
|
||||
@ -445,7 +443,7 @@ namespace dbghelp {
|
||||
}
|
||||
if(get_cache_mode() != cache_mode::prioritize_speed) {
|
||||
if(!SymCleanup(proc)) {
|
||||
throw std::logic_error("Cpptrace SymCleanup failed");
|
||||
throw internal_error("Cpptrace SymCleanup failed");
|
||||
}
|
||||
}
|
||||
return trace;
|
||||
|
||||
@ -15,9 +15,12 @@ namespace libdl {
|
||||
stacktrace_frame resolve_frame(const frame_ptr addr) {
|
||||
Dl_info info;
|
||||
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe
|
||||
auto base = get_module_image_base(info.dli_fname);
|
||||
return {
|
||||
addr,
|
||||
addr - reinterpret_cast<std::uintptr_t>(info.dli_fbase) + get_module_image_base(info.dli_fname),
|
||||
base.has_value()
|
||||
? addr - reinterpret_cast<std::uintptr_t>(info.dli_fbase) + base.unwrap_value()
|
||||
: 0,
|
||||
nullable<std::uint32_t>::null(),
|
||||
nullable<std::uint32_t>::null(),
|
||||
info.dli_fname ? info.dli_fname : "",
|
||||
|
||||
@ -38,7 +38,7 @@ namespace libbacktrace {
|
||||
}
|
||||
|
||||
void error_callback(void*, const char* msg, int errnum) {
|
||||
throw std::runtime_error(microfmt::format("Libbacktrace error: {}, code {}\n", msg, errnum));
|
||||
throw internal_error("Libbacktrace error: {}, code {}", msg, errnum);
|
||||
}
|
||||
|
||||
backtrace_state* get_backtrace_state() {
|
||||
|
||||
@ -149,8 +149,15 @@ namespace libdwarf {
|
||||
}
|
||||
use_buffer = false; // we resolved dSYM above as appropriate
|
||||
}
|
||||
if(macho_is_fat(object_path)) {
|
||||
universal_number = mach_o(object_path).get_fat_index();
|
||||
auto result = macho_is_fat(object_path);
|
||||
if(result.is_error()) {
|
||||
result.drop_error();
|
||||
} else if(result.unwrap_value()) {
|
||||
auto obj = mach_o::open_mach_o(object_path);
|
||||
if(!obj) {
|
||||
// TODO
|
||||
}
|
||||
universal_number = obj.unwrap_value().get_fat_index();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -901,7 +908,9 @@ namespace libdwarf {
|
||||
retrieve_symbol(vec_it->die, pc, vec_it->dwversion, frame, inlines);
|
||||
}
|
||||
} else {
|
||||
ASSERT(cu_cache.size() == 0, "Vec should be empty?");
|
||||
// I've had this happen for _start, where there is a cached CU for the object but _start is outside
|
||||
// of the CU's PC range
|
||||
// ASSERT(cu_cache.size() == 0, "Vec should be empty?");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -992,7 +1001,11 @@ namespace libdwarf {
|
||||
// the path doesn't exist
|
||||
std::unordered_map<std::string, uint64_t> symbols;
|
||||
this->symbols = symbols;
|
||||
auto symbol_table = mach_o(object_path).symbol_table();
|
||||
auto obj = mach_o::open_mach_o(object_path);
|
||||
if(!obj) {
|
||||
// TODO
|
||||
}
|
||||
auto symbol_table = obj.unwrap_value().symbol_table();
|
||||
for(const auto& symbol : symbol_table) {
|
||||
symbols[symbol.name] = symbol.address;
|
||||
}
|
||||
@ -1045,7 +1058,11 @@ namespace libdwarf {
|
||||
debug_map_resolver(const std::string& source_object_path) {
|
||||
// load mach-o
|
||||
// TODO: Cache somehow?
|
||||
mach_o source_mach(source_object_path);
|
||||
auto obj = mach_o::open_mach_o(source_object_path);
|
||||
if(!obj) {
|
||||
// TODO
|
||||
}
|
||||
mach_o& source_mach = obj.unwrap_value();
|
||||
auto source_debug_map = source_mach.get_debug_map();
|
||||
// get symbol entries from debug map, as well as the various object files used to make this binary
|
||||
for(auto& entry : source_debug_map) {
|
||||
|
||||
@ -110,7 +110,7 @@ namespace detail {
|
||||
get_syminit_manager().init(proc);
|
||||
} else {
|
||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
||||
throw std::logic_error("Cpptrace SymInitialize failed");
|
||||
throw internal_error("Cpptrace SymInitialize failed");
|
||||
}
|
||||
}
|
||||
while(trace.size() < max_depth) {
|
||||
@ -147,7 +147,7 @@ namespace detail {
|
||||
}
|
||||
if(get_cache_mode() != cache_mode::prioritize_speed) {
|
||||
if(!SymCleanup(proc)) {
|
||||
throw std::logic_error("Cpptrace SymCleanup failed");
|
||||
throw internal_error("Cpptrace SymCleanup failed");
|
||||
}
|
||||
}
|
||||
return trace;
|
||||
|
||||
@ -50,6 +50,14 @@
|
||||
#define MAGENTA ESC "35m"
|
||||
#define CYAN ESC "36m"
|
||||
|
||||
#if IS_GCC || IS_CLANG
|
||||
#define NODISCARD __attribute__((warn_unused_result))
|
||||
// #elif IS_MSVC && _MSC_VER >= 1700
|
||||
// #define NODISCARD _Check_return_
|
||||
#else
|
||||
#define NODISCARD
|
||||
#endif
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
static const stacktrace_frame null_frame {
|
||||
|
||||
@ -25,7 +25,7 @@ namespace detail {
|
||||
void init(HANDLE proc) {
|
||||
if(set.count(proc) == 0) {
|
||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
||||
throw std::logic_error(microfmt::format("SymInitialize failed {}", GetLastError()));
|
||||
throw internal_error("SymInitialize failed {}", GetLastError());
|
||||
}
|
||||
set.insert(proc);
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ namespace libdwarf {
|
||||
char* msg = dwarf_errmsg(error);
|
||||
(void)dbg;
|
||||
// dwarf_dealloc_error(dbg, error);
|
||||
throw std::runtime_error(microfmt::format("Cpptrace dwarf error {} {}\n", ev, msg));
|
||||
throw internal_error("Cpptrace dwarf error {} {}", ev, msg);
|
||||
}
|
||||
|
||||
struct die_object {
|
||||
|
||||
@ -17,10 +17,12 @@
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
class file_error : public std::exception {
|
||||
class internal_error : public std::exception {
|
||||
std::string msg;
|
||||
public:
|
||||
file_error(std::string path) : msg("Unable to read file " + std::move(path)) {}
|
||||
internal_error(std::string message) : msg(std::move(message)) {}
|
||||
template<typename... Args>
|
||||
internal_error(const char* format, Args&&... args) : msg(microfmt::format(format, args...)) {}
|
||||
const char* what() const noexcept override {
|
||||
return msg.c_str();
|
||||
}
|
||||
@ -57,22 +59,18 @@ namespace detail {
|
||||
const char* action = assert_actions[static_cast<std::underlying_type<assert_type>::type>(type)];
|
||||
const char* name = assert_names[static_cast<std::underlying_type<assert_type>::type>(type)];
|
||||
if(message == "") {
|
||||
throw std::logic_error(
|
||||
microfmt::format(
|
||||
"Cpptrace {} failed at {}:{}: {}\n"
|
||||
" %s(%s);\n",
|
||||
action, location.file, location.line, signature,
|
||||
name, expression
|
||||
)
|
||||
throw internal_error(
|
||||
"Cpptrace {} failed at {}:{}: {}\n"
|
||||
" %s(%s);\n",
|
||||
action, location.file, location.line, signature,
|
||||
name, expression
|
||||
);
|
||||
} else {
|
||||
throw std::logic_error(
|
||||
microfmt::format(
|
||||
"Cpptrace {} failed at {}:{}: {}: {}\n"
|
||||
" %s(%s);\n",
|
||||
action, location.file, location.line, signature, message.c_str(),
|
||||
name, expression
|
||||
)
|
||||
throw internal_error(
|
||||
"Cpptrace {} failed at {}:{}: {}: {}\n"
|
||||
" %s(%s);\n",
|
||||
action, location.file, location.line, signature, message.c_str(),
|
||||
name, expression
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -83,18 +81,14 @@ namespace detail {
|
||||
const std::string& message = ""
|
||||
) {
|
||||
if(message == "") {
|
||||
throw std::logic_error(
|
||||
microfmt::format(
|
||||
"Cpptrace panic {}:{}: {}\n",
|
||||
location.file, location.line, signature
|
||||
)
|
||||
throw internal_error(
|
||||
"Cpptrace panic {}:{}: {}\n",
|
||||
location.file, location.line, signature
|
||||
);
|
||||
} else {
|
||||
throw std::logic_error(
|
||||
microfmt::format(
|
||||
"Cpptrace panic {}:{}: {}: {}\n",
|
||||
location.file, location.line, signature, message.c_str()
|
||||
)
|
||||
throw internal_error(
|
||||
"Cpptrace panic {}:{}: {}: {}\n",
|
||||
location.file, location.line, signature, message.c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ namespace microfmt {
|
||||
} else if(*it == '{') { // try to parse variable width
|
||||
MICROFMT_ASSERT(peek() == '}');
|
||||
it += 2;
|
||||
MICROFMT_ASSERT(arg_i < N);
|
||||
MICROFMT_ASSERT(arg_i < args.size());
|
||||
options.width = args[arg_i++].unwrap_int();
|
||||
}
|
||||
// try to parse fill/base
|
||||
@ -277,7 +277,7 @@ namespace microfmt {
|
||||
}
|
||||
}
|
||||
MICROFMT_ASSERT(*it == '}');
|
||||
MICROFMT_ASSERT(arg_i < N);
|
||||
MICROFMT_ASSERT(arg_i < args.size());
|
||||
args[arg_i++].write(str, options);
|
||||
}
|
||||
} else if(*it == '}') {
|
||||
|
||||
@ -192,15 +192,6 @@ namespace detail {
|
||||
static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
|
||||
static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
|
||||
|
||||
// TODO: Re-evaluate use of off_t
|
||||
template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0>
|
||||
T load_bytes(std::FILE* object_file, off_t offset) {
|
||||
T object;
|
||||
VERIFY(std::fseek(object_file, offset, SEEK_SET) == 0, "fseek error");
|
||||
VERIFY(std::fread(&object, sizeof(T), 1, object_file) == 1, "fread error");
|
||||
return object;
|
||||
}
|
||||
|
||||
struct nullopt_t {};
|
||||
|
||||
static constexpr nullopt_t nullopt;
|
||||
@ -309,45 +300,156 @@ namespace detail {
|
||||
holds_value = false;
|
||||
}
|
||||
|
||||
T& unwrap() & {
|
||||
if(!holds_value) {
|
||||
throw std::runtime_error{"Optional does not contain a value"};
|
||||
}
|
||||
NODISCARD T& unwrap() & {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return uvalue;
|
||||
}
|
||||
|
||||
const T& unwrap() const & {
|
||||
if(!holds_value) {
|
||||
throw std::runtime_error{"Optional does not contain a value"};
|
||||
}
|
||||
NODISCARD const T& unwrap() const & {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return uvalue;
|
||||
}
|
||||
|
||||
T&& unwrap() && {
|
||||
if(!holds_value) {
|
||||
throw std::runtime_error{"Optional does not contain a value"};
|
||||
}
|
||||
NODISCARD T&& unwrap() && {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return std::move(uvalue);
|
||||
}
|
||||
|
||||
const T&& unwrap() const && {
|
||||
if(!holds_value) {
|
||||
throw std::runtime_error{"Optional does not contain a value"};
|
||||
}
|
||||
NODISCARD const T&& unwrap() const && {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return std::move(uvalue);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
T value_or(U&& default_value) const & {
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
T value_or(U&& default_value) && {
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Better dump error
|
||||
// TODO: Explicit constructors for value, then add Ok()/Error() helpers
|
||||
template<typename T, typename E, typename std::enable_if<!std::is_same<T, E>::value, int>::type = 0>
|
||||
class Result {
|
||||
// Not using a union because I don't want to have to deal with that
|
||||
union {
|
||||
T value_;
|
||||
E error_;
|
||||
};
|
||||
enum class member { value, error };
|
||||
member active;
|
||||
public:
|
||||
Result(T value) : value_(std::move(value)), active(member::value) {}
|
||||
Result(E error) : error_(std::move(error)), active(member::error) {}
|
||||
Result(Result&& other) : active(other.active) {
|
||||
if(other.active == member::value) {
|
||||
new (&value_) T(std::move(other.value_));
|
||||
} else {
|
||||
new (&error_) E(std::move(other.error_));
|
||||
}
|
||||
}
|
||||
~Result() {
|
||||
if(active == member::value) {
|
||||
value_.~T();
|
||||
} else {
|
||||
error_.~E();
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return active == member::value;
|
||||
}
|
||||
|
||||
bool is_error() const {
|
||||
return active == member::error;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return has_value();
|
||||
}
|
||||
|
||||
NODISCARD optional<T> value() const & {
|
||||
return has_value() ? value_ : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<E> error() const & {
|
||||
return is_error() ? error_ : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<T> value() && {
|
||||
return has_value() ? std::move(value_) : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<E> error() && {
|
||||
return is_error() ? std::move(error_) : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD T& unwrap_value() & {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return value_;
|
||||
}
|
||||
|
||||
NODISCARD const T& unwrap_value() const & {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return value_;
|
||||
}
|
||||
|
||||
NODISCARD T unwrap_value() && {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return std::move(value_);
|
||||
}
|
||||
|
||||
NODISCARD E& unwrap_error() & {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return error_;
|
||||
}
|
||||
|
||||
NODISCARD const E& unwrap_error() const & {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return error_;
|
||||
}
|
||||
|
||||
NODISCARD E unwrap_error() && {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return std::move(error_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return has_value() ? value_ : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return has_value() ? std::move(value_) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
void drop_error() const {
|
||||
if(is_error()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct monostate {};
|
||||
|
||||
// TODO: Re-evaluate use of off_t
|
||||
template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0>
|
||||
Result<T, internal_error> load_bytes(std::FILE* object_file, off_t offset) {
|
||||
T object;
|
||||
if(std::fseek(object_file, offset, SEEK_SET) != 0) {
|
||||
return internal_error("fseek error");
|
||||
}
|
||||
if(std::fread(&object, sizeof(T), 1, object_file) != 1) {
|
||||
return internal_error("fread error");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
// shamelessly stolen from stackoverflow
|
||||
inline bool directory_exists(const std::string& path) {
|
||||
#if IS_WINDOWS
|
||||
@ -385,6 +487,8 @@ namespace detail {
|
||||
return static_cast<U>(v);
|
||||
}
|
||||
|
||||
// TODO: Rework some stuff here. Not sure deleters should be optional or moved.
|
||||
// Also allow file_wrapper file = std::fopen(object_path.c_str(), "rb");
|
||||
template<
|
||||
typename T,
|
||||
typename D
|
||||
@ -460,6 +564,8 @@ namespace detail {
|
||||
fclose(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
using file_wrapper = raii_wrapper<std::FILE*, void(*)(std::FILE*)>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user