mozglue/linker/CustomElf.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial