1 | #ifndef ELF_HPP |
---|
2 | #define ELF_HPP |
---|
3 | |
---|
4 | #include "common.hpp" |
---|
5 | |
---|
6 | #if IS_LINUX |
---|
7 | #include <array> |
---|
8 | #include <cstdint> |
---|
9 | #include <cstdio> |
---|
10 | #include <cstring> |
---|
11 | |
---|
12 | #include <elf.h> |
---|
13 | |
---|
14 | template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> |
---|
15 | T elf_byteswap_if_needed(T value, bool elf_is_little) { |
---|
16 | if(is_little_endian() == elf_is_little) { |
---|
17 | return value; |
---|
18 | } else { |
---|
19 | return byteswap(value); |
---|
20 | } |
---|
21 | } |
---|
22 | |
---|
23 | // TODO: Address code duplication here. Do we actually have to care about 32-bit if the library is compiled as 64-bit? |
---|
24 | // I think probably not... |
---|
25 | |
---|
26 | // TODO: Re-evaluate use of off_t |
---|
27 | // I think we can rely on PT_PHDR https://stackoverflow.com/q/61568612/15675011... |
---|
28 | static uintptr_t elf_get_module_image_base_from_program_table( |
---|
29 | FILE* file, |
---|
30 | bool is_64, |
---|
31 | bool is_little_endian, |
---|
32 | off_t e_phoff, |
---|
33 | off_t e_phentsize, |
---|
34 | int e_phnum |
---|
35 | ) { |
---|
36 | for(int i = 0; i < e_phnum; i++) { |
---|
37 | if(is_64) { |
---|
38 | Elf64_Phdr program_header = load_bytes<Elf64_Phdr>(file, e_phoff + e_phentsize * i); |
---|
39 | if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) { |
---|
40 | return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian) |
---|
41 | - elf_byteswap_if_needed(program_header.p_offset, is_little_endian); |
---|
42 | } |
---|
43 | } else { |
---|
44 | Elf32_Phdr program_header = load_bytes<Elf32_Phdr>(file, e_phoff + e_phentsize * i); |
---|
45 | if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) { |
---|
46 | return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian) |
---|
47 | - elf_byteswap_if_needed(program_header.p_offset, is_little_endian); |
---|
48 | } |
---|
49 | } |
---|
50 | } |
---|
51 | return 0; |
---|
52 | } |
---|
53 | |
---|
54 | static uintptr_t elf_get_module_image_base(const std::string& obj_path) { |
---|
55 | FILE* file = fopen(obj_path.c_str(), "rb"); |
---|
56 | if(file == nullptr) { |
---|
57 | throw file_error(); |
---|
58 | } |
---|
59 | // Initial checks/metadata |
---|
60 | auto magic = load_bytes<std::array<char, 4>>(file, 0); |
---|
61 | internal_verify(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'})); |
---|
62 | bool is_64 = load_bytes<uint8_t>(file, 4) == 2; |
---|
63 | bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1; |
---|
64 | internal_verify(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF version"); |
---|
65 | // |
---|
66 | if(is_64) { |
---|
67 | Elf64_Ehdr file_header = load_bytes<Elf64_Ehdr>(file, 0); |
---|
68 | internal_verify(file_header.e_ehsize == sizeof(Elf64_Ehdr)); |
---|
69 | return elf_get_module_image_base_from_program_table( |
---|
70 | file, |
---|
71 | is_64, |
---|
72 | is_little_endian, |
---|
73 | elf_byteswap_if_needed(file_header.e_phoff, is_little_endian), |
---|
74 | elf_byteswap_if_needed(file_header.e_phentsize, is_little_endian), |
---|
75 | elf_byteswap_if_needed(file_header.e_phnum, is_little_endian) |
---|
76 | ); |
---|
77 | } else { |
---|
78 | Elf32_Ehdr file_header = load_bytes<Elf32_Ehdr>(file, 0); |
---|
79 | internal_verify(file_header.e_ehsize == sizeof(Elf32_Ehdr)); |
---|
80 | return elf_get_module_image_base_from_program_table( |
---|
81 | file, |
---|
82 | is_64, |
---|
83 | is_little_endian, |
---|
84 | elf_byteswap_if_needed(file_header.e_phoff, is_little_endian), |
---|
85 | elf_byteswap_if_needed(file_header.e_phentsize, is_little_endian), |
---|
86 | elf_byteswap_if_needed(file_header.e_phnum, is_little_endian) |
---|
87 | ); |
---|
88 | } |
---|
89 | } |
---|
90 | |
---|
91 | #endif |
---|
92 | |
---|
93 | #endif |
---|