Bug 683127 part 7 - Use a custom Elf linker for libraries given with an absolute path name. r=sewardj
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 20 Jan 2012 09:48:44 +0100
changeset 84939 54a8b1b25477d04ecfae8f13775ec5b25a7c27de
parent 84938 fe5a5abec7ed9d6546e5b715ec4ba009f119185a
child 84940 bd752f4935d978c6e81f04c632ea054701f64a08
push idunknown
push userunknown
push dateunknown
reviewerssewardj
bugs683127
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 683127 part 7 - Use a custom Elf linker for libraries given with an absolute path name. r=sewardj
mozglue/linker/CustomElf.cpp
mozglue/linker/CustomElf.h
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
mozglue/linker/Makefile.in
mozglue/linker/Utils.h
mozglue/tests/TestZip.cpp
new file mode 100644
--- /dev/null
+++ b/mozglue/linker/CustomElf.cpp
@@ -0,0 +1,632 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <cstring>
+#include <sys/mman.h>
+#include <vector>
+#include <dlfcn.h>
+#include "CustomElf.h"
+#include "Logging.h"
+
+using namespace Elf;
+using namespace mozilla;
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+#ifndef PAGE_MASK
+#define PAGE_MASK (~ (PAGE_SIZE - 1))
+#endif
+
+/* TODO: Fill ElfLoader::Singleton.lastError on errors. */
+
+/* Function used to report library mappings from the custom linker to Gecko
+ * crash reporter */
+#ifdef ANDROID
+extern "C" {
+  void report_mapping(char *name, void *base, uint32_t len, uint32_t offset);
+}
+#else
+#define report_mapping(...)
+#endif
+
+const Ehdr *Ehdr::validate(const void *buf)
+{
+  if (!buf || buf == MAP_FAILED)
+    return NULL;
+
+  const Ehdr *ehdr = reinterpret_cast<const Ehdr *>(buf);
+
+  /* Only support ELF executables or libraries for the host system */
+  if (memcmp(ELFMAG, &ehdr->e_ident, SELFMAG) ||
+      ehdr->e_ident[EI_CLASS] != ELFCLASS ||
+      ehdr->e_ident[EI_DATA] != ELFDATA ||
+      ehdr->e_ident[EI_VERSION] != 1 ||
+      (ehdr->e_ident[EI_OSABI] != ELFOSABI && ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) ||
+#ifdef EI_ABIVERSION
+      ehdr->e_ident[EI_ABIVERSION] != ELFABIVERSION ||
+#endif
+      (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) ||
+      ehdr->e_machine != ELFMACHINE ||
+      ehdr->e_version != 1 ||
+      ehdr->e_phentsize != sizeof(Phdr))
+    return NULL;
+
+  return ehdr;
+}
+
+namespace {
+
+void debug_phdr(const char *type, const Phdr *phdr)
+{
+  debug("%s @0x%08" PRIxAddr " ("
+        "filesz: 0x%08" PRIxAddr ", "
+        "memsz: 0x%08" PRIxAddr ", "
+        "offset: 0x%08" PRIxAddr ", "
+        "flags: %c%c%c)",
+        type, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz,
+        phdr->p_offset, phdr->p_flags & PF_R ? 'r' : '-',
+        phdr->p_flags & PF_W ? 'w' : '-', phdr->p_flags & PF_X ? 'x' : '-');
+}
+
+} /* anonymous namespace */
+
+TemporaryRef<LibHandle>
+CustomElf::Load(int fd, const char *path, int flags)
+{
+  debug("CustomElf::Load(\"%s\", %x) = ...", path, flags);
+  if (fd == -1)
+    return NULL;
+  /* Keeping a RefPtr of the CustomElf is going to free the appropriate
+   * resources when returning NULL */
+  RefPtr<CustomElf> elf = new CustomElf(fd, path);
+  /* Map the first page of the Elf object to access Elf and program headers */
+  MappedPtr ehdr_raw(mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0),
+                      PAGE_SIZE);
+  if (ehdr_raw == MAP_FAILED)
+    return NULL;
+
+  const Ehdr *ehdr = Ehdr::validate(ehdr_raw);
+  if (!ehdr)
+    return NULL;
+
+  /* Scan Elf Program Headers and gather some information about them */
+  std::vector<const Phdr *> pt_loads;
+  Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest
+  Addr max_vaddr = 0;         // virtual address used by this Elf.
+  const Phdr *dyn = NULL;
+
+  const Phdr *first_phdr = reinterpret_cast<const Phdr *>(
+                           reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff);
+  const Phdr *end_phdr = &first_phdr[ehdr->e_phnum];
+
+  for (const Phdr *phdr = first_phdr; phdr < end_phdr; phdr++) {
+    switch (phdr->p_type) {
+      case PT_LOAD:
+        debug_phdr("PT_LOAD", phdr);
+        pt_loads.push_back(phdr);
+        if (phdr->p_vaddr < min_vaddr)
+          min_vaddr = phdr->p_vaddr;
+        if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
+          max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+        break;
+      case PT_DYNAMIC:
+        debug_phdr("PT_DYNAMIC", phdr);
+        if (!dyn) {
+          dyn = phdr;
+        } else {
+          log("%s: Multiple PT_DYNAMIC segments detected", elf->GetPath());
+          return NULL;
+        }
+        break;
+      case PT_TLS:
+        debug_phdr("PT_TLS", phdr);
+        if (phdr->p_memsz) {
+          log("%s: TLS is not supported", elf->GetPath());
+          return NULL;
+        }
+        break;
+      case PT_GNU_STACK:
+        debug_phdr("PT_GNU_STACK", phdr);
+// Skip on Android until bug 706116 is fixed
+#ifndef ANDROID
+        if (phdr->p_flags & PF_X) {
+          log("%s: Executable stack is not supported", elf->GetPath());
+          return NULL;
+        }
+#endif
+        break;
+      default:
+        debug("%s: Warning: program header type #%d not handled",
+              elf->GetPath(), phdr->p_type);
+    }
+  }
+
+  if (min_vaddr != 0) {
+    log("%s: Unsupported minimal virtual address: 0x%08" PRIxAddr,
+        elf->GetPath(), min_vaddr);
+    return NULL;
+  }
+  if (!dyn) {
+    log("%s: No PT_DYNAMIC segment found", elf->GetPath());
+    return NULL;
+  }
+
+  /* Reserve enough memory to map the complete virtual address space for this
+   * library. */
+  elf->base.Init(mmap(NULL, max_vaddr, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
+                      -1, 0), max_vaddr);
+  if (elf->base == MAP_FAILED) {
+    log("%s: Failed to mmap", elf->GetPath());
+    return NULL;
+  }
+
+  /* Load and initialize library */
+  for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+       it < pt_loads.end(); ++it)
+    if (!elf->LoadSegment(*it))
+      return NULL;
+
+  report_mapping(const_cast<char *>(elf->GetName()), elf->base,
+                 (max_vaddr + PAGE_SIZE - 1) & PAGE_MASK, 0);
+
+  if (!elf->InitDyn(dyn))
+    return NULL;
+
+  debug("CustomElf::Load(\"%s\", %x) = %p", path, flags,
+        static_cast<void *>(elf));
+  return elf;
+}
+
+CustomElf::~CustomElf()
+{
+  debug("CustomElf::~CustomElf(%p [\"%s\"])",
+        reinterpret_cast<void *>(this), GetPath());
+  CallFini();
+  /* Normally, __cxa_finalize is called by the .fini function. However,
+   * Android NDK before r6b doesn't do that. Our wrapped cxa_finalize only
+   * calls destructors once, so call it in all cases. */
+  ElfLoader::__wrap_cxa_finalize(this);
+}
+
+namespace {
+
+/**
+ * Hash function for symbol lookup, as defined in ELF standard for System V
+ */
+unsigned long
+ElfHash(const char *symbol)
+{
+  const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol);
+  unsigned long h = 0, g;
+  while (*sym) {
+    h = (h << 4) + *sym++;
+    if ((g = h & 0xf0000000))
+      h ^= g >> 24;
+    h &= ~g;
+  }
+  return h;
+}
+
+} /* anonymous namespace */
+
+void *
+CustomElf::GetSymbolPtr(const char *symbol) const
+{
+  return GetSymbolPtr(symbol, ElfHash(symbol));
+}
+
+void *
+CustomElf::GetSymbolPtr(const char *symbol, unsigned long hash) const
+{
+  const Sym *sym = GetSymbol(symbol, hash);
+  void *ptr = NULL;
+  if (sym && sym->st_shndx != SHN_UNDEF)
+    ptr = GetPtr(sym->st_value);
+  debug("CustomElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p",
+        reinterpret_cast<const void *>(this), GetPath(), symbol, ptr);
+  return ptr;
+}
+
+void *
+CustomElf::GetSymbolPtrInDeps(const char *symbol) const
+{
+  /* Resolve dlopen and related functions to point to ours */
+  if (symbol[0] == 'd' && symbol[1] == 'l') {
+    if (strcmp(symbol + 2, "open") == 0)
+      return FunctionPtr(__wrap_dlopen);
+    if (strcmp(symbol + 2, "error") == 0)
+      return FunctionPtr(__wrap_dlerror);
+    if (strcmp(symbol + 2, "close") == 0)
+      return FunctionPtr(__wrap_dlclose);
+    if (strcmp(symbol + 2, "sym") == 0)
+      return FunctionPtr(__wrap_dlsym);
+    if (strcmp(symbol + 2, "addr") == 0)
+      return FunctionPtr(__wrap_dladdr);
+  } else if (symbol[0] == '_' && symbol[1] == '_') {
+  /* Resolve a few C++ ABI specific functions to point to ours */
+#ifdef __ARM_EABI__
+    if (strcmp(symbol + 2, "aeabi_atexit") == 0)
+      return FunctionPtr(&ElfLoader::__wrap_aeabi_atexit);
+#else
+    if (strcmp(symbol + 2, "cxa_atexit") == 0)
+      return FunctionPtr(&ElfLoader::__wrap_cxa_atexit);
+#endif
+    if (strcmp(symbol + 2, "cxa_finalize") == 0)
+      return FunctionPtr(&ElfLoader::__wrap_cxa_finalize);
+    if (strcmp(symbol + 2, "dso_handle") == 0)
+      return const_cast<CustomElf *>(this);
+  }
+
+  void *sym;
+  /* Search the symbol in the main program. Note this also tries all libraries
+   * the system linker will have loaded RTLD_GLOBAL. Unfortunately, that doesn't
+   * work with bionic, but its linker doesn't normally search the main binary
+   * anyways. Moreover, on android, the main binary is dalvik. */
+#ifdef __GLIBC__
+  sym = dlsym(RTLD_DEFAULT, symbol);
+  debug("dlsym(RTLD_DEFAULT, \"%s\") = %p", symbol, sym);
+  if (sym)
+    return sym;
+#endif
+
+  /* Then search the symbol in our dependencies. Since we already searched in
+   * libraries the system linker loaded, skip those (on glibc systems). We
+   * also assume the symbol is to be found in one of the dependent libraries
+   * directly, not in their own dependent libraries. Building libraries with
+   * --no-allow-shlib-undefined ensures such indirect symbol dependency don't
+   * happen. */
+  unsigned long hash = ElfHash(symbol);
+  for (std::vector<RefPtr<LibHandle> >::const_iterator it = dependencies.begin();
+       it < dependencies.end(); ++it) {
+    if (!(*it)->IsSystemElf()) {
+      sym = reinterpret_cast<CustomElf *>((*it).get())->GetSymbolPtr(symbol, hash);
+#ifndef __GLIBC__
+    } else {
+      sym = (*it)->GetSymbolPtr(symbol);
+#endif
+    }
+    if (sym)
+      return sym;
+  }
+  return NULL;
+}
+
+const Sym *
+CustomElf::GetSymbol(const char *symbol, unsigned long hash) const
+{
+  /* Search symbol with the buckets and chains tables.
+   * The hash computed from the symbol name gives an index in the buckets
+   * table. The corresponding value in the bucket table is an index in the
+   * symbols table and in the chains table.
+   * If the corresponding symbol in the symbols table matches, we're done.
+   * Otherwise, the corresponding value in the chains table is a new index
+   * in both tables, which corresponding symbol is tested and so on and so
+   * forth */
+  size_t bucket = hash % buckets.numElements();
+  for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) {
+    if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name)))
+      continue;
+    return &symtab[y];
+  }
+  return NULL;
+}
+
+bool
+CustomElf::Contains(void *addr) const
+{
+  return base.Contains(addr);
+}
+
+bool
+CustomElf::LoadSegment(const Phdr *pt_load) const
+{
+  if (pt_load->p_type != PT_LOAD) {
+    debug("%s: Elf::LoadSegment only takes PT_LOAD program headers", GetPath());
+    return false;;
+  }
+
+  int prot = ((pt_load->p_flags & PF_X) ? PROT_EXEC : 0) |
+             ((pt_load->p_flags & PF_W) ? PROT_WRITE : 0) |
+             ((pt_load->p_flags & PF_R) ? PROT_READ : 0);
+
+  /* Mmap at page boundary */
+  Addr page_offset = pt_load->p_vaddr & ~PAGE_MASK;
+  void *where = GetPtr(pt_load->p_vaddr - page_offset);
+  debug("%s: Loading segment @%p %c%c%c", GetPath(), where,
+                                          prot & PROT_READ ? 'r' : '-',
+                                          prot & PROT_WRITE ? 'w' : '-',
+                                          prot & PROT_EXEC ? 'x' : '-');
+  void *mapped = mmap(where, pt_load->p_filesz + page_offset,
+                      prot, MAP_PRIVATE | MAP_FIXED, fd,
+                      pt_load->p_offset - page_offset);
+  if (mapped != where) {
+    if (mapped == MAP_FAILED) {
+      log("%s: Failed to mmap", GetPath());
+    } else {
+      log("%s: Didn't map at the expected location (wanted: %p, got: %p)",
+          GetPath(), where, mapped);
+    }
+    return false;
+  }
+
+  /* When p_memsz is greater than p_filesz, we need to have nulled out memory
+   * after p_filesz and before p_memsz.
+   * We first null out bytes after p_filesz and up to the end of the page
+   * p_filesz is in. */
+  Addr end_offset = pt_load->p_filesz + page_offset;
+  if ((prot & PROT_WRITE) && (end_offset & ~PAGE_MASK)) {
+    memset(reinterpret_cast<char *>(mapped) + end_offset,
+           0, PAGE_SIZE - (end_offset & ~PAGE_MASK));
+  }
+  /* Above the end of that page, and up to p_memsz, we already have nulled out
+   * memory because we mapped anonymous memory on the whole library virtual
+   * address space. We just need to adjust this anonymous memory protection
+   * flags. */
+  if (pt_load->p_memsz > pt_load->p_filesz) {
+    Addr file_end = pt_load->p_vaddr + pt_load->p_filesz;
+    Addr mem_end = pt_load->p_vaddr + pt_load->p_memsz;
+    Addr next_page = (file_end & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
+    if (mem_end > next_page) {
+      if (mprotect(GetPtr(next_page), mem_end - next_page, prot) < 0) {
+        log("%s: Failed to mprotect", GetPath());
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+namespace {
+
+void debug_dyn(const char *type, const Dyn *dyn)
+{
+  debug("%s 0x%08" PRIxAddr, type, dyn->d_un.d_val);
+}
+
+} /* anonymous namespace */
+
+bool
+CustomElf::InitDyn(const Phdr *pt_dyn)
+{
+  /* Scan PT_DYNAMIC segment and gather some information */
+  const Dyn *first_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr);
+  const Dyn *end_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr + pt_dyn->p_filesz);
+  std::vector<Word> dt_needed;
+  size_t symnum = 0;
+  for (const Dyn *dyn = first_dyn; dyn < end_dyn && dyn->d_tag; dyn++) {
+    switch (dyn->d_tag) {
+      case DT_NEEDED:
+        debug_dyn("DT_NEEDED", dyn);
+        dt_needed.push_back(dyn->d_un.d_val);
+        break;
+      case DT_HASH:
+        {
+          debug_dyn("DT_HASH", dyn);
+          const Word *hash_table_header = GetPtr<Word>(dyn->d_un.d_ptr);
+          symnum = hash_table_header[1];
+          buckets.Init(&hash_table_header[2], hash_table_header[0]);
+          chains.Init(&*buckets.end());
+        }
+        break;
+      case DT_STRTAB:
+        debug_dyn("DT_STRTAB", dyn);
+        strtab.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case DT_SYMTAB:
+        debug_dyn("DT_SYMTAB", dyn);
+        symtab.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case DT_SYMENT:
+        debug_dyn("DT_SYMENT", dyn);
+        if (dyn->d_un.d_val != sizeof(Sym)) {
+          log("%s: Unsupported DT_SYMENT", GetPath());
+          return false;
+        }
+        break;
+      case DT_TEXTREL:
+        log("%s: Text relocations are not supported", GetPath());
+        return false;
+      case DT_STRSZ: /* Ignored */
+        debug_dyn("DT_STRSZ", dyn);
+        break;
+      case UNSUPPORTED_RELOC():
+      case UNSUPPORTED_RELOC(SZ):
+      case UNSUPPORTED_RELOC(ENT):
+        log("%s: Unsupported relocations", GetPath());
+        return false;
+      case RELOC():
+        debug_dyn(STR_RELOC(), dyn);
+        relocations.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case RELOC(SZ):
+        debug_dyn(STR_RELOC(SZ), dyn);
+        relocations.InitSize(dyn->d_un.d_val);
+        break;
+      case RELOC(ENT):
+        debug_dyn(STR_RELOC(ENT), dyn);
+        if (dyn->d_un.d_val != sizeof(Reloc)) {
+          log("%s: Unsupported DT_RELENT", GetPath());
+          return false;
+        }
+        break;
+      case DT_JMPREL:
+        debug_dyn("DT_JMPREL", dyn);
+        jumprels.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case DT_PLTRELSZ:
+        debug_dyn("DT_PLTRELSZ", dyn);
+        jumprels.InitSize(dyn->d_un.d_val);
+        break;
+      case DT_PLTGOT:
+        debug_dyn("DT_PLTGOT", dyn);
+        break;
+      case DT_INIT:
+        debug_dyn("DT_INIT", dyn);
+        init = dyn->d_un.d_ptr;
+        break;
+      case DT_INIT_ARRAY:
+        debug_dyn("DT_INIT_ARRAY", dyn);
+        init_array.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case DT_INIT_ARRAYSZ:
+        debug_dyn("DT_INIT_ARRAYSZ", dyn);
+        init_array.InitSize(dyn->d_un.d_val);
+        break;
+      case DT_FINI:
+        debug_dyn("DT_FINI", dyn);
+        fini = dyn->d_un.d_ptr;
+        break;
+      case DT_FINI_ARRAY:
+        debug_dyn("DT_FINI_ARRAY", dyn);
+        fini_array.Init(GetPtr(dyn->d_un.d_ptr));
+        break;
+      case DT_FINI_ARRAYSZ:
+        debug_dyn("DT_FINI_ARRAYSZ", dyn);
+        fini_array.InitSize(dyn->d_un.d_val);
+        break;
+      default:
+        log("%s: Warning: dynamic header type #%" PRIxAddr" not handled",
+            GetPath(), dyn->d_tag);
+    }
+  }
+
+  if (!buckets || !symnum) {
+    log("%s: Missing or broken DT_HASH", GetPath());
+    return false;
+  }
+  if (!strtab) {
+    log("%s: Missing DT_STRTAB", GetPath());
+    return false;
+  }
+  if (!symtab) {
+    log("%s: Missing DT_SYMTAB", GetPath());
+    return false;
+  }
+
+  /* Load dependent libraries */
+  for (size_t i = 0; i < dt_needed.size(); i++) {
+    const char *name = strtab.GetStringAt(dt_needed[i]);
+    RefPtr<LibHandle> handle =
+      ElfLoader::Singleton.Load(name, RTLD_GLOBAL | RTLD_LAZY, this);
+    if (!handle)
+      return false;
+    dependencies.push_back(handle);
+  }
+
+  /* Finish initialization */
+  return Relocate() && RelocateJumps() && CallInit();
+}
+
+bool
+CustomElf::Relocate()
+{
+  debug("Relocate %s @%p", GetPath(), static_cast<void *>(base));
+  for (Array<Reloc>::iterator rel = relocations.begin();
+       rel < relocations.end(); ++rel) {
+    /* Location of the relocation */
+    void *ptr = GetPtr(rel->r_offset);
+
+    /* R_*_RELATIVE relocations apply directly at the given location */
+    if (ELF_R_TYPE(rel->r_info) == R_RELATIVE) {
+      *(void **) ptr = GetPtr(rel->GetAddend(base));
+      continue;
+    }
+    /* Other relocation types need a symbol resolution */
+    const Sym sym = symtab[ELF_R_SYM(rel->r_info)];
+    void *symptr;
+    if (sym.st_shndx != SHN_UNDEF) {
+      symptr = GetPtr(sym.st_value);
+    } else {
+      /* TODO: avoid symbol resolution when it's the same symbol as last
+       * iteration */
+      /* TODO: handle symbol resolving to NULL vs. being undefined. */
+      symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
+    }
+
+    if (symptr == NULL)
+      log("%s: Warning: relocation to NULL @0x%08" PRIxAddr,
+          GetPath(), rel->r_offset);
+
+    /* Apply relocation */
+    switch (ELF_R_TYPE(rel->r_info)) {
+    case R_GLOB_DAT:
+      /* R_*_GLOB_DAT relocations simply use the symbol value */
+      *(void **) ptr = symptr;
+      break;
+    case R_ABS:
+      /* R_*_ABS* relocations add the relocation added to the symbol value */
+      *(const char **) ptr = (const char *)symptr + rel->GetAddend(base);
+      break;
+    default:
+      log("%s: Unsupported relocation type: 0x%" PRIxAddr,
+          GetPath(), ELF_R_TYPE(rel->r_info));
+      return false;
+    }
+  }
+  return true;
+}
+
+bool
+CustomElf::RelocateJumps()
+{
+  /* TODO: Dynamic symbol resolution */
+  for (Array<Reloc>::iterator rel = jumprels.begin();
+       rel < jumprels.end(); ++rel) {
+    /* Location of the relocation */
+    void *ptr = GetPtr(rel->r_offset);
+
+    /* Only R_*_JMP_SLOT relocations are expected */
+    if (ELF_R_TYPE(rel->r_info) != R_JMP_SLOT) {
+      log("%s: Jump relocation type mismatch", GetPath());
+      return false;
+    }
+
+    /* TODO: Avoid code duplication with the relocations above */
+    const Sym sym = symtab[ELF_R_SYM(rel->r_info)];
+    void *symptr;
+    if (sym.st_shndx != SHN_UNDEF)
+      symptr = GetPtr(sym.st_value);
+    else
+      symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
+
+    if (symptr == NULL) {
+      log("%s: Error: relocation to NULL @0x%08" PRIxAddr, GetPath(), rel->r_offset);
+      return false;
+    }
+    /* Apply relocation */
+    *(void **) ptr = symptr;
+  }
+  return true;
+}
+
+bool
+CustomElf::CallInit()
+{
+  if (init)
+    CallFunction(init);
+
+  for (Array<void *>::iterator it = init_array.begin();
+       it < init_array.end(); ++it) {
+    if (*it)
+      CallFunction(*it);
+  }
+  initialized = true;
+  return true;
+}
+
+void
+CustomElf::CallFini()
+{
+  if (!initialized)
+    return;
+  for (Array<void *>::iterator it = fini_array.begin();
+       it < fini_array.end(); ++it) {
+    if (*it)
+      CallFunction(*it);
+  }
+  if (fini)
+    CallFunction(fini);
+}
new file mode 100644
--- /dev/null
+++ b/mozglue/linker/CustomElf.h
@@ -0,0 +1,372 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CustomElf_h
+#define CustomElf_h
+
+/**
+ * Android system headers have two different elf.h file. The one under linux/
+ * is the most complete.
+ */
+#ifdef ANDROID
+#include <linux/elf.h>
+#else
+#include <elf.h>
+#endif
+#include <endian.h>
+#include "ElfLoader.h"
+#include "Logging.h"
+
+/**
+ * Generic ELF macros for the target system
+ */
+#ifdef HAVE_64BIT_OS
+#define Elf_(type) Elf64_ ## type
+#define ELFCLASS ELFCLASS64
+#define ELF_R_TYPE ELF64_R_TYPE
+#define ELF_R_SYM ELF64_R_SYM
+#define PRIxAddr "lx"
+#else
+#define Elf_(type) Elf32_ ## type
+#define ELFCLASS ELFCLASS32
+#define ELF_R_TYPE ELF32_R_TYPE
+#define ELF_R_SYM ELF32_R_SYM
+#define PRIxAddr "x"
+#endif
+
+#ifndef __BYTE_ORDER
+#error Cannot find endianness
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ELFDATA ELFDATA2LSB
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define ELFDATA ELFDATA2MSB
+#endif
+
+#ifdef __linux__
+#define ELFOSABI ELFOSABI_LINUX
+#ifdef EI_ABIVERSION
+#define ELFABIVERSION 0
+#endif
+#else
+#error Unknown ELF OSABI
+#endif
+
+#if defined(__i386__)
+#define ELFMACHINE EM_386
+
+// Doing this way probably doesn't scale to other architectures
+#define R_ABS R_386_32
+#define R_GLOB_DAT R_386_GLOB_DAT
+#define R_JMP_SLOT R_386_JMP_SLOT
+#define R_RELATIVE R_386_RELATIVE
+#define RELOC(n) DT_REL ## n
+#define UNSUPPORTED_RELOC(n) DT_RELA ## n
+#define STR_RELOC(n) "DT_REL" # n
+#define Reloc Rel
+
+#elif defined(__x86_64__)
+#define ELFMACHINE EM_X86_64
+
+#define R_ABS R_X86_64_64
+#define R_GLOB_DAT R_X86_64_GLOB_DAT
+#define R_JMP_SLOT R_X86_64_JUMP_SLOT
+#define R_RELATIVE R_X86_64_RELATIVE
+#define RELOC(n) DT_RELA ## n
+#define UNSUPPORTED_RELOC(n) DT_REL ## n
+#define STR_RELOC(n) "DT_RELA" # n
+#define Reloc Rela
+
+#elif defined(__arm__)
+#define ELFMACHINE EM_ARM
+
+#ifndef R_ARM_ABS32
+#define R_ARM_ABS32 2
+#endif
+#ifndef R_ARM_GLOB_DAT
+#define R_ARM_GLOB_DAT 21
+#endif
+#ifndef R_ARM_JUMP_SLOT
+#define R_ARM_JUMP_SLOT 22
+#endif
+#ifndef R_ARM_RELATIVE
+#define R_ARM_RELATIVE 23
+#endif
+
+#define R_ABS R_ARM_ABS32
+#define R_GLOB_DAT R_ARM_GLOB_DAT
+#define R_JMP_SLOT R_ARM_JUMP_SLOT
+#define R_RELATIVE R_ARM_RELATIVE
+#define RELOC(n) DT_REL ## n
+#define UNSUPPORTED_RELOC(n) DT_RELA ## n
+#define STR_RELOC(n) "DT_REL" # n
+#define Reloc Rel
+
+#else
+#error Unknown ELF machine type
+#endif
+
+/**
+ * Android system headers don't have all definitions
+ */
+#ifndef STN_UNDEF
+#define STN_UNDEF 0
+#endif
+
+#ifndef DT_INIT_ARRAY
+#define DT_INIT_ARRAY 25
+#endif
+
+#ifndef DT_FINI_ARRAY
+#define DT_FINI_ARRAY 26
+#endif
+
+#ifndef DT_INIT_ARRAYSZ
+#define DT_INIT_ARRAYSZ 27
+#endif
+
+#ifndef DT_FINI_ARRAYSZ
+#define DT_FINI_ARRAYSZ 28
+#endif
+
+namespace Elf {
+
+/**
+ * Define a few basic Elf Types
+ */
+typedef Elf_(Phdr) Phdr;
+typedef Elf_(Dyn) Dyn;
+typedef Elf_(Sym) Sym;
+typedef Elf_(Addr) Addr;
+typedef Elf_(Word) Word;
+
+/**
+ * Helper class around the standard Elf header struct
+ */
+struct Ehdr: public Elf_(Ehdr)
+{
+  /**
+   * Equivalent to reinterpret_cast<const Ehdr *>(buf), but additionally
+   * checking that this is indeed an Elf header and that the Elf type
+   * corresponds to that of the system
+   */
+  static const Ehdr *validate(const void *buf);
+};
+
+/**
+ * Elf String table
+ */
+class Strtab: public UnsizedArray<const char>
+{
+public:
+  /**
+   * Returns the string at the given index in the table
+   */
+  const char *GetStringAt(off_t index) const
+  {
+    return &UnsizedArray<const char>::operator[](index);
+  }
+};
+
+/**
+ * Helper class around Elf relocation.
+ */
+struct Rel: public Elf_(Rel)
+{
+  /**
+   * Returns the addend for the relocation, which is the value stored
+   * at r_offset.
+   */
+  Addr GetAddend(void *base) const
+  {
+    return *(reinterpret_cast<const Addr *>(
+                   reinterpret_cast<const char *>(base) + r_offset));
+  }
+};
+
+/**
+ * Helper class around Elf relocation with addend.
+ */
+struct Rela: public Elf_(Rela)
+{
+  /**
+   * Returns the addend for the relocation.
+   */
+  Addr GetAddend(void *base) const
+  {
+    return r_addend;
+  }
+};
+
+} /* namespace Elf */
+
+/**
+ * Library Handle class for ELF libraries we don't let the system linker
+ * handle.
+ */
+class CustomElf: public LibHandle
+{
+public:
+  /**
+   * Returns a new CustomElf using the given file descriptor to map ELF
+   * content. The file descriptor ownership is stolen, and it will be closed
+   * in CustomElf's destructor if an instance is created, or by the Load
+   * method otherwise. The path corresponds to the file descriptor, and flags
+   * are the same kind of flags that would be given to dlopen(), though
+   * currently, none are supported and the behaviour is more or less that of
+   * RTLD_GLOBAL | RTLD_BIND_NOW.
+   */
+  static mozilla::TemporaryRef<LibHandle> Load(int fd,
+                                               const char *path, int flags);
+
+  /**
+   * Inherited from LibHandle
+   */
+  virtual ~CustomElf();
+  virtual void *GetSymbolPtr(const char *symbol) const;
+  virtual bool Contains(void *addr) const;
+
+private:
+  /**
+   * Returns a pointer to the Elf Symbol in the Dynamic Symbol table
+   * corresponding to the given symbol name (with a pre-computed hash).
+   */
+  const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const;
+
+  /**
+   * Returns the address corresponding to the given symbol name (with a
+   * pre-computed hash).
+   */
+  void *GetSymbolPtr(const char *symbol, unsigned long hash) const;
+
+  /**
+   * Scan dependent libraries to find the address corresponding to the
+   * given symbol name. This is used to find symbols that are undefined
+   * in the Elf object.
+   */
+  void *GetSymbolPtrInDeps(const char *symbol) const;
+
+  /**
+   * Private constructor
+   */
+  CustomElf(int fd, const char *path)
+  : LibHandle(path), fd(fd), init(0), fini(0), initialized(false)
+  { }
+
+  /**
+   * Returns a pointer relative to the base address where the library is
+   * loaded.
+   */
+  void *GetPtr(const Elf::Addr offset) const
+  {
+    return base + offset;
+  }
+
+  /**
+   * Like the above, but returns a typed (const) pointer
+   */
+  template <typename T>
+  const T *GetPtr(const Elf::Addr offset) const
+  {
+    return reinterpret_cast<const T *>(base + offset);
+  }
+
+  /**
+   * Loads an Elf segment defined by the given PT_LOAD header.
+   * Returns whether this succeeded or failed.
+   */
+  bool LoadSegment(const Elf::Phdr *pt_load) const;
+
+  /**
+   * Initializes the library according to information found in the given
+   * PT_DYNAMIC header.
+   * Returns whether this succeeded or failed.
+   */
+  bool InitDyn(const Elf::Phdr *pt_dyn);
+
+  /**
+   * Apply .rel.dyn/.rela.dyn relocations.
+   * Returns whether this succeeded or failed.
+   */
+  bool Relocate();
+
+  /**
+   * Apply .rel.plt/.rela.plt relocations.
+   * Returns whether this succeeded or failed.
+   */
+  bool RelocateJumps();
+
+  /**
+   * Call initialization functions (.init/.init_array)
+   * Returns true;
+   */
+  bool CallInit();
+
+  /**
+   * Call destructor functions (.fini_array/.fini)
+   * Returns whether this succeeded or failed.
+   */
+  void CallFini();
+
+  /**
+   * Call a function given a pointer to its location.
+   */
+  void CallFunction(void *ptr) const
+  {
+    /* C++ doesn't allow direct conversion between pointer-to-object
+     * and pointer-to-function. */
+    union {
+      void *ptr;
+      void (*func)(void);
+    } f;
+    f.ptr = ptr;
+    f.func();
+  }
+
+  /**
+   * Call a function given a an address relative to the library base
+   */
+  void CallFunction(Elf::Addr addr) const
+  {
+    return CallFunction(GetPtr(addr));
+  }
+
+  /* Appropriated file descriptor */
+  AutoCloseFD fd;
+
+  /* Base address where the library is loaded */
+  MappedPtr base;
+
+  /* String table */
+  Elf::Strtab strtab;
+
+  /* Symbol table */
+  UnsizedArray<Elf::Sym> symtab;
+
+  /* Buckets and chains for the System V symbol hash table */
+  Array<Elf::Word> buckets;
+  UnsizedArray<Elf::Word> chains;
+
+  /* List of dependent libraries */
+  std::vector<mozilla::RefPtr<LibHandle> > dependencies;
+
+  /* List of .rel.dyn/.rela.dyn relocations */
+  Array<Elf::Reloc> relocations;
+
+  /* List of .rel.plt/.rela.plt relocation */
+  Array<Elf::Reloc> jumprels;
+
+  /* Relative address of the initialization and destruction functions
+   * (.init/.fini) */
+  Elf::Addr init, fini;
+
+  /* List of initialization and destruction functions
+   * (.init_array/.fini_array) */
+  Array<void *> init_array, fini_array;
+
+  bool initialized;
+};
+
+#endif /* CustomElf_h */
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -2,17 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <cstring>
 #include <cstdlib>
 #include <dlfcn.h>
 #include <unistd.h>
 #include <algorithm>
+#include <fcntl.h>
 #include "ElfLoader.h"
+#include "CustomElf.h"
 #include "Logging.h"
 
 using namespace mozilla;
 
 /**
  * dlfcn.h replacements functions
  */
 
@@ -168,38 +170,47 @@ ElfLoader::Load(const char *path, int fl
         return *it;
   } else {
     for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
       if ((*it)->GetPath() && (strcmp((*it)->GetPath(), path) == 0))
         return *it;
   }
 
   char *abs_path = NULL;
+  const char *requested_path = path;
 
   /* When the path is not absolute and the library is being loaded for
    * another, first try to load the library from the directory containing
    * that parent library. */
   if ((name == path) && parent) {
     const char *parentPath = parent->GetPath();
     abs_path = new char[strlen(parentPath) + strlen(path)];
     strcpy(abs_path, parentPath);
     char *slash = strrchr(abs_path, '/');
     strcpy(slash + 1, path);
     path = abs_path;
   }
 
-  handle = SystemElf::Load(path, flags);
+  /* Try loading the file with the custom linker, and fall back to the
+   * system linker if that fails */
+  AutoCloseFD fd = open(path, O_RDONLY);
+  if (fd != -1) {
+    handle = CustomElf::Load(fd, path, flags);
+    fd.forget();
+  }
+  if (!handle)
+    handle = SystemElf::Load(path, flags);
 
   /* If we didn't have an absolute path and haven't been able to load
    * a library yet, try in the system search path */
   if (!handle && abs_path)
     handle = SystemElf::Load(name, flags);
 
   delete [] abs_path;
-  debug("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", path, flags,
+  debug("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", requested_path, flags,
         reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "",
         static_cast<void *>(handle));
 
   /* Bookkeeping */
   if (handle)
     handles.push_back(handle);
 
   return handle;
@@ -268,8 +279,53 @@ ElfLoader::~ElfLoader()
               (*it)->DirectRefCount(), (*it)->refCount());
         /* Not removing, since it could have references to other libraries,
          * destroying them as a side effect, and possibly leaving dangling
          * pointers in the handle list we're scanning */
       }
     }
   }
 }
