1212
1313namespace cpptrace {
1414namespace detail {
15- template <typename T, typename std::enable_if<std::is_integral<T>::value, int >::type = 0 >
16- T elf_byteswap_if_needed (T value, bool elf_is_little) {
17- if (is_little_endian () == elf_is_little) {
18- return value;
19- } else {
20- return byteswap (value);
21- }
22- }
23-
24- template <std::size_t Bits>
25- static Result<std::uintptr_t , internal_error> elf_get_module_image_base_from_program_table (
15+ elf::elf (
16+ file_wrapper file,
2617 const std::string& object_path,
27- std::FILE* file,
28- bool is_little_endian
29- ) {
30- static_assert (Bits == 32 || Bits == 64 , " Unexpected Bits argument" );
31- using Header = typename std::conditional<Bits == 32 , Elf32_Ehdr, Elf64_Ehdr>::type;
32- using PHeader = typename std::conditional<Bits == 32 , Elf32_Phdr, Elf64_Phdr>::type;
33- auto loaded_header = load_bytes<Header>(file, 0 );
34- if (loaded_header.is_error ()) {
35- return std::move (loaded_header).unwrap_error ();
36- }
37- const Header& file_header = loaded_header.unwrap_value ();
38- if (file_header.e_ehsize != sizeof (Header)) {
39- return internal_error (" ELF file header size mismatch" + object_path);
40- }
41- // PT_PHDR will occur at most once
42- // Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
43- // It should occur at the beginning but may as well loop just in case
44- for (int i = 0 ; i < file_header.e_phnum ; i++) {
45- auto loaded_ph = load_bytes<PHeader>(file, file_header.e_phoff + file_header.e_phentsize * i);
46- if (loaded_ph.is_error ()) {
47- return std::move (loaded_ph).unwrap_error ();
48- }
49- const PHeader& program_header = loaded_ph.unwrap_value ();
50- if (elf_byteswap_if_needed (program_header.p_type , is_little_endian) == PT_PHDR) {
51- return elf_byteswap_if_needed (program_header.p_vaddr , is_little_endian) -
52- elf_byteswap_if_needed (program_header.p_offset , is_little_endian);
53- }
54- }
55- // Apparently some objects like shared objects can end up missing this header. 0 as a base seems correct.
56- return 0 ;
57- }
18+ bool is_little_endian,
19+ bool is_64
20+ ) : file(std::move(file)), object_path(object_path), is_little_endian(is_little_endian), is_64(is_64) {}
5821
59- Result<std:: uintptr_t , internal_error> elf_get_module_image_base (const std::string& object_path) {
22+ Result<elf , internal_error> elf::open_elf (const std::string& object_path) {
6023 auto file = raii_wrap (std::fopen (object_path.c_str (), " rb" ), file_deleter);
6124 if (file == nullptr ) {
62- return internal_error (" Unable to read object file " + object_path);
25+ return internal_error (" Unable to read object file {} " , object_path);
6326 }
6427 // Initial checks/metadata
6528 auto magic = load_bytes<std::array<char , 4 >>(file, 0 );
@@ -86,14 +49,57 @@ namespace detail {
8649 if (ei_version.unwrap_value () != 1 ) {
8750 return internal_error (" Unexpected ELF version " + object_path);
8851 }
52+ return elf (std::move (file), object_path, is_little_endian, is_64);
53+ }
54+
55+ Result<std::uintptr_t , internal_error> elf::get_module_image_base () {
8956 // get image base
9057 if (is_64) {
91- return elf_get_module_image_base_from_program_table <64 >(object_path, file, is_little_endian );
58+ return get_module_image_base_from_program_table <64 >();
9259 } else {
93- return elf_get_module_image_base_from_program_table <32 >(object_path, file, is_little_endian );
60+ return get_module_image_base_from_program_table <32 >();
9461 }
9562 }
9663
64+ template <typename T, typename std::enable_if<std::is_integral<T>::value, int >::type>
65+ T elf::byteswap_if_needed (T value, bool elf_is_little) {
66+ if (cpptrace::detail::is_little_endian () == elf_is_little) {
67+ return value;
68+ } else {
69+ return byteswap (value);
70+ }
71+ }
72+
73+ template <std::size_t Bits>
74+ Result<std::uintptr_t , internal_error> elf::get_module_image_base_from_program_table () {
75+ static_assert (Bits == 32 || Bits == 64 , " Unexpected Bits argument" );
76+ using Header = typename std::conditional<Bits == 32 , Elf32_Ehdr, Elf64_Ehdr>::type;
77+ using PHeader = typename std::conditional<Bits == 32 , Elf32_Phdr, Elf64_Phdr>::type;
78+ auto loaded_header = load_bytes<Header>(file, 0 );
79+ if (loaded_header.is_error ()) {
80+ return std::move (loaded_header).unwrap_error ();
81+ }
82+ const Header& file_header = loaded_header.unwrap_value ();
83+ if (file_header.e_ehsize != sizeof (Header)) {
84+ return internal_error (" ELF file header size mismatch" + object_path);
85+ }
86+ // PT_PHDR will occur at most once
87+ // Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
88+ // It should occur at the beginning but may as well loop just in case
89+ for (int i = 0 ; i < file_header.e_phnum ; i++) {
90+ auto loaded_ph = load_bytes<PHeader>(file, file_header.e_phoff + file_header.e_phentsize * i);
91+ if (loaded_ph.is_error ()) {
92+ return std::move (loaded_ph).unwrap_error ();
93+ }
94+ const PHeader& program_header = loaded_ph.unwrap_value ();
95+ if (byteswap_if_needed (program_header.p_type , is_little_endian) == PT_PHDR) {
96+ return byteswap_if_needed (program_header.p_vaddr , is_little_endian) -
97+ byteswap_if_needed (program_header.p_offset , is_little_endian);
98+ }
99+ }
100+ // Apparently some objects like shared objects can end up missing this header. 0 as a base seems correct.
101+ return 0 ;
102+ }
97103}
98104}
99105
0 commit comments