Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include <cstring> |
michael@0 | 6 | #include <sys/mman.h> |
michael@0 | 7 | #include <vector> |
michael@0 | 8 | #include <dlfcn.h> |
michael@0 | 9 | #include "CustomElf.h" |
michael@0 | 10 | #include "Mappable.h" |
michael@0 | 11 | #include "Logging.h" |
michael@0 | 12 | |
michael@0 | 13 | using namespace Elf; |
michael@0 | 14 | using namespace mozilla; |
michael@0 | 15 | |
michael@0 | 16 | /* TODO: Fill ElfLoader::Singleton.lastError on errors. */ |
michael@0 | 17 | |
michael@0 | 18 | /* Function used to report library mappings from the custom linker to Gecko |
michael@0 | 19 | * crash reporter */ |
michael@0 | 20 | #ifdef ANDROID |
michael@0 | 21 | extern "C" { |
michael@0 | 22 | void report_mapping(char *name, void *base, uint32_t len, uint32_t offset); |
michael@0 | 23 | } |
michael@0 | 24 | #else |
michael@0 | 25 | #define report_mapping(...) |
michael@0 | 26 | #endif |
michael@0 | 27 | |
michael@0 | 28 | const Ehdr *Ehdr::validate(const void *buf) |
michael@0 | 29 | { |
michael@0 | 30 | if (!buf || buf == MAP_FAILED) |
michael@0 | 31 | return nullptr; |
michael@0 | 32 | |
michael@0 | 33 | const Ehdr *ehdr = reinterpret_cast<const Ehdr *>(buf); |
michael@0 | 34 | |
michael@0 | 35 | /* Only support ELF executables or libraries for the host system */ |
michael@0 | 36 | if (memcmp(ELFMAG, &ehdr->e_ident, SELFMAG) || |
michael@0 | 37 | ehdr->e_ident[EI_CLASS] != ELFCLASS || |
michael@0 | 38 | ehdr->e_ident[EI_DATA] != ELFDATA || |
michael@0 | 39 | ehdr->e_ident[EI_VERSION] != 1 || |
michael@0 | 40 | (ehdr->e_ident[EI_OSABI] != ELFOSABI && ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) || |
michael@0 | 41 | #ifdef EI_ABIVERSION |
michael@0 | 42 | ehdr->e_ident[EI_ABIVERSION] != ELFABIVERSION || |
michael@0 | 43 | #endif |
michael@0 | 44 | (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) || |
michael@0 | 45 | ehdr->e_machine != ELFMACHINE || |
michael@0 | 46 | ehdr->e_version != 1 || |
michael@0 | 47 | ehdr->e_phentsize != sizeof(Phdr)) |
michael@0 | 48 | return nullptr; |
michael@0 | 49 | |
michael@0 | 50 | return ehdr; |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | namespace { |
michael@0 | 54 | |
michael@0 | 55 | void debug_phdr(const char *type, const Phdr *phdr) |
michael@0 | 56 | { |
michael@0 | 57 | DEBUG_LOG("%s @0x%08" PRIxAddr " (" |
michael@0 | 58 | "filesz: 0x%08" PRIxAddr ", " |
michael@0 | 59 | "memsz: 0x%08" PRIxAddr ", " |
michael@0 | 60 | "offset: 0x%08" PRIxAddr ", " |
michael@0 | 61 | "flags: %c%c%c)", |
michael@0 | 62 | type, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, |
michael@0 | 63 | phdr->p_offset, phdr->p_flags & PF_R ? 'r' : '-', |
michael@0 | 64 | phdr->p_flags & PF_W ? 'w' : '-', phdr->p_flags & PF_X ? 'x' : '-'); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | static int p_flags_to_mprot(Word flags) |
michael@0 | 68 | { |
michael@0 | 69 | return ((flags & PF_X) ? PROT_EXEC : 0) | |
michael@0 | 70 | ((flags & PF_W) ? PROT_WRITE : 0) | |
michael@0 | 71 | ((flags & PF_R) ? PROT_READ : 0); |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | void |
michael@0 | 75 | __void_stub(void) |
michael@0 | 76 | { |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | } /* anonymous namespace */ |
michael@0 | 80 | |
michael@0 | 81 | /** |
michael@0 | 82 | * RAII wrapper for a mapping of the first page off a Mappable object. |
michael@0 | 83 | * This calls Mappable::munmap instead of system munmap. |
michael@0 | 84 | */ |
michael@0 | 85 | class Mappable1stPagePtr: public GenericMappedPtr<Mappable1stPagePtr> { |
michael@0 | 86 | public: |
michael@0 | 87 | Mappable1stPagePtr(Mappable *mappable) |
michael@0 | 88 | : GenericMappedPtr<Mappable1stPagePtr>( |
michael@0 | 89 | mappable->mmap(nullptr, PageSize(), PROT_READ, MAP_PRIVATE, 0)) |
michael@0 | 90 | , mappable(mappable) |
michael@0 | 91 | { |
michael@0 | 92 | /* Ensure the content of this page */ |
michael@0 | 93 | mappable->ensure(*this); |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | private: |
michael@0 | 97 | friend class GenericMappedPtr<Mappable1stPagePtr>; |
michael@0 | 98 | void munmap(void *buf, size_t length) { |
michael@0 | 99 | mappable->munmap(buf, length); |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | mozilla::RefPtr<Mappable> mappable; |
michael@0 | 103 | }; |
michael@0 | 104 | |
michael@0 | 105 | |
michael@0 | 106 | TemporaryRef<LibHandle> |
michael@0 | 107 | CustomElf::Load(Mappable *mappable, const char *path, int flags) |
michael@0 | 108 | { |
michael@0 | 109 | DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = ...", path, flags); |
michael@0 | 110 | if (!mappable) |
michael@0 | 111 | return nullptr; |
michael@0 | 112 | /* Keeping a RefPtr of the CustomElf is going to free the appropriate |
michael@0 | 113 | * resources when returning nullptr */ |
michael@0 | 114 | RefPtr<CustomElf> elf = new CustomElf(mappable, path); |
michael@0 | 115 | /* Map the first page of the Elf object to access Elf and program headers */ |
michael@0 | 116 | Mappable1stPagePtr ehdr_raw(mappable); |
michael@0 | 117 | if (ehdr_raw == MAP_FAILED) |
michael@0 | 118 | return nullptr; |
michael@0 | 119 | |
michael@0 | 120 | const Ehdr *ehdr = Ehdr::validate(ehdr_raw); |
michael@0 | 121 | if (!ehdr) |
michael@0 | 122 | return nullptr; |
michael@0 | 123 | |
michael@0 | 124 | /* Scan Elf Program Headers and gather some information about them */ |
michael@0 | 125 | std::vector<const Phdr *> pt_loads; |
michael@0 | 126 | Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest |
michael@0 | 127 | Addr max_vaddr = 0; // virtual address used by this Elf. |
michael@0 | 128 | const Phdr *dyn = nullptr; |
michael@0 | 129 | |
michael@0 | 130 | const Phdr *first_phdr = reinterpret_cast<const Phdr *>( |
michael@0 | 131 | reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff); |
michael@0 | 132 | const Phdr *end_phdr = &first_phdr[ehdr->e_phnum]; |
michael@0 | 133 | #ifdef __ARM_EABI__ |
michael@0 | 134 | const Phdr *arm_exidx_phdr = nullptr; |
michael@0 | 135 | #endif |
michael@0 | 136 | |
michael@0 | 137 | for (const Phdr *phdr = first_phdr; phdr < end_phdr; phdr++) { |
michael@0 | 138 | switch (phdr->p_type) { |
michael@0 | 139 | case PT_LOAD: |
michael@0 | 140 | debug_phdr("PT_LOAD", phdr); |
michael@0 | 141 | pt_loads.push_back(phdr); |
michael@0 | 142 | if (phdr->p_vaddr < min_vaddr) |
michael@0 | 143 | min_vaddr = phdr->p_vaddr; |
michael@0 | 144 | if (max_vaddr < phdr->p_vaddr + phdr->p_memsz) |
michael@0 | 145 | max_vaddr = phdr->p_vaddr + phdr->p_memsz; |
michael@0 | 146 | break; |
michael@0 | 147 | case PT_DYNAMIC: |
michael@0 | 148 | debug_phdr("PT_DYNAMIC", phdr); |
michael@0 | 149 | if (!dyn) { |
michael@0 | 150 | dyn = phdr; |
michael@0 | 151 | } else { |
michael@0 | 152 | LOG("%s: Multiple PT_DYNAMIC segments detected", elf->GetPath()); |
michael@0 | 153 | return nullptr; |
michael@0 | 154 | } |
michael@0 | 155 | break; |
michael@0 | 156 | case PT_TLS: |
michael@0 | 157 | debug_phdr("PT_TLS", phdr); |
michael@0 | 158 | if (phdr->p_memsz) { |
michael@0 | 159 | LOG("%s: TLS is not supported", elf->GetPath()); |
michael@0 | 160 | return nullptr; |
michael@0 | 161 | } |
michael@0 | 162 | break; |
michael@0 | 163 | case PT_GNU_STACK: |
michael@0 | 164 | debug_phdr("PT_GNU_STACK", phdr); |
michael@0 | 165 | // Skip on Android until bug 706116 is fixed |
michael@0 | 166 | #ifndef ANDROID |
michael@0 | 167 | if (phdr->p_flags & PF_X) { |
michael@0 | 168 | LOG("%s: Executable stack is not supported", elf->GetPath()); |
michael@0 | 169 | return nullptr; |
michael@0 | 170 | } |
michael@0 | 171 | #endif |
michael@0 | 172 | break; |
michael@0 | 173 | #ifdef __ARM_EABI__ |
michael@0 | 174 | case PT_ARM_EXIDX: |
michael@0 | 175 | /* We cannot initialize arm_exidx here |
michael@0 | 176 | because we don't have a base yet */ |
michael@0 | 177 | arm_exidx_phdr = phdr; |
michael@0 | 178 | break; |
michael@0 | 179 | #endif |
michael@0 | 180 | default: |
michael@0 | 181 | DEBUG_LOG("%s: Warning: program header type #%d not handled", |
michael@0 | 182 | elf->GetPath(), phdr->p_type); |
michael@0 | 183 | } |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | if (min_vaddr != 0) { |
michael@0 | 187 | LOG("%s: Unsupported minimal virtual address: 0x%08" PRIxAddr, |
michael@0 | 188 | elf->GetPath(), min_vaddr); |
michael@0 | 189 | return nullptr; |
michael@0 | 190 | } |
michael@0 | 191 | if (!dyn) { |
michael@0 | 192 | LOG("%s: No PT_DYNAMIC segment found", elf->GetPath()); |
michael@0 | 193 | return nullptr; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | /* Reserve enough memory to map the complete virtual address space for this |
michael@0 | 197 | * library. |
michael@0 | 198 | * As we are using the base address from here to mmap something else with |
michael@0 | 199 | * MAP_FIXED | MAP_SHARED, we need to make sure these mmaps will work. For |
michael@0 | 200 | * instance, on armv6, MAP_SHARED mappings require a 16k alignment, but mmap |
michael@0 | 201 | * MAP_PRIVATE only returns a 4k aligned address. So we first get a base |
michael@0 | 202 | * address with MAP_SHARED, which guarantees the kernel returns an address |
michael@0 | 203 | * that we'll be able to use with MAP_FIXED, and then remap MAP_PRIVATE at |
michael@0 | 204 | * the same address, because of some bad side effects of keeping it as |
michael@0 | 205 | * MAP_SHARED. */ |
michael@0 | 206 | elf->base.Assign(MemoryRange::mmap(nullptr, max_vaddr, PROT_NONE, |
michael@0 | 207 | MAP_SHARED | MAP_ANONYMOUS, -1, 0)); |
michael@0 | 208 | if ((elf->base == MAP_FAILED) || |
michael@0 | 209 | (mmap(elf->base, max_vaddr, PROT_NONE, |
michael@0 | 210 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != elf->base)) { |
michael@0 | 211 | LOG("%s: Failed to mmap", elf->GetPath()); |
michael@0 | 212 | return nullptr; |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | /* Load and initialize library */ |
michael@0 | 216 | for (std::vector<const Phdr *>::iterator it = pt_loads.begin(); |
michael@0 | 217 | it < pt_loads.end(); ++it) |
michael@0 | 218 | if (!elf->LoadSegment(*it)) |
michael@0 | 219 | return nullptr; |
michael@0 | 220 | |
michael@0 | 221 | /* We're not going to mmap anymore */ |
michael@0 | 222 | mappable->finalize(); |
michael@0 | 223 | |
michael@0 | 224 | report_mapping(const_cast<char *>(elf->GetName()), elf->base, |
michael@0 | 225 | (max_vaddr + PAGE_SIZE - 1) & PAGE_MASK, 0); |
michael@0 | 226 | |
michael@0 | 227 | elf->l_addr = elf->base; |
michael@0 | 228 | elf->l_name = elf->GetPath(); |
michael@0 | 229 | elf->l_ld = elf->GetPtr<Dyn>(dyn->p_vaddr); |
michael@0 | 230 | ElfLoader::Singleton.Register(elf); |
michael@0 | 231 | |
michael@0 | 232 | if (!elf->InitDyn(dyn)) |
michael@0 | 233 | return nullptr; |
michael@0 | 234 | |
michael@0 | 235 | if (elf->has_text_relocs) { |
michael@0 | 236 | for (std::vector<const Phdr *>::iterator it = pt_loads.begin(); |
michael@0 | 237 | it < pt_loads.end(); ++it) |
michael@0 | 238 | mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)), |
michael@0 | 239 | PageAlignedEndPtr((*it)->p_memsz), |
michael@0 | 240 | p_flags_to_mprot((*it)->p_flags) | PROT_WRITE); |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | if (!elf->Relocate() || !elf->RelocateJumps()) |
michael@0 | 244 | return nullptr; |
michael@0 | 245 | |
michael@0 | 246 | if (elf->has_text_relocs) { |
michael@0 | 247 | for (std::vector<const Phdr *>::iterator it = pt_loads.begin(); |
michael@0 | 248 | it < pt_loads.end(); ++it) |
michael@0 | 249 | mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)), |
michael@0 | 250 | PageAlignedEndPtr((*it)->p_memsz), |
michael@0 | 251 | p_flags_to_mprot((*it)->p_flags)); |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | if (!elf->CallInit()) |
michael@0 | 255 | return nullptr; |
michael@0 | 256 | |
michael@0 | 257 | #ifdef __ARM_EABI__ |
michael@0 | 258 | if (arm_exidx_phdr) |
michael@0 | 259 | elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr), |
michael@0 | 260 | arm_exidx_phdr->p_memsz); |
michael@0 | 261 | #endif |
michael@0 | 262 | |
michael@0 | 263 | elf->stats("oneLibLoaded"); |
michael@0 | 264 | DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = %p", path, flags, |
michael@0 | 265 | static_cast<void *>(elf)); |
michael@0 | 266 | return elf; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | CustomElf::~CustomElf() |
michael@0 | 270 | { |
michael@0 | 271 | DEBUG_LOG("CustomElf::~CustomElf(%p [\"%s\"])", |
michael@0 | 272 | reinterpret_cast<void *>(this), GetPath()); |
michael@0 | 273 | CallFini(); |
michael@0 | 274 | /* Normally, __cxa_finalize is called by the .fini function. However, |
michael@0 | 275 | * Android NDK before r6b doesn't do that. Our wrapped cxa_finalize only |
michael@0 | 276 | * calls destructors once, so call it in all cases. */ |
michael@0 | 277 | ElfLoader::__wrap_cxa_finalize(this); |
michael@0 | 278 | ElfLoader::Singleton.Forget(this); |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | namespace { |
michael@0 | 282 | |
michael@0 | 283 | /** |
michael@0 | 284 | * Hash function for symbol lookup, as defined in ELF standard for System V |
michael@0 | 285 | */ |
michael@0 | 286 | unsigned long |
michael@0 | 287 | ElfHash(const char *symbol) |
michael@0 | 288 | { |
michael@0 | 289 | const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol); |
michael@0 | 290 | unsigned long h = 0, g; |
michael@0 | 291 | while (*sym) { |
michael@0 | 292 | h = (h << 4) + *sym++; |
michael@0 | 293 | if ((g = h & 0xf0000000)) |
michael@0 | 294 | h ^= g >> 24; |
michael@0 | 295 | h &= ~g; |
michael@0 | 296 | } |
michael@0 | 297 | return h; |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | } /* anonymous namespace */ |
michael@0 | 301 | |
michael@0 | 302 | void * |
michael@0 | 303 | CustomElf::GetSymbolPtr(const char *symbol) const |
michael@0 | 304 | { |
michael@0 | 305 | return GetSymbolPtr(symbol, ElfHash(symbol)); |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | void * |
michael@0 | 309 | CustomElf::GetSymbolPtr(const char *symbol, unsigned long hash) const |
michael@0 | 310 | { |
michael@0 | 311 | const Sym *sym = GetSymbol(symbol, hash); |
michael@0 | 312 | void *ptr = nullptr; |
michael@0 | 313 | if (sym && sym->st_shndx != SHN_UNDEF) |
michael@0 | 314 | ptr = GetPtr(sym->st_value); |
michael@0 | 315 | DEBUG_LOG("CustomElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p", |
michael@0 | 316 | reinterpret_cast<const void *>(this), GetPath(), symbol, ptr); |
michael@0 | 317 | return ptr; |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | void * |
michael@0 | 321 | CustomElf::GetSymbolPtrInDeps(const char *symbol) const |
michael@0 | 322 | { |
michael@0 | 323 | /* Resolve dlopen and related functions to point to ours */ |
michael@0 | 324 | if (symbol[0] == 'd' && symbol[1] == 'l') { |
michael@0 | 325 | if (strcmp(symbol + 2, "open") == 0) |
michael@0 | 326 | return FunctionPtr(__wrap_dlopen); |
michael@0 | 327 | if (strcmp(symbol + 2, "error") == 0) |
michael@0 | 328 | return FunctionPtr(__wrap_dlerror); |
michael@0 | 329 | if (strcmp(symbol + 2, "close") == 0) |
michael@0 | 330 | return FunctionPtr(__wrap_dlclose); |
michael@0 | 331 | if (strcmp(symbol + 2, "sym") == 0) |
michael@0 | 332 | return FunctionPtr(__wrap_dlsym); |
michael@0 | 333 | if (strcmp(symbol + 2, "addr") == 0) |
michael@0 | 334 | return FunctionPtr(__wrap_dladdr); |
michael@0 | 335 | if (strcmp(symbol + 2, "_iterate_phdr") == 0) |
michael@0 | 336 | return FunctionPtr(__wrap_dl_iterate_phdr); |
michael@0 | 337 | } else if (symbol[0] == '_' && symbol[1] == '_') { |
michael@0 | 338 | /* Resolve a few C++ ABI specific functions to point to ours */ |
michael@0 | 339 | #ifdef __ARM_EABI__ |
michael@0 | 340 | if (strcmp(symbol + 2, "aeabi_atexit") == 0) |
michael@0 | 341 | return FunctionPtr(&ElfLoader::__wrap_aeabi_atexit); |
michael@0 | 342 | #else |
michael@0 | 343 | if (strcmp(symbol + 2, "cxa_atexit") == 0) |
michael@0 | 344 | return FunctionPtr(&ElfLoader::__wrap_cxa_atexit); |
michael@0 | 345 | #endif |
michael@0 | 346 | if (strcmp(symbol + 2, "cxa_finalize") == 0) |
michael@0 | 347 | return FunctionPtr(&ElfLoader::__wrap_cxa_finalize); |
michael@0 | 348 | if (strcmp(symbol + 2, "dso_handle") == 0) |
michael@0 | 349 | return const_cast<CustomElf *>(this); |
michael@0 | 350 | if (strcmp(symbol + 2, "moz_linker_stats") == 0) |
michael@0 | 351 | return FunctionPtr(&ElfLoader::stats); |
michael@0 | 352 | #ifdef __ARM_EABI__ |
michael@0 | 353 | if (strcmp(symbol + 2, "gnu_Unwind_Find_exidx") == 0) |
michael@0 | 354 | return FunctionPtr(__wrap___gnu_Unwind_Find_exidx); |
michael@0 | 355 | #endif |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | #define MISSING_FLASH_SYMNAME_START "_ZN7android10VectorImpl19reservedVectorImpl" |
michael@0 | 359 | |
michael@0 | 360 | // Android changed some symbols that Flash depended on in 4.4, |
michael@0 | 361 | // so stub those out here |
michael@0 | 362 | if (strncmp(symbol, |
michael@0 | 363 | MISSING_FLASH_SYMNAME_START, |
michael@0 | 364 | sizeof(MISSING_FLASH_SYMNAME_START) - 1) == 0) { |
michael@0 | 365 | return FunctionPtr(__void_stub); |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | void *sym; |
michael@0 | 369 | /* Search the symbol in the main program. Note this also tries all libraries |
michael@0 | 370 | * the system linker will have loaded RTLD_GLOBAL. Unfortunately, that doesn't |
michael@0 | 371 | * work with bionic, but its linker doesn't normally search the main binary |
michael@0 | 372 | * anyways. Moreover, on android, the main binary is dalvik. */ |
michael@0 | 373 | #ifdef __GLIBC__ |
michael@0 | 374 | sym = dlsym(RTLD_DEFAULT, symbol); |
michael@0 | 375 | DEBUG_LOG("dlsym(RTLD_DEFAULT, \"%s\") = %p", symbol, sym); |
michael@0 | 376 | if (sym) |
michael@0 | 377 | return sym; |
michael@0 | 378 | #endif |
michael@0 | 379 | |
michael@0 | 380 | /* Then search the symbol in our dependencies. Since we already searched in |
michael@0 | 381 | * libraries the system linker loaded, skip those (on glibc systems). We |
michael@0 | 382 | * also assume the symbol is to be found in one of the dependent libraries |
michael@0 | 383 | * directly, not in their own dependent libraries. Building libraries with |
michael@0 | 384 | * --no-allow-shlib-undefined ensures such indirect symbol dependency don't |
michael@0 | 385 | * happen. */ |
michael@0 | 386 | unsigned long hash = ElfHash(symbol); |
michael@0 | 387 | for (std::vector<RefPtr<LibHandle> >::const_iterator it = dependencies.begin(); |
michael@0 | 388 | it < dependencies.end(); ++it) { |
michael@0 | 389 | if (!(*it)->IsSystemElf()) { |
michael@0 | 390 | sym = reinterpret_cast<CustomElf *>((*it).get())->GetSymbolPtr(symbol, hash); |
michael@0 | 391 | #ifndef __GLIBC__ |
michael@0 | 392 | } else { |
michael@0 | 393 | sym = (*it)->GetSymbolPtr(symbol); |
michael@0 | 394 | #endif |
michael@0 | 395 | } |
michael@0 | 396 | if (sym) |
michael@0 | 397 | return sym; |
michael@0 | 398 | } |
michael@0 | 399 | return nullptr; |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | const Sym * |
michael@0 | 403 | CustomElf::GetSymbol(const char *symbol, unsigned long hash) const |
michael@0 | 404 | { |
michael@0 | 405 | /* Search symbol with the buckets and chains tables. |
michael@0 | 406 | * The hash computed from the symbol name gives an index in the buckets |
michael@0 | 407 | * table. The corresponding value in the bucket table is an index in the |
michael@0 | 408 | * symbols table and in the chains table. |
michael@0 | 409 | * If the corresponding symbol in the symbols table matches, we're done. |
michael@0 | 410 | * Otherwise, the corresponding value in the chains table is a new index |
michael@0 | 411 | * in both tables, which corresponding symbol is tested and so on and so |
michael@0 | 412 | * forth */ |
michael@0 | 413 | size_t bucket = hash % buckets.numElements(); |
michael@0 | 414 | for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) { |
michael@0 | 415 | if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name))) |
michael@0 | 416 | continue; |
michael@0 | 417 | return &symtab[y]; |
michael@0 | 418 | } |
michael@0 | 419 | return nullptr; |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | bool |
michael@0 | 423 | CustomElf::Contains(void *addr) const |
michael@0 | 424 | { |
michael@0 | 425 | return base.Contains(addr); |
michael@0 | 426 | } |
michael@0 | 427 | |
michael@0 | 428 | #ifdef __ARM_EABI__ |
michael@0 | 429 | const void * |
michael@0 | 430 | CustomElf::FindExidx(int *pcount) const |
michael@0 | 431 | { |
michael@0 | 432 | if (arm_exidx) { |
michael@0 | 433 | *pcount = arm_exidx.numElements(); |
michael@0 | 434 | return arm_exidx; |
michael@0 | 435 | } |
michael@0 | 436 | *pcount = 0; |
michael@0 | 437 | return nullptr; |
michael@0 | 438 | } |
michael@0 | 439 | #endif |
michael@0 | 440 | |
michael@0 | 441 | void |
michael@0 | 442 | CustomElf::stats(const char *when) const |
michael@0 | 443 | { |
michael@0 | 444 | mappable->stats(when, GetPath()); |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | bool |
michael@0 | 448 | CustomElf::LoadSegment(const Phdr *pt_load) const |
michael@0 | 449 | { |
michael@0 | 450 | if (pt_load->p_type != PT_LOAD) { |
michael@0 | 451 | DEBUG_LOG("%s: Elf::LoadSegment only takes PT_LOAD program headers", GetPath()); |
michael@0 | 452 | return false;; |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | int prot = p_flags_to_mprot(pt_load->p_flags); |
michael@0 | 456 | |
michael@0 | 457 | /* Mmap at page boundary */ |
michael@0 | 458 | Addr align = PageSize(); |
michael@0 | 459 | Addr align_offset; |
michael@0 | 460 | void *mapped, *where; |
michael@0 | 461 | do { |
michael@0 | 462 | align_offset = pt_load->p_vaddr - AlignedPtr(pt_load->p_vaddr, align); |
michael@0 | 463 | where = GetPtr(pt_load->p_vaddr - align_offset); |
michael@0 | 464 | DEBUG_LOG("%s: Loading segment @%p %c%c%c", GetPath(), where, |
michael@0 | 465 | prot & PROT_READ ? 'r' : '-', |
michael@0 | 466 | prot & PROT_WRITE ? 'w' : '-', |
michael@0 | 467 | prot & PROT_EXEC ? 'x' : '-'); |
michael@0 | 468 | mapped = mappable->mmap(where, pt_load->p_filesz + align_offset, |
michael@0 | 469 | prot, MAP_PRIVATE | MAP_FIXED, |
michael@0 | 470 | pt_load->p_offset - align_offset); |
michael@0 | 471 | if ((mapped != MAP_FAILED) || (pt_load->p_vaddr == 0) || |
michael@0 | 472 | (pt_load->p_align == align)) |
michael@0 | 473 | break; |
michael@0 | 474 | /* The virtual address space for the library is properly aligned at |
michael@0 | 475 | * 16k on ARMv6 (see CustomElf::Load), and so is the first segment |
michael@0 | 476 | * (p_vaddr == 0). But subsequent segments may not be 16k aligned |
michael@0 | 477 | * and fail to mmap. In such case, try to mmap again at the p_align |
michael@0 | 478 | * boundary instead of page boundary. */ |
michael@0 | 479 | DEBUG_LOG("%s: Failed to mmap, retrying", GetPath()); |
michael@0 | 480 | align = pt_load->p_align; |
michael@0 | 481 | } while (1); |
michael@0 | 482 | |
michael@0 | 483 | if (mapped != where) { |
michael@0 | 484 | if (mapped == MAP_FAILED) { |
michael@0 | 485 | LOG("%s: Failed to mmap", GetPath()); |
michael@0 | 486 | } else { |
michael@0 | 487 | LOG("%s: Didn't map at the expected location (wanted: %p, got: %p)", |
michael@0 | 488 | GetPath(), where, mapped); |
michael@0 | 489 | } |
michael@0 | 490 | return false; |
michael@0 | 491 | } |
michael@0 | 492 | |
michael@0 | 493 | /* Ensure the availability of all pages within the mapping if on-demand |
michael@0 | 494 | * decompression is disabled (MOZ_LINKER_ONDEMAND=0 or signal handler not |
michael@0 | 495 | * registered). */ |
michael@0 | 496 | const char *ondemand = getenv("MOZ_LINKER_ONDEMAND"); |
michael@0 | 497 | if (!ElfLoader::Singleton.hasRegisteredHandler() || |
michael@0 | 498 | (ondemand && !strncmp(ondemand, "0", 2 /* Including '\0' */))) { |
michael@0 | 499 | for (Addr off = 0; off < pt_load->p_filesz + align_offset; |
michael@0 | 500 | off += PageSize()) { |
michael@0 | 501 | mappable->ensure(reinterpret_cast<char *>(mapped) + off); |
michael@0 | 502 | } |
michael@0 | 503 | } |
michael@0 | 504 | /* When p_memsz is greater than p_filesz, we need to have nulled out memory |
michael@0 | 505 | * after p_filesz and before p_memsz. |
michael@0 | 506 | * Above the end of the last page, and up to p_memsz, we already have nulled |
michael@0 | 507 | * out memory because we mapped anonymous memory on the whole library virtual |
michael@0 | 508 | * address space. We just need to adjust this anonymous memory protection |
michael@0 | 509 | * flags. */ |
michael@0 | 510 | if (pt_load->p_memsz > pt_load->p_filesz) { |
michael@0 | 511 | Addr file_end = pt_load->p_vaddr + pt_load->p_filesz; |
michael@0 | 512 | Addr mem_end = pt_load->p_vaddr + pt_load->p_memsz; |
michael@0 | 513 | Addr next_page = PageAlignedEndPtr(file_end); |
michael@0 | 514 | if (next_page > file_end) { |
michael@0 | 515 | /* The library is not registered at this point, so we can't rely on |
michael@0 | 516 | * on-demand decompression to handle missing pages here. */ |
michael@0 | 517 | void *ptr = GetPtr(file_end); |
michael@0 | 518 | mappable->ensure(ptr); |
michael@0 | 519 | memset(ptr, 0, next_page - file_end); |
michael@0 | 520 | } |
michael@0 | 521 | if (mem_end > next_page) { |
michael@0 | 522 | if (mprotect(GetPtr(next_page), mem_end - next_page, prot) < 0) { |
michael@0 | 523 | LOG("%s: Failed to mprotect", GetPath()); |
michael@0 | 524 | return false; |
michael@0 | 525 | } |
michael@0 | 526 | } |
michael@0 | 527 | } |
michael@0 | 528 | return true; |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | namespace { |
michael@0 | 532 | |
michael@0 | 533 | void debug_dyn(const char *type, const Dyn *dyn) |
michael@0 | 534 | { |
michael@0 | 535 | DEBUG_LOG("%s 0x%08" PRIxAddr, type, dyn->d_un.d_val); |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | } /* anonymous namespace */ |
michael@0 | 539 | |
michael@0 | 540 | bool |
michael@0 | 541 | CustomElf::InitDyn(const Phdr *pt_dyn) |
michael@0 | 542 | { |
michael@0 | 543 | /* Scan PT_DYNAMIC segment and gather some information */ |
michael@0 | 544 | const Dyn *first_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr); |
michael@0 | 545 | const Dyn *end_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr + pt_dyn->p_filesz); |
michael@0 | 546 | std::vector<Word> dt_needed; |
michael@0 | 547 | size_t symnum = 0; |
michael@0 | 548 | for (const Dyn *dyn = first_dyn; dyn < end_dyn && dyn->d_tag; dyn++) { |
michael@0 | 549 | switch (dyn->d_tag) { |
michael@0 | 550 | case DT_NEEDED: |
michael@0 | 551 | debug_dyn("DT_NEEDED", dyn); |
michael@0 | 552 | dt_needed.push_back(dyn->d_un.d_val); |
michael@0 | 553 | break; |
michael@0 | 554 | case DT_HASH: |
michael@0 | 555 | { |
michael@0 | 556 | debug_dyn("DT_HASH", dyn); |
michael@0 | 557 | const Word *hash_table_header = GetPtr<Word>(dyn->d_un.d_ptr); |
michael@0 | 558 | symnum = hash_table_header[1]; |
michael@0 | 559 | buckets.Init(&hash_table_header[2], hash_table_header[0]); |
michael@0 | 560 | chains.Init(&*buckets.end()); |
michael@0 | 561 | } |
michael@0 | 562 | break; |
michael@0 | 563 | case DT_STRTAB: |
michael@0 | 564 | debug_dyn("DT_STRTAB", dyn); |
michael@0 | 565 | strtab.Init(GetPtr(dyn->d_un.d_ptr)); |
michael@0 | 566 | break; |
michael@0 | 567 | case DT_SYMTAB: |
michael@0 | 568 | debug_dyn("DT_SYMTAB", dyn); |
michael@0 | 569 | symtab.Init(GetPtr(dyn->d_un.d_ptr)); |
michael@0 | 570 | break; |
michael@0 | 571 | case DT_SYMENT: |
michael@0 | 572 | debug_dyn("DT_SYMENT", dyn); |
michael@0 | 573 | if (dyn->d_un.d_val != sizeof(Sym)) { |
michael@0 | 574 | LOG("%s: Unsupported DT_SYMENT", GetPath()); |
michael@0 | 575 | return false; |
michael@0 | 576 | } |
michael@0 | 577 | break; |
michael@0 | 578 | case DT_TEXTREL: |
michael@0 | 579 | if (strcmp("libflashplayer.so", GetName()) == 0) { |
michael@0 | 580 | has_text_relocs = true; |
michael@0 | 581 | } else { |
michael@0 | 582 | LOG("%s: Text relocations are not supported", GetPath()); |
michael@0 | 583 | return false; |
michael@0 | 584 | } |
michael@0 | 585 | break; |
michael@0 | 586 | case DT_STRSZ: /* Ignored */ |
michael@0 | 587 | debug_dyn("DT_STRSZ", dyn); |
michael@0 | 588 | break; |
michael@0 | 589 | case UNSUPPORTED_RELOC(): |
michael@0 | 590 | case UNSUPPORTED_RELOC(SZ): |
michael@0 | 591 | case UNSUPPORTED_RELOC(ENT): |
michael@0 | 592 | LOG("%s: Unsupported relocations", GetPath()); |
michael@0 | 593 | return false; |
michael@0 | 594 | case RELOC(): |
michael@0 | 595 | debug_dyn(STR_RELOC(), dyn); |
michael@0 | 596 | relocations.Init(GetPtr(dyn->d_un.d_ptr)); |
michael@0 | 597 | break; |
michael@0 | 598 | case RELOC(SZ): |
michael@0 | 599 | debug_dyn(STR_RELOC(SZ), dyn); |
michael@0 | 600 | relocations.InitSize(dyn->d_un.d_val); |
michael@0 | 601 | break; |
michael@0 | 602 | case RELOC(ENT): |
michael@0 | 603 | debug_dyn(STR_RELOC(ENT), dyn); |
michael@0 | 604 | if (dyn->d_un.d_val != sizeof(Reloc)) { |
michael@0 | 605 | LOG("%s: Unsupported DT_RELENT", GetPath()); |
michael@0 | 606 | return false; |
michael@0 | 607 | } |
michael@0 | 608 | break; |
michael@0 | 609 | case DT_JMPREL: |
michael@0 | 610 | debug_dyn("DT_JMPREL", dyn); |
michael@0 | 611 | jumprels.Init(GetPtr(dyn->d_un.d_ptr)); |
michael@0 | 612 | break; |
michael@0 | 613 | case DT_PLTRELSZ: |
michael@0 | 614 | debug_dyn("DT_PLTRELSZ", dyn); |
michael@0 | 615 | jumprels.InitSize(dyn->d_un.d_val); |
michael@0 | 616 | break; |
michael@0 | 617 | case DT_PLTGOT: |
michael@0 | 618 | debug_dyn("DT_PLTGOT", dyn); |
michael@0 | 619 | break; |
michael@0 | 620 | case DT_INIT: |
michael@0 | 621 | debug_dyn("DT_INIT", dyn); |
michael@0 | 622 | init = dyn->d_un.d_ptr; |
michael@0 | 623 | break; |
michael@0 | 624 | case DT_INIT_ARRAY: |
michael@0 | 625 | debug_dyn("DT_INIT_ARRAY", dyn); |
michael@0 | 626 | init_array.Init(GetPtr(dyn->d_un.d_ptr)); |
michael@0 | 627 | break; |
michael@0 | 628 | case DT_INIT_ARRAYSZ: |
michael@0 | 629 | debug_dyn("DT_INIT_ARRAYSZ", dyn); |
michael@0 | 630 | init_array.InitSize(dyn->d_un.d_val); |
michael@0 | 631 | break; |
michael@0 | 632 | case DT_FINI: |
michael@0 | 633 | debug_dyn("DT_FINI", dyn); |
michael@0 | 634 | fini = dyn->d_un.d_ptr; |
michael@0 | 635 | break; |
michael@0 | 636 | case DT_FINI_ARRAY: |
michael@0 | 637 | debug_dyn("DT_FINI_ARRAY", dyn); |
michael@0 | 638 | fini_array.Init(GetPtr(dyn->d_un.d_ptr)); |
michael@0 | 639 | break; |
michael@0 | 640 | case DT_FINI_ARRAYSZ: |
michael@0 | 641 | debug_dyn("DT_FINI_ARRAYSZ", dyn); |
michael@0 | 642 | fini_array.InitSize(dyn->d_un.d_val); |
michael@0 | 643 | break; |
michael@0 | 644 | case DT_PLTREL: |
michael@0 | 645 | if (dyn->d_un.d_val != RELOC()) { |
michael@0 | 646 | LOG("%s: Error: DT_PLTREL is not " STR_RELOC(), GetPath()); |
michael@0 | 647 | return false; |
michael@0 | 648 | } |
michael@0 | 649 | break; |
michael@0 | 650 | case DT_FLAGS: |
michael@0 | 651 | { |
michael@0 | 652 | Addr flags = dyn->d_un.d_val; |
michael@0 | 653 | /* Treat as a DT_TEXTREL tag */ |
michael@0 | 654 | if (flags & DF_TEXTREL) { |
michael@0 | 655 | if (strcmp("libflashplayer.so", GetName()) == 0) { |
michael@0 | 656 | has_text_relocs = true; |
michael@0 | 657 | } else { |
michael@0 | 658 | LOG("%s: Text relocations are not supported", GetPath()); |
michael@0 | 659 | return false; |
michael@0 | 660 | } |
michael@0 | 661 | } |
michael@0 | 662 | /* we can treat this like having a DT_SYMBOLIC tag */ |
michael@0 | 663 | flags &= ~DF_SYMBOLIC; |
michael@0 | 664 | if (flags) |
michael@0 | 665 | LOG("%s: Warning: unhandled flags #%" PRIxAddr" not handled", |
michael@0 | 666 | GetPath(), flags); |
michael@0 | 667 | } |
michael@0 | 668 | break; |
michael@0 | 669 | case DT_SONAME: /* Should match GetName(), but doesn't matter */ |
michael@0 | 670 | case DT_SYMBOLIC: /* Indicates internal symbols should be looked up in |
michael@0 | 671 | * the library itself first instead of the executable, |
michael@0 | 672 | * which is actually what this linker does by default */ |
michael@0 | 673 | case RELOC(COUNT): /* Indicates how many relocations are relative, which |
michael@0 | 674 | * is usually used to skip relocations on prelinked |
michael@0 | 675 | * libraries. They are not supported anyways. */ |
michael@0 | 676 | case UNSUPPORTED_RELOC(COUNT): /* This should error out, but it doesn't |
michael@0 | 677 | * really matter. */ |
michael@0 | 678 | case DT_FLAGS_1: /* Additional linker-internal flags that we don't care about. See |
michael@0 | 679 | * DF_1_* values in src/include/elf/common.h in binutils. */ |
michael@0 | 680 | case DT_VERSYM: /* DT_VER* entries are used for symbol versioning, which */ |
michael@0 | 681 | case DT_VERDEF: /* this linker doesn't support yet. */ |
michael@0 | 682 | case DT_VERDEFNUM: |
michael@0 | 683 | case DT_VERNEED: |
michael@0 | 684 | case DT_VERNEEDNUM: |
michael@0 | 685 | /* Ignored */ |
michael@0 | 686 | break; |
michael@0 | 687 | default: |
michael@0 | 688 | LOG("%s: Warning: dynamic header type #%" PRIxAddr" not handled", |
michael@0 | 689 | GetPath(), dyn->d_tag); |
michael@0 | 690 | } |
michael@0 | 691 | } |
michael@0 | 692 | |
michael@0 | 693 | if (!buckets || !symnum) { |
michael@0 | 694 | LOG("%s: Missing or broken DT_HASH", GetPath()); |
michael@0 | 695 | return false; |
michael@0 | 696 | } |
michael@0 | 697 | if (!strtab) { |
michael@0 | 698 | LOG("%s: Missing DT_STRTAB", GetPath()); |
michael@0 | 699 | return false; |
michael@0 | 700 | } |
michael@0 | 701 | if (!symtab) { |
michael@0 | 702 | LOG("%s: Missing DT_SYMTAB", GetPath()); |
michael@0 | 703 | return false; |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | /* Load dependent libraries */ |
michael@0 | 707 | for (size_t i = 0; i < dt_needed.size(); i++) { |
michael@0 | 708 | const char *name = strtab.GetStringAt(dt_needed[i]); |
michael@0 | 709 | RefPtr<LibHandle> handle = |
michael@0 | 710 | ElfLoader::Singleton.Load(name, RTLD_GLOBAL | RTLD_LAZY, this); |
michael@0 | 711 | if (!handle) |
michael@0 | 712 | return false; |
michael@0 | 713 | dependencies.push_back(handle); |
michael@0 | 714 | } |
michael@0 | 715 | |
michael@0 | 716 | return true; |
michael@0 | 717 | } |
michael@0 | 718 | |
michael@0 | 719 | bool |
michael@0 | 720 | CustomElf::Relocate() |
michael@0 | 721 | { |
michael@0 | 722 | DEBUG_LOG("Relocate %s @%p", GetPath(), static_cast<void *>(base)); |
michael@0 | 723 | uint32_t symtab_index = (uint32_t) -1; |
michael@0 | 724 | void *symptr = nullptr; |
michael@0 | 725 | for (Array<Reloc>::iterator rel = relocations.begin(); |
michael@0 | 726 | rel < relocations.end(); ++rel) { |
michael@0 | 727 | /* Location of the relocation */ |
michael@0 | 728 | void *ptr = GetPtr(rel->r_offset); |
michael@0 | 729 | |
michael@0 | 730 | /* R_*_RELATIVE relocations apply directly at the given location */ |
michael@0 | 731 | if (ELF_R_TYPE(rel->r_info) == R_RELATIVE) { |
michael@0 | 732 | *(void **) ptr = GetPtr(rel->GetAddend(base)); |
michael@0 | 733 | continue; |
michael@0 | 734 | } |
michael@0 | 735 | /* Other relocation types need a symbol resolution */ |
michael@0 | 736 | /* Avoid symbol resolution when it's the same symbol as last iteration */ |
michael@0 | 737 | if (symtab_index != ELF_R_SYM(rel->r_info)) { |
michael@0 | 738 | symtab_index = ELF_R_SYM(rel->r_info); |
michael@0 | 739 | const Sym sym = symtab[symtab_index]; |
michael@0 | 740 | if (sym.st_shndx != SHN_UNDEF) { |
michael@0 | 741 | symptr = GetPtr(sym.st_value); |
michael@0 | 742 | } else { |
michael@0 | 743 | /* TODO: handle symbol resolving to nullptr vs. being undefined. */ |
michael@0 | 744 | symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name)); |
michael@0 | 745 | } |
michael@0 | 746 | } |
michael@0 | 747 | |
michael@0 | 748 | if (symptr == nullptr) |
michael@0 | 749 | LOG("%s: Warning: relocation to NULL @0x%08" PRIxAddr, |
michael@0 | 750 | GetPath(), rel->r_offset); |
michael@0 | 751 | |
michael@0 | 752 | /* Apply relocation */ |
michael@0 | 753 | switch (ELF_R_TYPE(rel->r_info)) { |
michael@0 | 754 | case R_GLOB_DAT: |
michael@0 | 755 | /* R_*_GLOB_DAT relocations simply use the symbol value */ |
michael@0 | 756 | *(void **) ptr = symptr; |
michael@0 | 757 | break; |
michael@0 | 758 | case R_ABS: |
michael@0 | 759 | /* R_*_ABS* relocations add the relocation added to the symbol value */ |
michael@0 | 760 | *(const char **) ptr = (const char *)symptr + rel->GetAddend(base); |
michael@0 | 761 | break; |
michael@0 | 762 | default: |
michael@0 | 763 | LOG("%s: Unsupported relocation type: 0x%" PRIxAddr, |
michael@0 | 764 | GetPath(), ELF_R_TYPE(rel->r_info)); |
michael@0 | 765 | return false; |
michael@0 | 766 | } |
michael@0 | 767 | } |
michael@0 | 768 | return true; |
michael@0 | 769 | } |
michael@0 | 770 | |
michael@0 | 771 | bool |
michael@0 | 772 | CustomElf::RelocateJumps() |
michael@0 | 773 | { |
michael@0 | 774 | /* TODO: Dynamic symbol resolution */ |
michael@0 | 775 | for (Array<Reloc>::iterator rel = jumprels.begin(); |
michael@0 | 776 | rel < jumprels.end(); ++rel) { |
michael@0 | 777 | /* Location of the relocation */ |
michael@0 | 778 | void *ptr = GetPtr(rel->r_offset); |
michael@0 | 779 | |
michael@0 | 780 | /* Only R_*_JMP_SLOT relocations are expected */ |
michael@0 | 781 | if (ELF_R_TYPE(rel->r_info) != R_JMP_SLOT) { |
michael@0 | 782 | LOG("%s: Jump relocation type mismatch", GetPath()); |
michael@0 | 783 | return false; |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | /* TODO: Avoid code duplication with the relocations above */ |
michael@0 | 787 | const Sym sym = symtab[ELF_R_SYM(rel->r_info)]; |
michael@0 | 788 | void *symptr; |
michael@0 | 789 | if (sym.st_shndx != SHN_UNDEF) |
michael@0 | 790 | symptr = GetPtr(sym.st_value); |
michael@0 | 791 | else |
michael@0 | 792 | symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name)); |
michael@0 | 793 | |
michael@0 | 794 | if (symptr == nullptr) { |
michael@0 | 795 | LOG("%s: %s: relocation to NULL @0x%08" PRIxAddr " for symbol \"%s\"", |
michael@0 | 796 | GetPath(), |
michael@0 | 797 | (ELF_ST_BIND(sym.st_info) == STB_WEAK) ? "Warning" : "Error", |
michael@0 | 798 | rel->r_offset, strtab.GetStringAt(sym.st_name)); |
michael@0 | 799 | if (ELF_ST_BIND(sym.st_info) != STB_WEAK) |
michael@0 | 800 | return false; |
michael@0 | 801 | } |
michael@0 | 802 | /* Apply relocation */ |
michael@0 | 803 | *(void **) ptr = symptr; |
michael@0 | 804 | } |
michael@0 | 805 | return true; |
michael@0 | 806 | } |
michael@0 | 807 | |
michael@0 | 808 | bool |
michael@0 | 809 | CustomElf::CallInit() |
michael@0 | 810 | { |
michael@0 | 811 | if (init) |
michael@0 | 812 | CallFunction(init); |
michael@0 | 813 | |
michael@0 | 814 | for (Array<void *>::iterator it = init_array.begin(); |
michael@0 | 815 | it < init_array.end(); ++it) { |
michael@0 | 816 | /* Android x86 NDK wrongly puts 0xffffffff in INIT_ARRAY */ |
michael@0 | 817 | if (*it && *it != reinterpret_cast<void *>(-1)) |
michael@0 | 818 | CallFunction(*it); |
michael@0 | 819 | } |
michael@0 | 820 | initialized = true; |
michael@0 | 821 | return true; |
michael@0 | 822 | } |
michael@0 | 823 | |
michael@0 | 824 | void |
michael@0 | 825 | CustomElf::CallFini() |
michael@0 | 826 | { |
michael@0 | 827 | if (!initialized) |
michael@0 | 828 | return; |
michael@0 | 829 | for (Array<void *>::reverse_iterator it = fini_array.rbegin(); |
michael@0 | 830 | it < fini_array.rend(); ++it) { |
michael@0 | 831 | /* Android x86 NDK wrongly puts 0xffffffff in FINI_ARRAY */ |
michael@0 | 832 | if (*it && *it != reinterpret_cast<void *>(-1)) |
michael@0 | 833 | CallFunction(*it); |
michael@0 | 834 | } |
michael@0 | 835 | if (fini) |
michael@0 | 836 | CallFunction(fini); |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | Mappable * |
michael@0 | 840 | CustomElf::GetMappable() const |
michael@0 | 841 | { |
michael@0 | 842 | if (!mappable) |
michael@0 | 843 | return nullptr; |
michael@0 | 844 | if (mappable->GetKind() == Mappable::MAPPABLE_EXTRACT_FILE) |
michael@0 | 845 | return mappable; |
michael@0 | 846 | return ElfLoader::GetMappableFromPath(GetPath()); |
michael@0 | 847 | } |