1
1
#include " binary/elf.hpp"
2
+
3
+ #include " utils/error.hpp"
4
+ #include " utils/io/base_file.hpp"
5
+ #include " utils/io/memory_file_view.hpp"
2
6
#include " utils/optional.hpp"
7
+ #include " utils/io/file.hpp"
8
+ #include " utils/string_view.hpp"
3
9
4
10
#if IS_LINUX
5
11
16
22
namespace cpptrace {
17
23
namespace detail {
18
24
elf::elf (
19
- file_wrapper file,
20
- cstring_view object_path,
25
+ std::unique_ptr<base_file> file,
21
26
bool is_little_endian,
22
27
bool is_64
23
- ) : file(std::move(file)), object_path(object_path), is_little_endian(is_little_endian), is_64(is_64) {}
28
+ ) : file(std::move(file)), is_little_endian(is_little_endian), is_64(is_64) {}
24
29
25
- Result<elf, internal_error> elf::open_elf (cstring_view object_path) {
26
- auto file = raii_wrap (std::fopen (object_path.c_str (), " rb" ), file_deleter);
27
- if (file == nullptr ) {
28
- return internal_error (" Unable to read object file {}" , object_path);
29
- }
30
+ Result<elf, internal_error> elf::open (std::unique_ptr<base_file> file) {
30
31
// Initial checks/metadata
31
- auto magic = load_bytes <std::array<char , 4 >>(file, 0 );
32
+ auto magic = file-> read <std::array<char , 4 >>(0 );
32
33
if (magic.is_error ()) {
33
34
return std::move (magic).unwrap_error ();
34
35
}
35
36
if (magic.unwrap_value () != (std::array<char , 4 >{0x7F , ' E' , ' L' , ' F' })) {
36
- return internal_error (" File is not ELF {}" , object_path );
37
+ return internal_error (" File is not ELF {}" , file-> path () );
37
38
}
38
- auto ei_class = load_bytes <std::uint8_t >(file, 4 );
39
+ auto ei_class = file-> read <std::uint8_t >(4 );
39
40
if (ei_class.is_error ()) {
40
41
return std::move (ei_class).unwrap_error ();
41
42
}
42
43
bool is_64 = ei_class.unwrap_value () == 2 ;
43
- auto ei_data = load_bytes <std::uint8_t >(file, 5 );
44
+ auto ei_data = file-> read <std::uint8_t >(5 );
44
45
if (ei_data.is_error ()) {
45
46
return std::move (ei_data).unwrap_error ();
46
47
}
47
48
bool is_little_endian = ei_data.unwrap_value () == 1 ;
48
- auto ei_version = load_bytes <std::uint8_t >(file, 6 );
49
+ auto ei_version = file-> read <std::uint8_t >(6 );
49
50
if (ei_version.is_error ()) {
50
51
return std::move (ei_version).unwrap_error ();
51
52
}
52
53
if (ei_version.unwrap_value () != 1 ) {
53
- return internal_error (" Unexpected ELF version {}" , object_path );
54
+ return internal_error (" Unexpected ELF version {}" , file-> path () );
54
55
}
55
- return elf (std::move (file), object_path, is_little_endian, is_64);
56
+ return elf (std::move (file), is_little_endian, is_64);
57
+ }
58
+
59
+ Result<elf, internal_error> elf::open (cstring_view object_path) {
60
+ auto file_res = file::open (object_path);
61
+ if (!file_res) {
62
+ return internal_error (" Unable to read object file {}" , object_path);
63
+ }
64
+ auto & file = file_res.unwrap_value ();
65
+ return open (make_unique (std::move (file)));
66
+ }
67
+
68
+ Result<elf, internal_error> elf::open (cbspan object) {
69
+ return open (make_unique<memory_file_view>(object));
56
70
}
57
71
58
72
Result<std::uintptr_t , internal_error> elf::get_module_image_base () {
@@ -77,7 +91,7 @@ namespace detail {
77
91
// Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
78
92
// It should occur at the beginning but may as well loop just in case
79
93
for (unsigned i = 0 ; i < header_info.e_phnum ; i++) {
80
- auto loaded_ph = load_bytes <PHeader>(file, header_info.e_phoff + header_info.e_phentsize * i);
94
+ auto loaded_ph = file-> read <PHeader>(header_info.e_phoff + header_info.e_phentsize * i);
81
95
if (loaded_ph.is_error ()) {
82
96
return std::move (loaded_ph).unwrap_error ();
83
97
}
@@ -135,6 +149,31 @@ namespace detail {
135
149
return nullopt;
136
150
}
137
151
152
+ Result<std::vector<elf::pc_range>, internal_error> elf::get_pc_ranges () {
153
+ std::vector<pc_range> vec;
154
+ auto header_info_ = get_header_info ();
155
+ if (header_info_.is_error ()) {
156
+ return header_info_.unwrap_error ();
157
+ }
158
+ auto & header_info = header_info_.unwrap_value ();
159
+ auto strtab_ = get_strtab (header_info.e_shstrndx );
160
+ if (strtab_.is_error ()) {
161
+ return strtab_.unwrap_error ();
162
+ }
163
+ auto & strtab = strtab_.unwrap_value ();
164
+ auto sections_res = get_sections ();
165
+ if (!sections_res) {
166
+ return sections_res.unwrap_error ();
167
+ }
168
+ const auto & sections = sections_res.unwrap_value ();
169
+ for (const auto & section : sections) {
170
+ if (string_view (strtab.data () + section.sh_name ) == " .text" ) {
171
+ vec.push_back (pc_range{section.sh_addr , section.sh_addr + section.sh_size });
172
+ }
173
+ }
174
+ return vec;
175
+ }
176
+
138
177
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::get_symtab_entries () {
139
178
return resolve_symtab_entries (get_symtab ());
140
179
}
@@ -187,7 +226,7 @@ namespace detail {
187
226
return std::ref (header.unwrap ());
188
227
}
189
228
if (tried_to_load_header) {
190
- return internal_error (" previous header load failed " + object_path );
229
+ return internal_error (" previous header load failed {} " , file-> path () );
191
230
}
192
231
tried_to_load_header = true ;
193
232
if (is_64) {
@@ -201,13 +240,13 @@ namespace detail {
201
240
Result<const elf::header_info&, internal_error> elf::get_header_info_impl () {
202
241
static_assert (Bits == 32 || Bits == 64 , " Unexpected Bits argument" );
203
242
using Header = typename std::conditional<Bits == 32 , Elf32_Ehdr, Elf64_Ehdr>::type;
204
- auto loaded_header = load_bytes <Header>(file, 0 );
243
+ auto loaded_header = file-> read <Header>(0 );
205
244
if (loaded_header.is_error ()) {
206
245
return std::move (loaded_header).unwrap_error ();
207
246
}
208
247
const Header& file_header = loaded_header.unwrap_value ();
209
248
if (file_header.e_ehsize != sizeof (Header)) {
210
- return internal_error (" ELF file header size mismatch" + object_path );
249
+ return internal_error (" ELF file header size mismatch {} " , file-> path () );
211
250
}
212
251
header_info info;
213
252
info.e_phoff = byteswap_if_needed (file_header.e_phoff );
@@ -216,6 +255,7 @@ namespace detail {
216
255
info.e_shoff = byteswap_if_needed (file_header.e_shoff );
217
256
info.e_shnum = byteswap_if_needed (file_header.e_shnum );
218
257
info.e_shentsize = byteswap_if_needed (file_header.e_shentsize );
258
+ info.e_shstrndx = byteswap_if_needed (file_header.e_shstrndx );
219
259
header = info;
220
260
return header.unwrap ();
221
261
}
@@ -225,7 +265,7 @@ namespace detail {
225
265
return sections;
226
266
}
227
267
if (tried_to_load_sections) {
228
- return internal_error (" previous sections load failed " + object_path );
268
+ return internal_error (" previous sections load failed {} " , file-> path () );
229
269
}
230
270
tried_to_load_sections = true ;
231
271
if (is_64) {
@@ -245,12 +285,13 @@ namespace detail {
245
285
}
246
286
const auto & header_info = header.unwrap_value ();
247
287
for (unsigned i = 0 ; i < header_info.e_shnum ; i++) {
248
- auto loaded_sh = load_bytes <SHeader>(file, header_info.e_shoff + header_info.e_shentsize * i);
288
+ auto loaded_sh = file-> read <SHeader>(header_info.e_shoff + header_info.e_shentsize * i);
249
289
if (loaded_sh.is_error ()) {
250
290
return std::move (loaded_sh).unwrap_error ();
251
291
}
252
292
const SHeader& section_header = loaded_sh.unwrap_value ();
253
293
section_info info;
294
+ info.sh_name = byteswap_if_needed (section_header.sh_name );
254
295
info.sh_type = byteswap_if_needed (section_header.sh_type );
255
296
info.sh_addr = byteswap_if_needed (section_header.sh_addr );
256
297
info.sh_offset = byteswap_if_needed (section_header.sh_offset );
@@ -273,7 +314,7 @@ namespace detail {
273
314
return entry.data ;
274
315
}
275
316
if (entry.tried_to_load_strtab ) {
276
- return internal_error (" previous strtab load failed {}" , object_path );
317
+ return internal_error (" previous strtab load failed {}" , file-> path () );
277
318
}
278
319
}
279
320
entry.tried_to_load_strtab = true ;
@@ -287,14 +328,12 @@ namespace detail {
287
328
}
288
329
const auto & section = sections[index];
289
330
if (section.sh_type != SHT_STRTAB) {
290
- return internal_error (" requested strtab section not a strtab (requested {} of {})" , index, object_path );
331
+ return internal_error (" requested strtab section not a strtab (requested {} of {})" , index, file-> path () );
291
332
}
292
333
entry.data .resize (section.sh_size + 1 );
293
- if (std::fseek (file, section.sh_offset , SEEK_SET) != 0 ) {
294
- return internal_error (" fseek error while loading elf string table" );
295
- }
296
- if (std::fread (entry.data .data (), sizeof (char ), section.sh_size , file) != section.sh_size ) {
297
- return internal_error (" fread error while loading elf string table" );
334
+ auto read_res = file->read_bytes (span<char >{entry.data .data (), section.sh_size }, section.sh_offset );
335
+ if (!read_res) {
336
+ return read_res.unwrap_error ();
298
337
}
299
338
entry.data [section.sh_size ] = 0 ; // just out of an abundance of caution
300
339
entry.did_load_strtab = true ;
@@ -306,7 +345,7 @@ namespace detail {
306
345
return symtab;
307
346
}
308
347
if (tried_to_load_symtab) {
309
- return internal_error (" previous symtab load failed {}" , object_path );
348
+ return internal_error (" previous symtab load failed {}" , file-> path () );
310
349
}
311
350
tried_to_load_symtab = true ;
312
351
if (is_64) {
@@ -335,7 +374,7 @@ namespace detail {
335
374
return dynamic_symtab;
336
375
}
337
376
if (tried_to_load_dynamic_symtab) {
338
- return internal_error (" previous dynamic symtab load failed {}" , object_path );
377
+ return internal_error (" previous dynamic symtab load failed {}" , file-> path () );
339
378
}
340
379
tried_to_load_dynamic_symtab = true ;
341
380
if (is_64) {
@@ -375,17 +414,15 @@ namespace detail {
375
414
for (const auto & section : sections) {
376
415
if (section.sh_type == (dynamic ? SHT_DYNSYM : SHT_SYMTAB)) {
377
416
if (section.sh_entsize != sizeof (SymEntry)) {
378
- return internal_error (" elf seems corrupted, sym entry mismatch {}" , object_path );
417
+ return internal_error (" elf seems corrupted, sym entry mismatch {}" , file-> path () );
379
418
}
380
419
if (section.sh_size % section.sh_entsize != 0 ) {
381
- return internal_error (" elf seems corrupted, sym entry vs section size mismatch {}" , object_path );
420
+ return internal_error (" elf seems corrupted, sym entry vs section size mismatch {}" , file-> path () );
382
421
}
383
422
std::vector<SymEntry> buffer (section.sh_size / section.sh_entsize );
384
- if (std::fseek (file, section.sh_offset , SEEK_SET) != 0 ) {
385
- return internal_error (" fseek error while loading elf symbol table" );
386
- }
387
- if (std::fread (buffer.data (), section.sh_entsize , buffer.size (), file) != buffer.size ()) {
388
- return internal_error (" fread error while loading elf symbol table" );
423
+ auto res = file->read_span (make_span (buffer.begin (), buffer.end ()), section.sh_offset );
424
+ if (!res) {
425
+ return res.unwrap_error ();
389
426
}
390
427
symbol_table = symtab_info{};
391
428
symbol_table.unwrap ().entries .reserve (buffer.size ());
@@ -414,8 +451,11 @@ namespace detail {
414
451
}
415
452
416
453
Result<maybe_owned<elf>, internal_error> open_elf_cached (const std::string& object_path) {
454
+ if (object_path.empty ()) {
455
+ return internal_error{" empty object_path" };
456
+ }
417
457
if (get_cache_mode () == cache_mode::prioritize_memory) {
418
- return elf::open_elf (object_path)
458
+ return elf::open (object_path)
419
459
.transform ([](elf&& obj) { return maybe_owned<elf>{detail::make_unique<elf>(std::move (obj))}; });
420
460
} else {
421
461
std::mutex m;
@@ -424,7 +464,7 @@ namespace detail {
424
464
static std::unordered_map<std::string, Result<elf, internal_error>> cache;
425
465
auto it = cache.find (object_path);
426
466
if (it == cache.end ()) {
427
- auto res = cache.emplace (object_path, elf::open_elf (object_path));
467
+ auto res = cache.emplace (object_path, elf::open (object_path));
428
468
VERIFY (res.second );
429
469
it = res.first ;
430
470
}
0 commit comments