+
+#ifdef __ARM_EABI__
+int
+ElfLoader::__wrap_aeabi_atexit(void *that, ElfLoader::Destructor destructor,
+                               void *dso_handle)
+{
+  Singleton.destructors.push_back(
+    DestructorCaller(destructor, that, dso_handle));
+  return 0;
+}
+#else
+int
+ElfLoader::__wrap_cxa_atexit(ElfLoader::Destructor destructor, void *that,
+                             void *dso_handle)
+{
+  Singleton.destructors.push_back(
+    DestructorCaller(destructor, that, dso_handle));
+  return 0;
+}
+#endif
+
+void
+ElfLoader::__wrap_cxa_finalize(void *dso_handle)
+{
+  /* Call all destructors for the given DSO handle in reverse order they were
+   * registered. */
+  std::vector<DestructorCaller>::reverse_iterator it;
+  for (it = Singleton.destructors.rbegin();
+       it < Singleton.destructors.rend(); ++it) {
+    if (it->IsForHandle(dso_handle)) {
+      it->Call();
+    }
+  }
+}
+
+void
+ElfLoader::DestructorCaller::Call()
+{
+  if (destructor) {
+    debug("ElfLoader::DestructorCaller::Call(%p, %p, %p)",
+          FunctionPtr(destructor), object, dso_handle);
+    destructor(object);
+    destructor = NULL;
+  }
+}
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -4,16 +4,17 @@
 
 #ifndef ElfLoader_h
 #define ElfLoader_h
 
 #include <vector>
 /* Until RefPtr.h stops using JS_Assert */
 #undef DEBUG
 #include "mozilla/RefPtr.h"
+#include "Zip.h"
 
 /**
  * dlfcn.h replacement functions
  */
 extern "C" {
   void *__wrap_dlopen(const char *path, int flags);
   const char *__wrap_dlerror(void);
   void *__wrap_dlsym(void *handle, const char *symbol);
@@ -113,16 +114,17 @@ public:
   }
 
 protected:
   /**
    * Returns whether the handle is a SystemElf or not. (short of a better way
    * to do this without RTTI)
    */
   friend class ElfLoader;
+  friend class CustomElf;
   virtual bool IsSystemElf() const { return false; }
 
 private:
   int directRefCnt;
   char *path;
 };
 
 /**
@@ -217,11 +219,75 @@ protected:
 
 private:
   ElfLoader() { }
   ~ElfLoader();
 
   /* Bookkeeping */
   typedef std::vector<LibHandle *> LibHandleList;
   LibHandleList handles;
+
+protected:
+  friend class CustomElf;
+  /* Definition of static destructors as to be used for C++ ABI compatibility */
+  typedef void (*Destructor)(void *object);
+
+  /**
+   * C++ ABI makes static initializers register destructors through a specific
+   * atexit interface. On glibc/linux systems, the dso_handle is a pointer
+   * within a given library. On bionic/android systems, it is an undefined
+   * symbol. Making sense of the value is not really important, and all that
+   * is really important is that it is different for each loaded library, so
+   * that they can be discriminated when shutting down. For convenience, on
+   * systems where the dso handle is a symbol, that symbol is resolved to
+   * point at corresponding CustomElf.
+   *
+   * Destructors are registered with __*_atexit with an associated object to
+   * be passed as argument when it is called.
+   *
+   * When __cxa_finalize is called, destructors registered for the given
+   * DSO handle are called in the reverse order they were registered.
+   */
+#ifdef __ARM_EABI__
+  static int __wrap_aeabi_atexit(void *that, Destructor destructor,
+                                 void *dso_handle);
+#else
+  static int __wrap_cxa_atexit(Destructor destructor, void *that,
+                               void *dso_handle);
+#endif
+
+  static void __wrap_cxa_finalize(void *dso_handle);
+
+  /**
+   * Registered destructor. Keeps track of the destructor function pointer,
+   * associated object to call it with, and DSO handle.
+   */
+  class DestructorCaller {
+  public:
+    DestructorCaller(Destructor destructor, void *object, void *dso_handle)
+    : destructor(destructor), object(object), dso_handle(dso_handle) { }
+
+    /**
+     * Call the destructor function with the associated object.
+     * Call only once, see CustomElf::~CustomElf.
+     */
+    void Call();
+
+    /**
+     * Returns whether the destructor is associated to the given DSO handle
+     */
+    bool IsForHandle(void *handle) const
+    {
+      return handle == dso_handle;
+    }
+
+  private:
+    Destructor destructor;
+    void *object;
+    void *dso_handle;
+  };
+
+private:
+  /* Keep track of all registered destructors */
+  std::vector<DestructorCaller> destructors;
 };
 
 #endif /* ElfLoader_h */
