CustomElf.cpp (23780B)
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/. */ 4 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" 16 17 using namespace Elf; 18 19 /* TODO: Fill ElfLoader::Singleton.lastError on errors. */ 20 21 const Ehdr* Ehdr::validate(const void* buf) { 22 if (!buf || buf == MAP_FAILED) return nullptr; 23 24 const Ehdr* ehdr = reinterpret_cast<const Ehdr*>(buf); 25 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; 39 40 return ehdr; 41 } 42 43 namespace { 44 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' : '-'); 59 } 60 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); 64 } 65 66 } /* anonymous namespace */ 67 68 /** 69 * RAII wrapper for a mapping of the first page off a Mappable object. 70 * This calls Mappable::munmap instead of system munmap. 71 */ 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) {} 78 79 private: 80 friend class GenericMappedPtr<Mappable1stPagePtr>; 81 void munmap(void* buf, size_t length) { mappable->munmap(buf, length); } 82 83 RefPtr<Mappable> mappable; 84 }; 85 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; 96 97 const Ehdr* ehdr = Ehdr::validate(ehdr_raw); 98 if (!ehdr) return nullptr; 99 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; 105 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 112 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; 129 } 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; 136 } 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; 145 } 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); 158 } 159 } 160 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; 165 } 166 if (!dyn) { 167 ERROR("%s: No PT_DYNAMIC segment found", elf->GetPath()); 168 return nullptr; 169 } 170 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; 188 } 189 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; 194 195 /* We're not going to mmap anymore */ 196 mappable->finalize(); 197 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); 202 203 if (!elf->InitDyn(dyn)) return nullptr; 204 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); 211 } 212 213 if (!elf->Relocate() || !elf->RelocateJumps()) return nullptr; 214 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)); 221 } 222 223 if (!elf->CallInit()) return nullptr; 224 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 230 231 DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = %p", path, flags, 232 static_cast<void*>(elf)); 233 return elf.forget(); 234 } 235 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); 245 } 246 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); 277 } 278 279 void* sym; 280 281 unsigned long hash = Hash(symbol); 282 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; 290 } 291 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); 308 } 309 if (sym) return sym; 310 } 311 return nullptr; 312 } 313 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; 319 ; 320 } 321 322 int prot = p_flags_to_mprot(pt_load->p_flags); 323 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); 348 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); 355 } 356 return false; 357 } 358 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); 372 } 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; 377 } 378 } 379 } 380 return true; 381 } 382 383 namespace { 384 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)); 387 } 388 389 } /* anonymous namespace */ 390 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; 423 } 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; 431 } 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; 454 } 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; 495 } 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; 506 } 507 } 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 527 */ 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)); 537 } 538 } 539 540 if (!buckets || !symnum) { 541 ERROR("%s: Missing or broken DT_HASH", GetPath()); 542 return false; 543 } 544 if (!strtab) { 545 ERROR("%s: Missing DT_STRTAB", GetPath()); 546 return false; 547 } 548 if (!symtab) { 549 ERROR("%s: Missing DT_SYMTAB", GetPath()); 550 return false; 551 } 552 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); 560 } 561 562 return true; 563 } 564 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); 573 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; 578 } 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)); 589 } 590 } 591 592 if (symptr == nullptr) 593 WARN("%s: Relocation to NULL @0x%08" PRIxPTR, GetPath(), 594 uintptr_t(rel->r_offset)); 595 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; 610 } 611 } 612 return true; 613 } 614 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); 621 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; 626 } 627 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)); 635 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; 646 } 647 } 648 /* Apply relocation */ 649 *(void**)ptr = symptr; 650 } 651 return true; 652 } 653 654 bool CustomElf::CallInit() { 655 if (init) CallFunction(init); 656 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); 661 } 662 initialized = true; 663 return true; 664 } 665 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); 672 } 673 if (fini) CallFunction(fini); 674 }