Refactor PE header parsing out of addr2line code
This commit is contained in:
parent
d12cd313d3
commit
0e701903ed
@ -250,6 +250,14 @@ static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result");
|
|||||||
static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
|
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");
|
static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<std::is_pod<T>::value, int>::type = 0>
|
||||||
|
T load_bytes(FILE* obj_file, off_t offset) {
|
||||||
|
T object;
|
||||||
|
internal_verify(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
|
||||||
|
internal_verify(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
#include <mach-o/loader.h>
|
#include <mach-o/loader.h>
|
||||||
#include <mach-o/swap.h>
|
#include <mach-o/swap.h>
|
||||||
#include <mach-o/fat.h>
|
#include <mach-o/fat.h>
|
||||||
@ -13,15 +15,6 @@
|
|||||||
// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
|
// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
|
||||||
// and https://lowlevelbits.org/parsing-mach-o-files/
|
// and https://lowlevelbits.org/parsing-mach-o-files/
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T load_bytes(FILE* obj_file, off_t offset) {
|
|
||||||
static_assert(std::is_pod<T>::value, "Expected POD type");
|
|
||||||
T object;
|
|
||||||
fseek(obj_file, offset, SEEK_SET);
|
|
||||||
fread(&object, sizeof(T), 1, obj_file);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_magic_64(uint32_t magic) {
|
static bool is_magic_64(uint32_t magic) {
|
||||||
return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
|
return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
|
||||||
}
|
}
|
||||||
@ -42,7 +35,7 @@ static bool should_swap_bytes(uint32_t magic) {
|
|||||||
#error "Unknown CPU architecture"
|
#error "Unknown CPU architecture"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uintptr_t get_text_vmaddr_from_segments(FILE* obj_file, off_t offset, bool should_swap, uint32_t ncmds) {
|
static uintptr_t macho_get_text_vmaddr_from_segments(FILE* obj_file, off_t offset, bool should_swap, uint32_t ncmds) {
|
||||||
off_t actual_offset = offset;
|
off_t actual_offset = offset;
|
||||||
for(uint32_t i = 0; i < ncmds; i++) {
|
for(uint32_t i = 0; i < ncmds; i++) {
|
||||||
load_command cmd = load_bytes<load_command>(obj_file, actual_offset);
|
load_command cmd = load_bytes<load_command>(obj_file, actual_offset);
|
||||||
@ -77,7 +70,7 @@ static uintptr_t get_text_vmaddr_from_segments(FILE* obj_file, off_t offset, boo
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t get_text_vmaddr_mach(FILE* obj_file, off_t offset, bool is_64, bool should_swap) {
|
static uintptr_t macho_get_text_vmaddr_mach(FILE* obj_file, off_t offset, bool is_64, bool should_swap) {
|
||||||
uint32_t ncmds;
|
uint32_t ncmds;
|
||||||
off_t load_commands_offset = offset;
|
off_t load_commands_offset = offset;
|
||||||
if(is_64) {
|
if(is_64) {
|
||||||
@ -107,10 +100,10 @@ static uintptr_t get_text_vmaddr_mach(FILE* obj_file, off_t offset, bool is_64,
|
|||||||
ncmds = header.ncmds;
|
ncmds = header.ncmds;
|
||||||
load_commands_offset += header_size;
|
load_commands_offset += header_size;
|
||||||
}
|
}
|
||||||
return get_text_vmaddr_from_segments(obj_file, load_commands_offset, should_swap, ncmds);
|
return macho_get_text_vmaddr_from_segments(obj_file, load_commands_offset, should_swap, ncmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t get_text_vmaddr_fat(FILE* obj_file, bool should_swap) {
|
static uintptr_t macho_get_text_vmaddr_fat(FILE* obj_file, bool should_swap) {
|
||||||
size_t header_size = sizeof(fat_header);
|
size_t header_size = sizeof(fat_header);
|
||||||
size_t arch_size = sizeof(fat_arch);
|
size_t arch_size = sizeof(fat_arch);
|
||||||
fat_header header = load_bytes<fat_header>(obj_file, 0);
|
fat_header header = load_bytes<fat_header>(obj_file, 0);
|
||||||
@ -127,7 +120,7 @@ static uintptr_t get_text_vmaddr_fat(FILE* obj_file, bool should_swap) {
|
|||||||
off_t mach_header_offset = (off_t)arch.offset;
|
off_t mach_header_offset = (off_t)arch.offset;
|
||||||
arch_offset += arch_size;
|
arch_offset += arch_size;
|
||||||
uint32_t magic = load_bytes<uint32_t>(obj_file, mach_header_offset);
|
uint32_t magic = load_bytes<uint32_t>(obj_file, mach_header_offset);
|
||||||
text_vmaddr = get_text_vmaddr_mach(
|
text_vmaddr = macho_get_text_vmaddr_mach(
|
||||||
obj_file,
|
obj_file,
|
||||||
mach_header_offset,
|
mach_header_offset,
|
||||||
is_magic_64(magic),
|
is_magic_64(magic),
|
||||||
@ -141,16 +134,16 @@ static uintptr_t get_text_vmaddr_fat(FILE* obj_file, bool should_swap) {
|
|||||||
return text_vmaddr;
|
return text_vmaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t get_text_vmaddr(const char* path) {
|
static uintptr_t macho_get_text_vmaddr(const char* path) {
|
||||||
FILE* obj_file = fopen(path, "rb");
|
FILE* obj_file = fopen(path, "rb");
|
||||||
uint32_t magic = load_bytes<uint32_t>(obj_file, 0);
|
uint32_t magic = load_bytes<uint32_t>(obj_file, 0);
|
||||||
bool is_64 = is_magic_64(magic);
|
bool is_64 = is_magic_64(magic);
|
||||||
bool should_swap = should_swap_bytes(magic);
|
bool should_swap = should_swap_bytes(magic);
|
||||||
uintptr_t addr;
|
uintptr_t addr;
|
||||||
if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
||||||
addr = get_text_vmaddr_fat(obj_file, should_swap);
|
addr = macho_get_text_vmaddr_fat(obj_file, should_swap);
|
||||||
} else {
|
} else {
|
||||||
addr = get_text_vmaddr_mach(obj_file, 0, is_64, should_swap);
|
addr = macho_get_text_vmaddr_mach(obj_file, 0, is_64, should_swap);
|
||||||
}
|
}
|
||||||
fclose(obj_file);
|
fclose(obj_file);
|
||||||
return addr;
|
return addr;
|
||||||
|
|||||||
65
src/platform/pe.hpp
Normal file
65
src/platform/pe.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef PE_HPP
|
||||||
|
#define PE_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#if IS_WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
static uintptr_t pe_get_module_image_base(const std::string& obj_path) {
|
||||||
|
// PE header values are little endian
|
||||||
|
bool do_swap = !is_little_endian();
|
||||||
|
FILE* file = fopen(obj_path.c_str(), "rb");
|
||||||
|
char magic[2];
|
||||||
|
internal_verify(fread(magic, 1, 2, file) == 2); // file + 0x0
|
||||||
|
internal_verify(memcmp(magic, "MZ", 2) == 0);
|
||||||
|
DWORD e_lfanew;
|
||||||
|
internal_verify(fseek(file, 0x3c, SEEK_SET) == 0);
|
||||||
|
internal_verify(fread(&e_lfanew, sizeof(DWORD), 1, file) == 1); // file + 0x3c
|
||||||
|
if(do_swap) e_lfanew = byteswap(e_lfanew);
|
||||||
|
long nt_header_offset = e_lfanew;
|
||||||
|
char signature[4];
|
||||||
|
internal_verify(fseek(file, nt_header_offset, SEEK_SET) == 0);
|
||||||
|
internal_verify(fread(signature, 1, 4, file) == 4); // NT header + 0x0
|
||||||
|
internal_verify(memcmp(signature, "PE\0\0", 4) == 0);
|
||||||
|
//WORD machine;
|
||||||
|
//internal_verify(fseek(file, nt_header_offset + 4, SEEK_SET) == 0); // file header + 0x0
|
||||||
|
//internal_verify(fread(&machine, sizeof(WORD), 1, file) == 1);
|
||||||
|
WORD size_of_optional_header;
|
||||||
|
internal_verify(fseek(file, nt_header_offset + 4 + 0x10, SEEK_SET) == 0); // file header + 0x10
|
||||||
|
internal_verify(fread(&size_of_optional_header, sizeof(DWORD), 1, file) == 1);
|
||||||
|
if(do_swap) size_of_optional_header = byteswap(size_of_optional_header);
|
||||||
|
internal_verify(size_of_optional_header != 0);
|
||||||
|
WORD optional_header_magic;
|
||||||
|
internal_verify(fseek(file, nt_header_offset + 0x18, SEEK_SET) == 0); // optional header + 0x0
|
||||||
|
internal_verify(fread(&optional_header_magic, sizeof(DWORD), 1, file) == 1);
|
||||||
|
if(do_swap) optional_header_magic = byteswap(optional_header_magic);
|
||||||
|
internal_verify(optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC);
|
||||||
|
uintptr_t image_base;
|
||||||
|
if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||||
|
// 32 bit
|
||||||
|
DWORD base;
|
||||||
|
internal_verify(fseek(file, nt_header_offset + 0x18 + 0x1c, SEEK_SET) == 0); // optional header + 0x1c
|
||||||
|
internal_verify(fread(&base, sizeof(DWORD), 1, file) == 1);
|
||||||
|
if(do_swap) base = byteswap(base);
|
||||||
|
image_base = base;
|
||||||
|
} else {
|
||||||
|
// 64 bit
|
||||||
|
// I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD
|
||||||
|
unsigned __int64 base;
|
||||||
|
internal_verify(fseek(file, nt_header_offset + 0x18 + 0x18, SEEK_SET) == 0); // optional header + 0x18
|
||||||
|
internal_verify(fread(&base, sizeof(unsigned __int64), 1, file) == 1);
|
||||||
|
if(do_swap) base = byteswap(base);
|
||||||
|
image_base = base;
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
return image_base;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -23,7 +23,7 @@
|
|||||||
#include "../platform/mach-o.hpp"
|
#include "../platform/mach-o.hpp"
|
||||||
#endif
|
#endif
|
||||||
#elif IS_WINDOWS
|
#elif IS_WINDOWS
|
||||||
#include <windows.h>
|
#include "../platform/pe.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
@ -156,7 +156,7 @@ namespace cpptrace {
|
|||||||
if(it == cache.end()) {
|
if(it == cache.end()) {
|
||||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
// 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
|
// have two threads try to do the same computation
|
||||||
auto base = get_text_vmaddr(entry.obj_path.c_str());
|
auto base = macho_get_text_vmaddr(entry.obj_path.c_str());
|
||||||
cache.insert(it, {entry.obj_path, base});
|
cache.insert(it, {entry.obj_path, base});
|
||||||
return base;
|
return base;
|
||||||
} else {
|
} else {
|
||||||
@ -242,56 +242,6 @@ namespace cpptrace {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t pe_get_module_image_base(const std::string& obj_path) {
|
|
||||||
// PE header values are little endian
|
|
||||||
bool do_swap = !is_little_endian();
|
|
||||||
FILE* file = fopen(obj_path.c_str(), "rb");
|
|
||||||
char magic[2];
|
|
||||||
internal_verify(fread(magic, 1, 2, file) == 2); // file + 0x0
|
|
||||||
internal_verify(memcmp(magic, "MZ", 2) == 0);
|
|
||||||
DWORD e_lfanew;
|
|
||||||
internal_verify(fseek(file, 0x3c, SEEK_SET) == 0);
|
|
||||||
internal_verify(fread(&e_lfanew, sizeof(DWORD), 1, file) == 1); // file + 0x3c
|
|
||||||
if(do_swap) e_lfanew = byteswap(e_lfanew);
|
|
||||||
long nt_header_offset = e_lfanew;
|
|
||||||
char signature[4];
|
|
||||||
internal_verify(fseek(file, nt_header_offset, SEEK_SET) == 0);
|
|
||||||
internal_verify(fread(signature, 1, 4, file) == 4); // NT header + 0x0
|
|
||||||
internal_verify(memcmp(signature, "PE\0\0", 4) == 0);
|
|
||||||
//WORD machine;
|
|
||||||
//internal_verify(fseek(file, nt_header_offset + 4, SEEK_SET) == 0); // file header + 0x0
|
|
||||||
//internal_verify(fread(&machine, sizeof(WORD), 1, file) == 1);
|
|
||||||
WORD size_of_optional_header;
|
|
||||||
internal_verify(fseek(file, nt_header_offset + 4 + 0x10, SEEK_SET) == 0); // file header + 0x10
|
|
||||||
internal_verify(fread(&size_of_optional_header, sizeof(DWORD), 1, file) == 1);
|
|
||||||
if(do_swap) size_of_optional_header = byteswap(size_of_optional_header);
|
|
||||||
internal_verify(size_of_optional_header != 0);
|
|
||||||
WORD optional_header_magic;
|
|
||||||
internal_verify(fseek(file, nt_header_offset + 0x18, SEEK_SET) == 0); // optional header + 0x0
|
|
||||||
internal_verify(fread(&optional_header_magic, sizeof(DWORD), 1, file) == 1);
|
|
||||||
if(do_swap) optional_header_magic = byteswap(optional_header_magic);
|
|
||||||
internal_verify(optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC);
|
|
||||||
uintptr_t image_base;
|
|
||||||
if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
||||||
// 32 bit
|
|
||||||
DWORD base;
|
|
||||||
internal_verify(fseek(file, nt_header_offset + 0x18 + 0x1c, SEEK_SET) == 0); // optional header + 0x1c
|
|
||||||
internal_verify(fread(&base, sizeof(DWORD), 1, file) == 1);
|
|
||||||
if(do_swap) base = byteswap(base);
|
|
||||||
image_base = base;
|
|
||||||
} else {
|
|
||||||
// 64 bit
|
|
||||||
// I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD
|
|
||||||
unsigned __int64 base;
|
|
||||||
internal_verify(fseek(file, nt_header_offset + 0x18 + 0x18, SEEK_SET) == 0); // optional header + 0x18
|
|
||||||
internal_verify(fread(&base, sizeof(unsigned __int64), 1, file) == 1);
|
|
||||||
if(do_swap) base = byteswap(base);
|
|
||||||
image_base = base;
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
return image_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t get_module_image_base(const dlframe &entry) {
|
uintptr_t get_module_image_base(const dlframe &entry) {
|
||||||
static std::mutex mutex;
|
static std::mutex mutex;
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user