--- a/mozglue/linker/Makefile.in
+++ b/mozglue/linker/Makefile.in
@@ -16,12 +16,13 @@ STL_FLAGS =
 
 CPPSRCS = \
   Zip.cpp \
   $(NULL)
 
 ifndef MOZ_OLD_LINKER
 CPPSRCS += \
   ElfLoader.cpp \
+  CustomElf.cpp \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/mozglue/linker/Utils.h
+++ b/mozglue/linker/Utils.h
@@ -1,16 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef Utils_h
 #define Utils_h
 
 #include <stdint.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <unistd.h>
 
 /**
  * On architectures that are little endian and that support unaligned reads,
  * we can use direct type, but on others, we want to have a special class
  * to handle conversion and alignment issues.
  */
 #if defined(__i386__) || defined(__x86_64__)
 typedef uint16_t le_uint16;
@@ -85,9 +88,285 @@ public:
     fd = other;
     return fd;
   }
 
 private:
   int fd;
 };
 
+/**
+ * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as
+ * a simple void * or unsigned char *.
+ */
+class MappedPtr
+{
+public:
+  MappedPtr(void *buf, size_t length): buf(buf), length(length) { }
+  MappedPtr(): buf(MAP_FAILED), length(0) { }
+
+  void Init(void *b, size_t len) {
+    buf = b;
+    length = len;
+  }
+
+  ~MappedPtr()
+  {
+    if (buf != MAP_FAILED)
+      munmap(buf, length);
+  }
+
+  operator void *() const
+  {
+    return buf;
+  }
+
+  operator unsigned char *() const
+  {
+    return reinterpret_cast<unsigned char *>(buf);
+  }
+
+  bool operator ==(void *ptr) const {
+    return buf == ptr;
+  }
+
+  bool operator ==(unsigned char *ptr) const {
+    return buf == ptr;
+  }
+
+  void *operator +(off_t offset) const
+  {
+    return reinterpret_cast<char *>(buf) + offset;
+  }
+
+  /**
+   * Returns whether the given address is within the mapped range
+   */
+  bool Contains(void *ptr) const
+  {
+    return (ptr >= buf) && (ptr < reinterpret_cast<char *>(buf) + length);
+  }
+
+private:
+  void *buf;
+  size_t length;
+};
+
+/**
+ * UnsizedArray is a way to access raw arrays of data in memory.
+ *
+ *   struct S { ... };
+ *   UnsizedArray<S> a(buf);
+ *   UnsizedArray<S> b; b.Init(buf);
+ *
+ * This is roughly equivalent to
+ *   const S *a = reinterpret_cast<const S *>(buf);
+ *   const S *b = NULL; b = reinterpret_cast<const S *>(buf);
+ *
+ * An UnsizedArray has no known length, and it's up to the caller to make
+ * sure the accessed memory is mapped and makes sense.
+ */
+template <typename T>
+class UnsizedArray
+{
+public:
+  typedef size_t idx_t;
+
+  /**
+   * Constructors and Initializers
+   */
+  UnsizedArray(): contents(NULL) { }
+  UnsizedArray(const void *buf): contents(reinterpret_cast<const T *>(buf)) { }
+
+  void Init(const void *buf)
+  {
+    // ASSERT(operator bool())
+    contents = reinterpret_cast<const T *>(buf);
+  }
+
+  /**
+   * Returns the nth element of the array
+   */
+  const T &operator[](const idx_t index) const
+  {
+    // ASSERT(operator bool())
+    return contents[index];
+  }
+
+  /**
+   * Returns whether the array points somewhere
+   */
+  operator bool() const
+  {
+    return contents != NULL;
+  }
+private:
+  const T *contents;
+};
+
+/**
+ * Array, like UnsizedArray, is a way to access raw arrays of data in memory.
+ * Unlike UnsizedArray, it has a known length, and is enumerable with an
+ * iterator.
+ *
+ *   struct S { ... };
+ *   Array<S> a(buf, len);
+ *   UnsizedArray<S> b; b.Init(buf, len);
+ *
+ * In the above examples, len is the number of elements in the array. It is
+ * also possible to initialize an Array with the buffer size:
+ *
+ *   Array<S> c; c.InitSize(buf, size);
+ *
+ * It is also possible to initialize an Array in two steps, only providing
+ * one data at a time:
+ *
+ *   Array<S> d;
+ *   d.Init(buf);
+ *   d.Init(len); // or d.InitSize(size);
+ *
+ */
+template <typename T>
+class Array: public UnsizedArray<T>
+{
+public:
+  typedef typename UnsizedArray<T>::idx_t idx_t;
+
+  /**
+   * Constructors and Initializers
+   */
+  Array(): UnsizedArray<T>(), length(0) { }
+  Array(const void *buf, const idx_t length)
+  : UnsizedArray<T>(buf), length(length) { }
+
+  void Init(const void *buf)
+  {
+    UnsizedArray<T>::Init(buf);
+  }
+
+  void Init(const idx_t len)
+  {
+    // ASSERT(length != 0)
+    length = len;
+  }
+
+  void InitSize(const idx_t size)
+  {
+    Init(size / sizeof(T));
+  }
+
+  void Init(const void *buf, const idx_t len)
+  {
+    UnsizedArray<T>::Init(buf);
+    Init(len);
+  }
+
+  void InitSize(const void *buf, const idx_t size)
+  {
+    UnsizedArray<T>::Init(buf);
+    InitSize(size);
+  }
+
+  /**
+   * Returns the nth element of the array
+   */
+  const T &operator[](const idx_t index) const
+  {
+    // ASSERT(index < length)
+    // ASSERT(operator bool())
+    return UnsizedArray<T>::operator[](index);
+  }
+
+  /**
+   * Returns the number of elements in the array
+   */
+  idx_t numElements() const
+  {
+    return length;
+  }
+
+  /**
+   * Returns whether the array points somewhere and has at least one element.
+   */
+  operator bool() const
+  {
+    return (length > 0) && UnsizedArray<T>::operator bool();
+  }
+
+  /**
+   * Iterator for an Array. Use is similar to that of STL const_iterators:
+   *
+   *   struct S { ... };
+   *   Array<S> a(buf, len);
+   *   for (Array<S>::iterator it = a.begin(); it < a.end(); ++it) {
+   *     // Do something with *it.
+   *   }
+   */
+  class iterator
+  {
+  public:
+    iterator(): item(NULL) { }
+
+    const T &operator *() const
+    {
+      return *item;
+    }
+
+    const T *operator ->() const
+    {
+      return item;
+    }
+
+    const T &operator ++()
+    {
+      return *(++item);
+    }
+
+    bool operator<(const iterator &other) const
+    {
+      return item < other.item;
+    }
+  protected:
+    friend class Array<T>;
+    iterator(const T &item): item(&item) { }
+
+  private:
+    const T *item;
+  };
+
+  /**
+   * Returns an iterator pointing at the beginning of the Array
+   */
+  iterator begin() const {
+    if (length)
+      return iterator(UnsizedArray<T>::operator[](0));
+    return iterator();
+  }
+
+  /**
+   * Returns an iterator pointing past the end of the Array
+   */
+  iterator end() const {
+    if (length)
+      return iterator(UnsizedArray<T>::operator[](length));
+    return iterator();
+  }
+private:
+  idx_t length;
+};
+
+/**
+ * Transforms a pointer-to-function to a pointer-to-object pointing at the
+ * same address.
+ */
+template <typename T>
+void *FunctionPtr(T func)
+{
+  union {
+    void *ptr;
+    T func;
+  } f;
+  f.func = func;
+  return f.ptr;
+}
+
 #endif /* Utils_h */
+ 
--- a/mozglue/tests/TestZip.cpp
+++ b/mozglue/tests/TestZip.cpp
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <cstdio>
 #include <unistd.h>
 #include "Zip.h"
 
+extern "C" void report_mapping() { }
+
 /**
  * test.zip is a basic test zip file with a central directory. It contains
  * four entries, in the following order:
  * "foo", "bar", "baz", "qux".
  * The entries are going to be read out of order.
  */
 const char *test_entries[] = {
   "baz", "foo", "bar", "qux"