Backed out changeset 88fbb17e3c20 (bug 1865637) for causing animation related mochite...
[gecko.git] / mozglue / linker / CustomElf.cpp
blobfa7dffa3953c5021a5956fefc1b2ed0528fc0bf8
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include <cstring>
6 #include <sys/mman.h>
7 #include <vector>
8 #include <dlfcn.h>
9 #include <signal.h>
10 #include <string.h>
11 #include "CustomElf.h"
12 #include "BaseElf.h"
13 #include "Mappable.h"
14 #include "Logging.h"
15 #include "mozilla/IntegerPrintfMacros.h"
17 using namespace Elf;
19 /* TODO: Fill ElfLoader::Singleton.lastError on errors. */
21 const Ehdr* Ehdr::validate(const void* buf) {
22 if (!buf || buf == MAP_FAILED) return nullptr;
24 const Ehdr* ehdr = reinterpret_cast<const Ehdr*>(buf);
26 /* Only support ELF executables or libraries for the host system */
27 if (memcmp(ELFMAG, &ehdr->e_ident, SELFMAG) ||
28 ehdr->e_ident[EI_CLASS] != ELFCLASS ||
29 ehdr->e_ident[EI_DATA] != ELFDATA || ehdr->e_ident[EI_VERSION] != 1 ||
30 (ehdr->e_ident[EI_OSABI] != ELFOSABI &&
31 ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) ||
32 #ifdef EI_ABIVERSION
33 ehdr->e_ident[EI_ABIVERSION] != ELFABIVERSION ||
34 #endif
35 (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) ||
36 ehdr->e_machine != ELFMACHINE || ehdr->e_version != 1 ||
37 ehdr->e_phentsize != sizeof(Phdr))
38 return nullptr;
40 return ehdr;
43 namespace {
45 void debug_phdr(const char* type, const Phdr* phdr) {
46 DEBUG_LOG("%s @0x%08" PRIxPTR
47 " ("
48 "filesz: 0x%08" PRIxPTR
49 ", "
50 "memsz: 0x%08" PRIxPTR
51 ", "
52 "offset: 0x%08" PRIxPTR
53 ", "
54 "flags: %c%c%c)",
55 type, uintptr_t(phdr->p_vaddr), uintptr_t(phdr->p_filesz),
56 uintptr_t(phdr->p_memsz), uintptr_t(phdr->p_offset),
57 phdr->p_flags & PF_R ? 'r' : '-', phdr->p_flags & PF_W ? 'w' : '-',
58 phdr->p_flags & PF_X ? 'x' : '-');
61 static int p_flags_to_mprot(Word flags) {
62 return ((flags & PF_X) ? PROT_EXEC : 0) | ((flags & PF_W) ? PROT_WRITE : 0) |
63 ((flags & PF_R) ? PROT_READ : 0);
66 } /* anonymous namespace */
68 /**
69 * RAII wrapper for a mapping of the first page off a Mappable object.
70 * This calls Mappable::munmap instead of system munmap.
72 class Mappable1stPagePtr : public GenericMappedPtr<Mappable1stPagePtr> {
73 public:
74 explicit Mappable1stPagePtr(Mappable* mappable)
75 : GenericMappedPtr<Mappable1stPagePtr>(
76 mappable->mmap(nullptr, PageSize(), PROT_READ, MAP_PRIVATE, 0)),
77 mappable(mappable) {}
79 private:
80 friend class GenericMappedPtr<Mappable1stPagePtr>;
81 void munmap(void* buf, size_t length) { mappable->munmap(buf, length); }
83 RefPtr<Mappable> mappable;
86 already_AddRefed<LibHandle> CustomElf::Load(Mappable* mappable,
87 const char* path, int flags) {
88 DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = ...", path, flags);
89 if (!mappable) return nullptr;
90 /* Keeping a RefPtr of the CustomElf is going to free the appropriate
91 * resources when returning nullptr */
92 RefPtr<CustomElf> elf = new CustomElf(mappable, path);
93 /* Map the first page of the Elf object to access Elf and program headers */
94 Mappable1stPagePtr ehdr_raw(mappable);
95 if (ehdr_raw == MAP_FAILED) return nullptr;
97 const Ehdr* ehdr = Ehdr::validate(ehdr_raw);
98 if (!ehdr) return nullptr;
100 /* Scan Elf Program Headers and gather some information about them */
101 std::vector<const Phdr*> pt_loads;
102 Addr min_vaddr = (Addr)-1; // We want to find the lowest and biggest
103 Addr max_vaddr = 0; // virtual address used by this Elf.
104 const Phdr* dyn = nullptr;
106 const Phdr* first_phdr = reinterpret_cast<const Phdr*>(
107 reinterpret_cast<const char*>(ehdr) + ehdr->e_phoff);
108 const Phdr* end_phdr = &first_phdr[ehdr->e_phnum];
109 #ifdef __ARM_EABI__
110 const Phdr* arm_exidx_phdr = nullptr;
111 #endif
113 for (const Phdr* phdr = first_phdr; phdr < end_phdr; phdr++) {
114 switch (phdr->p_type) {
115 case PT_LOAD:
116 debug_phdr("PT_LOAD", phdr);
117 pt_loads.push_back(phdr);
118 if (phdr->p_vaddr < min_vaddr) min_vaddr = phdr->p_vaddr;
119 if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
120 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
121 break;
122 case PT_DYNAMIC:
123 debug_phdr("PT_DYNAMIC", phdr);
124 if (!dyn) {
125 dyn = phdr;
126 } else {
127 ERROR("%s: Multiple PT_DYNAMIC segments detected", elf->GetPath());
128 return nullptr;
130 break;
131 case PT_TLS:
132 debug_phdr("PT_TLS", phdr);
133 if (phdr->p_memsz) {
134 ERROR("%s: TLS is not supported", elf->GetPath());
135 return nullptr;
137 break;
138 case PT_GNU_STACK:
139 debug_phdr("PT_GNU_STACK", phdr);
140 // Skip on Android until bug 706116 is fixed
141 #ifndef ANDROID
142 if (phdr->p_flags & PF_X) {
143 ERROR("%s: Executable stack is not supported", elf->GetPath());
144 return nullptr;
146 #endif
147 break;
148 #ifdef __ARM_EABI__
149 case PT_ARM_EXIDX:
150 /* We cannot initialize arm_exidx here
151 because we don't have a base yet */
152 arm_exidx_phdr = phdr;
153 break;
154 #endif
155 default:
156 DEBUG_LOG("%s: Program header type #%d not handled", elf->GetPath(),
157 phdr->p_type);
161 if (min_vaddr != 0) {
162 ERROR("%s: Unsupported minimal virtual address: 0x%08" PRIxPTR,
163 elf->GetPath(), uintptr_t(min_vaddr));
164 return nullptr;
166 if (!dyn) {
167 ERROR("%s: No PT_DYNAMIC segment found", elf->GetPath());
168 return nullptr;
171 /* Reserve enough memory to map the complete virtual address space for this
172 * library.
173 * As we are using the base address from here to mmap something else with
174 * MAP_FIXED | MAP_SHARED, we need to make sure these mmaps will work. For
175 * instance, on armv6, MAP_SHARED mappings require a 16k alignment, but mmap
176 * MAP_PRIVATE only returns a 4k aligned address. So we first get a base
177 * address with MAP_SHARED, which guarantees the kernel returns an address
178 * that we'll be able to use with MAP_FIXED, and then remap MAP_PRIVATE at
179 * the same address, because of some bad side effects of keeping it as
180 * MAP_SHARED. */
181 elf->base.Assign(MemoryRange::mmap(nullptr, max_vaddr, PROT_NONE,
182 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
183 if ((elf->base == MAP_FAILED) ||
184 (mmap(elf->base, max_vaddr, PROT_NONE,
185 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != elf->base)) {
186 ERROR("%s: Failed to mmap", elf->GetPath());
187 return nullptr;
190 /* Load and initialize library */
191 for (std::vector<const Phdr*>::iterator it = pt_loads.begin();
192 it < pt_loads.end(); ++it)
193 if (!elf->LoadSegment(*it)) return nullptr;
195 /* We're not going to mmap anymore */
196 mappable->finalize();
198 elf->l_addr = elf->base;
199 elf->l_name = elf->GetPath();
200 elf->l_ld = elf->GetPtr<Dyn>(dyn->p_vaddr);
201 ElfLoader::Singleton.Register(elf);
203 if (!elf->InitDyn(dyn)) return nullptr;
205 if (elf->has_text_relocs) {
206 for (std::vector<const Phdr*>::iterator it = pt_loads.begin();
207 it < pt_loads.end(); ++it)
208 mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)),
209 PageAlignedEndPtr((*it)->p_memsz),
210 p_flags_to_mprot((*it)->p_flags) | PROT_WRITE);
213 if (!elf->Relocate() || !elf->RelocateJumps()) return nullptr;
215 if (elf->has_text_relocs) {
216 for (std::vector<const Phdr*>::iterator it = pt_loads.begin();
217 it < pt_loads.end(); ++it)
218 mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)),
219 PageAlignedEndPtr((*it)->p_memsz),
220 p_flags_to_mprot((*it)->p_flags));
223 if (!elf->CallInit()) return nullptr;
225 #ifdef __ARM_EABI__
226 if (arm_exidx_phdr)
227 elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
228 arm_exidx_phdr->p_memsz);
229 #endif
231 DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = %p", path, flags,
232 static_cast<void*>(elf));
233 return elf.forget();
236 CustomElf::~CustomElf() {
237 DEBUG_LOG("CustomElf::~CustomElf(%p [\"%s\"])", reinterpret_cast<void*>(this),
238 GetPath());
239 CallFini();
240 /* Normally, __cxa_finalize is called by the .fini function. However,
241 * Android NDK before r6b doesn't do that. Our wrapped cxa_finalize only
242 * calls destructors once, so call it in all cases. */
243 ElfLoader::__wrap_cxa_finalize(this);
244 ElfLoader::Singleton.Forget(this);
247 void* CustomElf::GetSymbolPtrInDeps(const char* symbol) const {
248 /* Resolve dlopen and related functions to point to ours */
249 if (symbol[0] == 'd' && symbol[1] == 'l') {
250 if (strcmp(symbol + 2, "open") == 0) return FunctionPtr(__wrap_dlopen);
251 if (strcmp(symbol + 2, "error") == 0) return FunctionPtr(__wrap_dlerror);
252 if (strcmp(symbol + 2, "close") == 0) return FunctionPtr(__wrap_dlclose);
253 if (strcmp(symbol + 2, "sym") == 0) return FunctionPtr(__wrap_dlsym);
254 if (strcmp(symbol + 2, "addr") == 0) return FunctionPtr(__wrap_dladdr);
255 if (strcmp(symbol + 2, "_iterate_phdr") == 0)
256 return FunctionPtr(__wrap_dl_iterate_phdr);
257 } else if (symbol[0] == '_' && symbol[1] == '_') {
258 /* Resolve a few C++ ABI specific functions to point to ours */
259 #ifdef __ARM_EABI__
260 if (strcmp(symbol + 2, "aeabi_atexit") == 0)
261 return FunctionPtr(&ElfLoader::__wrap_aeabi_atexit);
262 #else
263 if (strcmp(symbol + 2, "cxa_atexit") == 0)
264 return FunctionPtr(&ElfLoader::__wrap_cxa_atexit);
265 #endif
266 if (strcmp(symbol + 2, "cxa_finalize") == 0)
267 return FunctionPtr(&ElfLoader::__wrap_cxa_finalize);
268 if (strcmp(symbol + 2, "dso_handle") == 0)
269 return const_cast<CustomElf*>(this);
270 #ifdef __ARM_EABI__
271 if (strcmp(symbol + 2, "gnu_Unwind_Find_exidx") == 0)
272 return FunctionPtr(__wrap___gnu_Unwind_Find_exidx);
273 #endif
274 } else if (symbol[0] == 's' && symbol[1] == 'i') {
275 if (strcmp(symbol + 2, "gnal") == 0) return FunctionPtr(signal);
276 if (strcmp(symbol + 2, "gaction") == 0) return FunctionPtr(sigaction);
279 void* sym;
281 unsigned long hash = Hash(symbol);
283 /* self_elf should never be NULL, but better safe than sorry. */
284 if (ElfLoader::Singleton.self_elf) {
285 /* We consider the library containing this code a permanent LD_PRELOAD,
286 * so, check if the symbol exists here first. */
287 sym = static_cast<BaseElf*>(ElfLoader::Singleton.self_elf.get())
288 ->GetSymbolPtr(symbol, hash);
289 if (sym) return sym;
292 /* Then search the symbol in our dependencies. Since we already searched in
293 * libraries the system linker loaded, skip those (on glibc systems). We
294 * also assume the symbol is to be found in one of the dependent libraries
295 * directly, not in their own dependent libraries. Building libraries with
296 * --no-allow-shlib-undefined ensures such indirect symbol dependency don't
297 * happen. */
298 for (std::vector<RefPtr<LibHandle> >::const_iterator it =
299 dependencies.begin();
300 it < dependencies.end(); ++it) {
301 /* Skip if it's the library containing this code, since we've already
302 * looked at it above. */
303 if (*it == ElfLoader::Singleton.self_elf) continue;
304 if (BaseElf* be = (*it)->AsBaseElf()) {
305 sym = be->GetSymbolPtr(symbol, hash);
306 } else {
307 sym = (*it)->GetSymbolPtr(symbol);
309 if (sym) return sym;
311 return nullptr;
314 bool CustomElf::LoadSegment(const Phdr* pt_load) const {
315 if (pt_load->p_type != PT_LOAD) {
316 DEBUG_LOG("%s: Elf::LoadSegment only takes PT_LOAD program headers",
317 GetPath());
318 return false;
322 int prot = p_flags_to_mprot(pt_load->p_flags);
324 /* Mmap at page boundary */
325 Addr align = PageSize();
326 Addr align_offset;
327 void *mapped, *where;
328 do {
329 align_offset = pt_load->p_vaddr - AlignedPtr(pt_load->p_vaddr, align);
330 where = GetPtr(pt_load->p_vaddr - align_offset);
331 DEBUG_LOG("%s: Loading segment @%p %c%c%c", GetPath(), where,
332 prot & PROT_READ ? 'r' : '-', prot & PROT_WRITE ? 'w' : '-',
333 prot & PROT_EXEC ? 'x' : '-');
334 mapped = mappable->mmap(where, pt_load->p_filesz + align_offset, prot,
335 MAP_PRIVATE | MAP_FIXED,
336 pt_load->p_offset - align_offset);
337 if ((mapped != MAP_FAILED) || (pt_load->p_vaddr == 0) ||
338 (pt_load->p_align == align))
339 break;
340 /* The virtual address space for the library is properly aligned at
341 * 16k on ARMv6 (see CustomElf::Load), and so is the first segment
342 * (p_vaddr == 0). But subsequent segments may not be 16k aligned
343 * and fail to mmap. In such case, try to mmap again at the p_align
344 * boundary instead of page boundary. */
345 DEBUG_LOG("%s: Failed to mmap, retrying", GetPath());
346 align = pt_load->p_align;
347 } while (1);
349 if (mapped != where) {
350 if (mapped == MAP_FAILED) {
351 ERROR("%s: Failed to mmap", GetPath());
352 } else {
353 ERROR("%s: Didn't map at the expected location (wanted: %p, got: %p)",
354 GetPath(), where, mapped);
356 return false;
359 /* When p_memsz is greater than p_filesz, we need to have nulled out memory
360 * after p_filesz and before p_memsz.
361 * Above the end of the last page, and up to p_memsz, we already have nulled
362 * out memory because we mapped anonymous memory on the whole library virtual
363 * address space. We just need to adjust this anonymous memory protection
364 * flags. */
365 if (pt_load->p_memsz > pt_load->p_filesz) {
366 Addr file_end = pt_load->p_vaddr + pt_load->p_filesz;
367 Addr mem_end = pt_load->p_vaddr + pt_load->p_memsz;
368 Addr next_page = PageAlignedEndPtr(file_end);
369 if (next_page > file_end) {
370 void* ptr = GetPtr(file_end);
371 memset(ptr, 0, next_page - file_end);
373 if (mem_end > next_page) {
374 if (mprotect(GetPtr(next_page), mem_end - next_page, prot) < 0) {
375 ERROR("%s: Failed to mprotect", GetPath());
376 return false;
380 return true;
383 namespace {
385 void debug_dyn(const char* type, const Dyn* dyn) {
386 DEBUG_LOG("%s 0x%08" PRIxPTR, type, uintptr_t(dyn->d_un.d_val));
389 } /* anonymous namespace */
391 bool CustomElf::InitDyn(const Phdr* pt_dyn) {
392 /* Scan PT_DYNAMIC segment and gather some information */
393 const Dyn* first_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr);
394 const Dyn* end_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr + pt_dyn->p_filesz);
395 std::vector<Word> dt_needed;
396 size_t symnum = 0;
397 for (const Dyn* dyn = first_dyn; dyn < end_dyn && dyn->d_tag; dyn++) {
398 switch (dyn->d_tag) {
399 case DT_NEEDED:
400 debug_dyn("DT_NEEDED", dyn);
401 dt_needed.push_back(dyn->d_un.d_val);
402 break;
403 case DT_HASH: {
404 debug_dyn("DT_HASH", dyn);
405 const Word* hash_table_header = GetPtr<Word>(dyn->d_un.d_ptr);
406 symnum = hash_table_header[1];
407 buckets.Init(&hash_table_header[2], hash_table_header[0]);
408 chains.Init(&*buckets.end());
409 } break;
410 case DT_STRTAB:
411 debug_dyn("DT_STRTAB", dyn);
412 strtab.Init(GetPtr(dyn->d_un.d_ptr));
413 break;
414 case DT_SYMTAB:
415 debug_dyn("DT_SYMTAB", dyn);
416 symtab.Init(GetPtr(dyn->d_un.d_ptr));
417 break;
418 case DT_SYMENT:
419 debug_dyn("DT_SYMENT", dyn);
420 if (dyn->d_un.d_val != sizeof(Sym)) {
421 ERROR("%s: Unsupported DT_SYMENT", GetPath());
422 return false;
424 break;
425 case DT_TEXTREL:
426 if (strcmp("libflashplayer.so", GetName()) == 0) {
427 has_text_relocs = true;
428 } else {
429 ERROR("%s: Text relocations are not supported", GetPath());
430 return false;
432 break;
433 case DT_STRSZ: /* Ignored */
434 debug_dyn("DT_STRSZ", dyn);
435 break;
436 case UNSUPPORTED_RELOC():
437 case UNSUPPORTED_RELOC(SZ):
438 case UNSUPPORTED_RELOC(ENT):
439 ERROR("%s: Unsupported relocations", GetPath());
440 return false;
441 case RELOC():
442 debug_dyn(STR_RELOC(), dyn);
443 relocations.Init(GetPtr(dyn->d_un.d_ptr));
444 break;
445 case RELOC(SZ):
446 debug_dyn(STR_RELOC(SZ), dyn);
447 relocations.InitSize(dyn->d_un.d_val);
448 break;
449 case RELOC(ENT):
450 debug_dyn(STR_RELOC(ENT), dyn);
451 if (dyn->d_un.d_val != sizeof(Reloc)) {
452 ERROR("%s: Unsupported DT_RELENT", GetPath());
453 return false;
455 break;
456 case DT_JMPREL:
457 debug_dyn("DT_JMPREL", dyn);
458 jumprels.Init(GetPtr(dyn->d_un.d_ptr));
459 break;
460 case DT_PLTRELSZ:
461 debug_dyn("DT_PLTRELSZ", dyn);
462 jumprels.InitSize(dyn->d_un.d_val);
463 break;
464 case DT_PLTGOT:
465 debug_dyn("DT_PLTGOT", dyn);
466 break;
467 case DT_INIT:
468 debug_dyn("DT_INIT", dyn);
469 init = dyn->d_un.d_ptr;
470 break;
471 case DT_INIT_ARRAY:
472 debug_dyn("DT_INIT_ARRAY", dyn);
473 init_array.Init(GetPtr(dyn->d_un.d_ptr));
474 break;
475 case DT_INIT_ARRAYSZ:
476 debug_dyn("DT_INIT_ARRAYSZ", dyn);
477 init_array.InitSize(dyn->d_un.d_val);
478 break;
479 case DT_FINI:
480 debug_dyn("DT_FINI", dyn);
481 fini = dyn->d_un.d_ptr;
482 break;
483 case DT_FINI_ARRAY:
484 debug_dyn("DT_FINI_ARRAY", dyn);
485 fini_array.Init(GetPtr(dyn->d_un.d_ptr));
486 break;
487 case DT_FINI_ARRAYSZ:
488 debug_dyn("DT_FINI_ARRAYSZ", dyn);
489 fini_array.InitSize(dyn->d_un.d_val);
490 break;
491 case DT_PLTREL:
492 if (dyn->d_un.d_val != RELOC()) {
493 ERROR("%s: Error: DT_PLTREL is not " STR_RELOC(), GetPath());
494 return false;
496 break;
497 case DT_FLAGS: {
498 Addr flags = dyn->d_un.d_val;
499 /* Treat as a DT_TEXTREL tag */
500 if (flags & DF_TEXTREL) {
501 if (strcmp("libflashplayer.so", GetName()) == 0) {
502 has_text_relocs = true;
503 } else {
504 ERROR("%s: Text relocations are not supported", GetPath());
505 return false;
508 /* we can treat this like having a DT_SYMBOLIC tag */
509 flags &= ~DF_SYMBOLIC;
510 if (flags)
511 WARN("%s: unhandled flags #%" PRIxPTR " not handled", GetPath(),
512 uintptr_t(flags));
513 } break;
514 case DT_SONAME: /* Should match GetName(), but doesn't matter */
515 case DT_SYMBOLIC: /* Indicates internal symbols should be looked up in
516 * the library itself first instead of the executable,
517 * which is actually what this linker does by default */
518 case RELOC(COUNT): /* Indicates how many relocations are relative, which
519 * is usually used to skip relocations on prelinked
520 * libraries. They are not supported anyways. */
521 case UNSUPPORTED_RELOC(COUNT): /* This should error out, but it doesn't
522 * really matter. */
523 case DT_FLAGS_1: /* Additional linker-internal flags that we don't care
524 * about. See DF_1_* values in src/include/elf/common.h
525 * in binutils. */
526 case DT_VERSYM: /* DT_VER* entries are used for symbol versioning, which
528 case DT_VERDEF: /* this linker doesn't support yet. */
529 case DT_VERDEFNUM:
530 case DT_VERNEED:
531 case DT_VERNEEDNUM:
532 /* Ignored */
533 break;
534 default:
535 WARN("%s: dynamic header type #%" PRIxPTR " not handled", GetPath(),
536 uintptr_t(dyn->d_tag));
540 if (!buckets || !symnum) {
541 ERROR("%s: Missing or broken DT_HASH", GetPath());
542 return false;
544 if (!strtab) {
545 ERROR("%s: Missing DT_STRTAB", GetPath());
546 return false;
548 if (!symtab) {
549 ERROR("%s: Missing DT_SYMTAB", GetPath());
550 return false;
553 /* Load dependent libraries */
554 for (size_t i = 0; i < dt_needed.size(); i++) {
555 const char* name = strtab.GetStringAt(dt_needed[i]);
556 RefPtr<LibHandle> handle =
557 ElfLoader::Singleton.Load(name, RTLD_GLOBAL | RTLD_LAZY, this);
558 if (!handle) return false;
559 dependencies.push_back(handle);
562 return true;
565 bool CustomElf::Relocate() {
566 DEBUG_LOG("Relocate %s @%p", GetPath(), static_cast<void*>(base));
567 uint32_t symtab_index = (uint32_t)-1;
568 void* symptr = nullptr;
569 for (Array<Reloc>::iterator rel = relocations.begin();
570 rel < relocations.end(); ++rel) {
571 /* Location of the relocation */
572 void* ptr = GetPtr(rel->r_offset);
574 /* R_*_RELATIVE relocations apply directly at the given location */
575 if (ELF_R_TYPE(rel->r_info) == R_RELATIVE) {
576 *(void**)ptr = GetPtr(rel->GetAddend(base));
577 continue;
579 /* Other relocation types need a symbol resolution */
580 /* Avoid symbol resolution when it's the same symbol as last iteration */
581 if (symtab_index != ELF_R_SYM(rel->r_info)) {
582 symtab_index = ELF_R_SYM(rel->r_info);
583 const Sym sym = symtab[symtab_index];
584 if (sym.st_shndx != SHN_UNDEF) {
585 symptr = GetPtr(sym.st_value);
586 } else {
587 /* TODO: handle symbol resolving to nullptr vs. being undefined. */
588 symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
592 if (symptr == nullptr)
593 WARN("%s: Relocation to NULL @0x%08" PRIxPTR, GetPath(),
594 uintptr_t(rel->r_offset));
596 /* Apply relocation */
597 switch (ELF_R_TYPE(rel->r_info)) {
598 case R_GLOB_DAT:
599 /* R_*_GLOB_DAT relocations simply use the symbol value */
600 *(void**)ptr = symptr;
601 break;
602 case R_ABS:
603 /* R_*_ABS* relocations add the relocation added to the symbol value */
604 *(const char**)ptr = (const char*)symptr + rel->GetAddend(base);
605 break;
606 default:
607 ERROR("%s: Unsupported relocation type: 0x%" PRIxPTR, GetPath(),
608 uintptr_t(ELF_R_TYPE(rel->r_info)));
609 return false;
612 return true;
615 bool CustomElf::RelocateJumps() {
616 /* TODO: Dynamic symbol resolution */
617 for (Array<Reloc>::iterator rel = jumprels.begin(); rel < jumprels.end();
618 ++rel) {
619 /* Location of the relocation */
620 void* ptr = GetPtr(rel->r_offset);
622 /* Only R_*_JMP_SLOT relocations are expected */
623 if (ELF_R_TYPE(rel->r_info) != R_JMP_SLOT) {
624 ERROR("%s: Jump relocation type mismatch", GetPath());
625 return false;
628 /* TODO: Avoid code duplication with the relocations above */
629 const Sym sym = symtab[ELF_R_SYM(rel->r_info)];
630 void* symptr;
631 if (sym.st_shndx != SHN_UNDEF)
632 symptr = GetPtr(sym.st_value);
633 else
634 symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
636 if (symptr == nullptr) {
637 if (ELF_ST_BIND(sym.st_info) == STB_WEAK) {
638 WARN("%s: Relocation to NULL @0x%08" PRIxPTR " for symbol \"%s\"",
639 GetPath(), uintptr_t(rel->r_offset),
640 strtab.GetStringAt(sym.st_name));
641 } else {
642 ERROR("%s: Relocation to NULL @0x%08" PRIxPTR " for symbol \"%s\"",
643 GetPath(), uintptr_t(rel->r_offset),
644 strtab.GetStringAt(sym.st_name));
645 return false;
648 /* Apply relocation */
649 *(void**)ptr = symptr;
651 return true;
654 bool CustomElf::CallInit() {
655 if (init) CallFunction(init);
657 for (Array<void*>::iterator it = init_array.begin(); it < init_array.end();
658 ++it) {
659 /* Android x86 NDK wrongly puts 0xffffffff in INIT_ARRAY */
660 if (*it && *it != reinterpret_cast<void*>(-1)) CallFunction(*it);
662 initialized = true;
663 return true;
666 void CustomElf::CallFini() {
667 if (!initialized) return;
668 for (Array<void*>::reverse_iterator it = fini_array.rbegin();
669 it < fini_array.rend(); ++it) {
670 /* Android x86 NDK wrongly puts 0xffffffff in FINI_ARRAY */
671 if (*it && *it != reinterpret_cast<void*>(-1)) CallFunction(*it);
673 if (fini) CallFunction(fini);