-
Notifications
You must be signed in to change notification settings - Fork 389
Expand file tree
/
Copy pathelf.cpp
More file actions
507 lines (463 loc) · 15 KB
/
elf.cpp
File metadata and controls
507 lines (463 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <kernel/elf.hpp>
#include <util/crc32.hpp>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <unistd.h>
#include <vector>
#include <elf.h>
#include <os.hpp>
#include <arch.hpp>
#include <likely>
#include <info>
#include <stdint.h>
#if UINTPTR_MAX == 0xffffffffffffffff
typedef Elf64_Sym ElfSym;
typedef Elf64_Ehdr ElfEhdr;
typedef Elf64_Phdr ElfPhdr;
typedef Elf64_Shdr ElfShdr;
typedef Elf64_Addr ElfAddr;
#elif UINTPTR_MAX == 0xffffffff
typedef Elf32_Sym ElfSym;
typedef Elf32_Ehdr ElfEhdr;
typedef Elf32_Phdr ElfPhdr;
typedef Elf32_Shdr ElfShdr;
typedef Elf32_Addr ElfAddr;
#else
#error "Unknown data model"
#endif
static const char* boot_stringz = "Bootloader area";
extern "C" char _ELF_START_;
static const uintptr_t ELF_START = reinterpret_cast<uintptr_t>(&_ELF_START_);
/* Calls __builtin_frame_address to determine if the top of the stack has been reached,
* then calls __builtin_return_address to get the return address at level N. Note that
* __builtin_frame_address can crash the program if frame address is not set up correctly.
*/
#define frp(N, ra) \
(__builtin_frame_address(N) != nullptr) && \
(ra = __builtin_return_address(N)) != nullptr && ra != (void*)-1
extern "C" char *
__cxa_demangle(const char *name, char *buf, size_t *n, int *status);
template <typename N>
static std::string to_hex_string(N n)
{
char buffer[16];
int len = snprintf(buffer, sizeof(buffer), "%#x", n);
return std::string(buffer, len);
}
static ElfEhdr& elf_header() {
return *(ElfEhdr*) ELF_START;
}
struct SymTab {
const ElfSym* base;
uint32_t entries;
};
struct StrTab {
const char* base;
uint32_t size;
};
class ElfTables
{
public:
ElfTables() {}
void set(const ElfSym* syms,
uint32_t entries,
const char* strs,
uint32_t strsize,
uint32_t csum_syms,
uint32_t csum_strs)
{
/*
auto* symbase = new ElfSym[entries];
std::copy(syms, syms + entries, symbase);
char* strbase = new char[strsize];
std::copy(string_table, string_table + strsize, strbase);
*/
this->symtab = {syms, entries};
this->strtab = {strs, strsize};
this->checksum_syms = csum_syms;
this->checksum_strs = csum_strs;
}
safe_func_offset getsym_safe(ElfAddr addr, char* buffer, size_t length)
{
// inside bootloader area
if (UNLIKELY(addr >= 0x7c00 && addr < 0x7e00))
return {boot_stringz, 0x7c00, (uint32_t) addr - 0x7c00};
// find symbol name for all addresses above first page
// which are treated as possible null pointers
if (LIKELY(addr > 0x1000))
{
// resolve manually from symtab
const auto* sym = getaddr(addr);
if (LIKELY(sym)) {
auto base = sym->st_value;
uint32_t offset = (uint32_t) (addr - base);
// return string name for symbol
return {demangle_safe( sym_name(sym), buffer, length ), static_cast<uintptr_t>(base), offset};
}
}
else if (addr == 0x0) {
return {"0x0 (null)", static_cast<uintptr_t>(addr), 0};
}
// function or space not found
snprintf(buffer, length, "%p", (void*) addr);
return {buffer, static_cast<uintptr_t>(addr), 0};
}
const ElfSym* getaddr(ElfAddr addr)
{
// find exact match
for (int i = 0; i < (int) symtab.entries; i++)
{
if (addr >= symtab.base[i].st_value
&& (addr < symtab.base[i].st_value + symtab.base[i].st_size)) {
//printf("found sym (%p) at %d\n", (void*) addr, i);
return &symtab.base[i];
}
}
// try again, but use closest match
const ElfSym* guess = nullptr;
uintptr_t gdiff = 512;
for (size_t i = 0; i < symtab.entries; i++)
{
if (addr >= symtab.base[i].st_value
&& (addr < symtab.base[i].st_value + 512))
{
auto diff = addr - symtab.base[i].st_value;
if (diff < gdiff) {
gdiff = diff;
guess = &symtab.base[i];
}
}
}
return guess;
}
size_t end_of_file() const {
auto& hdr = elf_header();
return hdr.e_ehsize + (hdr.e_phnum * hdr.e_phentsize) + (hdr.e_shnum * hdr.e_shentsize);
}
const SymTab& get_symtab() const {
return symtab;
}
const auto* get_strtab() const {
return strtab.base;
}
bool verify_symbols() const {
//printf("ELF verification from %p to %p (Syms=%#x, Strs=%#x)\n",
// symtab.base, strtab.base + strtab.size, checksum_syms, checksum_strs);
uint32_t csum =
crc32_fast(symtab.base, symtab.entries * sizeof(ElfSym));
if (csum != checksum_syms) {
printf("ELF symbol tables checksum failed! "
"(%08x vs %08x)\n", csum, checksum_syms);
return false;
}
csum = crc32_fast(strtab.base, strtab.size);
if (csum != checksum_strs) {
printf("ELF string tables checksum failed! "
"(%08x vs %08x)\n", csum, checksum_strs);
return false;
}
return true;
}
private:
const char* sym_name(const ElfSym* sym) const {
return &strtab.base[sym->st_name];
}
const char* demangle_safe(const char* name, char* buffer, size_t buflen) const
{
int status;
// internally, demangle just returns buf when status is ok
auto* res = __cxa_demangle(name, buffer, &buflen, &status);
if (status == 0) return res;
return name;
}
SymTab symtab;
StrTab strtab;
/* NOTE: DON'T INITIALIZE */
uint32_t checksum_syms;
uint32_t checksum_strs;
/* NOTE: DON'T INITIALIZE */
friend void elf_protect_symbol_areas();
};
static ElfTables parser;
ElfTables& get_parser() {
return parser;
}
size_t Elf::end_of_file() {
return get_parser().end_of_file();
}
const char* Elf::get_strtab() {
return get_parser().get_strtab();
}
uintptr_t Elf::resolve_addr(uintptr_t addr)
{
auto* sym = get_parser().getaddr(addr);
if (sym) return sym->st_value;
return addr;
}
uintptr_t Elf::resolve_addr(void* addr)
{
auto* sym = get_parser().getaddr((uintptr_t) addr);
if (sym) return sym->st_value;
return (uintptr_t) addr;
}
safe_func_offset Elf::safe_resolve_symbol(void* addr, char* buffer, size_t length)
{
return get_parser().getsym_safe((ElfAddr) addr, buffer, length);
}
bool Elf::verify_symbols()
{
return get_parser().verify_symbols();
}
void os::print_backtrace(void(*stdout_function)(const char*, size_t)) noexcept
{
char _symbol_buffer[8192];
char _btrace_buffer[8192];
if (Elf::get_strtab() == NULL) {
int len = snprintf(_btrace_buffer, sizeof(_btrace_buffer),
"symtab or strtab is empty, indicating image may be stripped\n");
write(1, _btrace_buffer, len);
}
#if UINTPTR_MAX == 0xffffffff
#define PRINT_TRACE(N, ra) \
auto symb = Elf::safe_resolve_symbol( \
ra, _symbol_buffer, sizeof(_symbol_buffer)); \
int len = snprintf(_btrace_buffer, sizeof(_btrace_buffer),\
"[%d] 0x%08x + 0x%.3x: %s\n", \
N, symb.addr, symb.offset, symb.name);\
stdout_function(_btrace_buffer, len);
#elif UINTPTR_MAX == 0xffffffffffffffff
#define PRINT_TRACE(N, ra) \
auto symb = Elf::safe_resolve_symbol( \
ra, _symbol_buffer, sizeof(_symbol_buffer)); \
int len = snprintf(_btrace_buffer, sizeof(_btrace_buffer),\
"[%d] 0x%016lx + 0x%.3x: %s\n", \
N, symb.addr, symb.offset, symb.name);\
stdout_function(_btrace_buffer, len);
#else
#error "Implement me"
#endif
void* ra;
if (frp(0, ra)) {
PRINT_TRACE(0, ra);
if (frp(1, ra)) {
PRINT_TRACE(1, ra);
if (frp(2, ra)) {
PRINT_TRACE(2, ra);
if (frp(3, ra)) {
PRINT_TRACE(3, ra);
if (frp(4, ra)) {
PRINT_TRACE(4, ra);
if (frp(5, ra)) {
PRINT_TRACE(5, ra);
if (frp(6, ra)) {
PRINT_TRACE(6, ra);
if (frp(7, ra)) {
PRINT_TRACE(7, ra);
if (frp(8, ra)) {
PRINT_TRACE(8, ra);
if (frp(9, ra)) {
PRINT_TRACE(9, ra);
if (frp(10, ra)) {
PRINT_TRACE(10, ra);
if (frp(11, ra)) {
PRINT_TRACE(11, ra);
if (frp(12, ra)) {
PRINT_TRACE(12, ra);
if (frp(13, ra)) {
PRINT_TRACE(13, ra);
if (frp(14, ra)) {
PRINT_TRACE(14, ra);
}}}}}}}}}}}}}}}
}
void os::print_backtrace() noexcept
{
print_backtrace([] (const char* text, size_t length) {
write(1, text, length);
});
}
#include <kprint>
extern "C"
void _print_elf_symbols()
{
const auto& symtab = parser.get_symtab();
const char* strtab = parser.get_strtab();
for (size_t i = 0; i < symtab.entries; i++)
{
kprintf("%16p: %s\n", (void*) symtab.base[i].st_value, &strtab[symtab.base[i].st_name]);
}
kprintf("*** %u entries\n", symtab.entries);
}
void Elf::print_info()
{
_print_elf_symbols();
}
/* This static struct stores the ELF symbol table details before we init BSS.
* To avoid it partially being cleared again when we initialise BSS to zero, we
* store it in a different ELF section with __attribute__. */
static struct relocated_header {
ElfSym* syms = (ElfSym*) 0x0;
uint32_t entries = 0xFFFF;
uint32_t strsize = 0xFFFF;
uint32_t check_syms = 0xFFFF;
uint32_t check_strs = 0xFFFF;
const char* strings() const {
return (char*) &syms[entries];
}
} relocs __attribute__((section(".data")));
struct elfsyms_header {
uint32_t symtab_entries;
uint32_t strtab_size;
uint32_t sanity_check;
uint32_t checksum_syms;
uint32_t checksum_strs;
ElfSym syms[0];
} __attribute__((packed));
extern "C"
int _get_elf_section_datasize(const void* location)
{
auto& hdr = *(elfsyms_header*) location;
// special case for missing call to elf_syms
if (hdr.symtab_entries == 0) return 0;
return hdr.symtab_entries * sizeof(ElfSym) + hdr.strtab_size;
}
extern "C"
void _move_elf_syms_location(const void* location, void* new_location)
{
int size = _get_elf_section_datasize(location);
// stripped variant
if (size == 0) {
relocs.entries = 0;
relocs.strsize = 0;
return;
}
// incoming header
auto* hdr = (elfsyms_header*) location;
// verify CRC sanity check
const uint32_t temp_hdr = hdr->sanity_check;
hdr->sanity_check = 0;
const uint32_t our_sanity = crc32c(hdr, sizeof(elfsyms_header));
hdr->sanity_check = temp_hdr;
if (hdr->sanity_check != our_sanity)
{
kprintf("* ELF syms header CRC failed! "
"(%08x vs %08x)\n", hdr->sanity_check, our_sanity);
relocs.entries = 0;
relocs.strsize = 0;
return;
} else {
kprintf("* ELF syms header CRC OK\n");
}
// verify separate checksums of symbols and strings
uint32_t symbsize = hdr->symtab_entries * sizeof(ElfSym);
uint32_t csum_syms = crc32c(hdr->syms, symbsize);
uint32_t csum_strs = crc32c(&hdr->syms[hdr->symtab_entries], hdr->strtab_size);
if (csum_syms != hdr->checksum_syms || csum_strs != hdr->checksum_strs)
{
if (csum_syms != hdr->checksum_syms)
kprintf(" * ELF symbol tables checksum failed! "
"(%08x vs %08x)\n", csum_syms, hdr->checksum_syms);
if (csum_strs != hdr->checksum_strs)
kprintf(" * ELF string tables checksum failed! "
"(%08x vs %08x)\n", csum_strs, hdr->checksum_strs);
uint32_t all = crc32c(hdr, sizeof(elfsyms_header) + size);
kprintf(" * Checksum ELF section: %08x\n", all);
relocs.entries = 0;
relocs.strsize = 0;
return;
}
// update header
relocs.syms = (ElfSym*) new_location;
relocs.entries = hdr->symtab_entries;
relocs.strsize = hdr->strtab_size;
relocs.check_syms = hdr->checksum_syms;
relocs.check_strs = hdr->checksum_strs;
// copy **overlapping** symbol and string data
memmove((char*) relocs.syms, (char*) hdr->syms, size);
}
extern "C"
void _init_elf_parser()
{
if (relocs.entries) {
// apply changes to the symbol parser from custom location
parser.set(relocs.syms, relocs.entries,
relocs.strings(), relocs.strsize,
relocs.check_syms, relocs.check_strs);
}
else {
// symbols and strings are stripped out
parser.set(nullptr, 0, nullptr, 0, 0, 0);
}
}
extern "C"
void elf_check_symbols_ok()
{
extern char _ELF_SYM_START_;
auto* hdr = (elfsyms_header*) &_ELF_SYM_START_;
// verify CRC sanity check
const uint32_t temp_hdr = hdr->sanity_check;
hdr->sanity_check = 0;
const uint32_t our_sanity = crc32c(hdr, sizeof(elfsyms_header));
hdr->sanity_check = temp_hdr;
if (hdr->sanity_check != our_sanity)
{
kprintf("ELF syms header CRC failed! "
"(%08x vs %08x)\n", hdr->sanity_check, our_sanity);
return;
}
// verify separate checksums of symbols and strings
uint32_t symbsize = hdr->symtab_entries * sizeof(ElfSym);
uint32_t csum_syms = crc32c(hdr->syms, symbsize);
uint32_t csum_strs = crc32c(&hdr->syms[hdr->symtab_entries], hdr->strtab_size);
if (csum_syms != hdr->checksum_syms || csum_strs != hdr->checksum_strs)
{
if (csum_syms != hdr->checksum_syms)
kprintf("ELF symbol tables checksum failed! "
"(%08x vs %08x)\n", csum_syms, hdr->checksum_syms);
if (csum_strs != hdr->checksum_strs)
kprintf("ELF string tables checksum failed! "
"(%08x vs %08x)\n", csum_strs, hdr->checksum_strs);
return;
}
}
#ifdef ARCH_x86_64
#include <kernel/memmap.hpp>
#include <kernel/memory.hpp>
#include <os.hpp>
void elf_protect_symbol_areas()
{
char* src = (char*) parser.symtab.base;
ptrdiff_t size = &parser.strtab.base[parser.strtab.size] - src;
if (size % os::mem::min_psize()) size += os::mem::min_psize() - (size & (os::mem::min_psize()-1));
if (size == 0) {
INFO2("* ELF symbol table size 0, ignoring.");
return;
}
if (src == 0) {
INFO2("ERROR: Symbol stable base address is 0x0, while size > 0. Not protecting. ELF symbol inspection may crash later.");
return;
}
// create the ELF symbols & strings area
os::mem::vmmap().assign_range(
{(uintptr_t) src, (uintptr_t) src + size-1, "Symbols & strings"});
INFO2("* Protecting syms %p to %p (size %#zx)", src, &src[size], size);
os::mem::protect((uintptr_t) src, size, os::mem::Access::read);
}
#endif