Bug 606145 part 2 - Import elfhack code. r=tglek,a=bsmedberg
authorMike Hommey <mh+mozilla@glandium.org>
Mon, 24 Jan 2011 09:56:40 +0100
changeset 61170 a1ccb1c489baeeb2e738ce9173b5ee079b0243f9
parent 61169 0d947763fe67c2a728141f97f328ce3b8055e270
child 61171 9a6de1e28d4bf476870fec0d01f29e4be6f05dcc
push id18255
push usermh@glandium.org
push dateMon, 24 Jan 2011 08:57:53 +0000
treeherdermozilla-central@9a6de1e28d4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstglek, bsmedberg
bugs606145
milestone2.0b10pre
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 606145 part 2 - Import elfhack code. r=tglek,a=bsmedberg
build/unix/elfhack/README
build/unix/elfhack/dummy.c
build/unix/elfhack/elf.cpp
build/unix/elfhack/elfhack.cpp
build/unix/elfhack/elfxx.h
build/unix/elfhack/inject.c
build/unix/elfhack/test.c
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/README
@@ -0,0 +1,28 @@
+Elfhack is a program to optimize ELF binaries for size and cold startup
+speed.
+
+Presently, it is quite experimental, though it works well for the target
+it was created for: Firefox's libxul.so.
+
+Elfhack currently only does one thing: packing dynamic relocations ;
+which ends up being a quite complex task, that can be summarized this
+way:
+- Remove RELATIVE relocations from the .rel.dyn/.rela.dyn section.
+- Inject a small code able to apply relative relocations "by hand"
+  after the .rel.dyn/.rela.dyn section.
+- Inject a section containing relocative relocations in a different
+  and more packed format, after the small code.
+- Register the small code as DT_INIT function. Make the small code call
+  what was initially the DT_INIT function, if there was one.
+- Remove the hole between the new section containing relative
+  relocations and the following sections, adjusting offsets and base
+  addresses accordingly.
+- Adjust PT_LOAD entries to fit new offsets, and add an additional
+  PT_LOAD entry when that is necessary to handle the discrepancy between
+  offsets and base addresses, meaning the section offsets may yet again
+  need adjustments.
+- Adjust various DT_* dynamic tags to fit the new ELF layout.
+- Adjust section headers.
+- Adjust ELF headers.
+
+See http://glandium.org/blog/?p=1177#relocations for some figures.
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/dummy.c
@@ -0,0 +1,3 @@
+int main() {
+    return 1;
+}
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/elf.cpp
@@ -0,0 +1,822 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is elfhack.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mike Hommey <mh@glandium.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <cstring>
+#include <assert.h>
+#include "elfxx.h"
+
+template <class endian, typename R, typename T>
+inline void Elf_Ehdr_Traits::swap(T &t, R &r)
+{
+    memcpy(r.e_ident, t.e_ident, sizeof(r.e_ident));
+    r.e_type = endian::swap(t.e_type);
+    r.e_machine = endian::swap(t.e_machine);
+    r.e_version = endian::swap(t.e_version);
+    r.e_entry = endian::swap(t.e_entry);
+    r.e_phoff = endian::swap(t.e_phoff);
+    r.e_shoff = endian::swap(t.e_shoff);
+    r.e_flags = endian::swap(t.e_flags);
+    r.e_ehsize = endian::swap(t.e_ehsize);
+    r.e_phentsize = endian::swap(t.e_phentsize);
+    r.e_phnum = endian::swap(t.e_phnum);
+    r.e_shentsize = endian::swap(t.e_shentsize);
+    r.e_shnum = endian::swap(t.e_shnum);
+    r.e_shstrndx = endian::swap(t.e_shstrndx);
+}
+
+template <class endian, typename R, typename T>
+inline void Elf_Phdr_Traits::swap(T &t, R &r)
+{
+    r.p_type = endian::swap(t.p_type);
+    r.p_offset = endian::swap(t.p_offset);
+    r.p_vaddr = endian::swap(t.p_vaddr);
+    r.p_paddr = endian::swap(t.p_paddr);
+    r.p_filesz = endian::swap(t.p_filesz);
+    r.p_memsz = endian::swap(t.p_memsz);
+    r.p_flags = endian::swap(t.p_flags);
+    r.p_align = endian::swap(t.p_align);
+}
+
+template <class endian, typename R, typename T>
+inline void Elf_Shdr_Traits::swap(T &t, R &r)
+{
+    r.sh_name = endian::swap(t.sh_name);
+    r.sh_type = endian::swap(t.sh_type);
+    r.sh_flags = endian::swap(t.sh_flags);
+    r.sh_addr = endian::swap(t.sh_addr);
+    r.sh_offset = endian::swap(t.sh_offset);
+    r.sh_size = endian::swap(t.sh_size);
+    r.sh_link = endian::swap(t.sh_link);
+    r.sh_info = endian::swap(t.sh_info);
+    r.sh_addralign = endian::swap(t.sh_addralign);
+    r.sh_entsize = endian::swap(t.sh_entsize);
+}
+
+template <class endian, typename R, typename T>
+inline void Elf_Dyn_Traits::swap(T &t, R &r)
+{
+    r.d_tag = endian::swap(t.d_tag);
+    r.d_un.d_val = endian::swap(t.d_un.d_val);
+}
+
+template <class endian, typename R, typename T>
+inline void Elf_Sym_Traits::swap(T &t, R &r)
+{
+    r.st_name = endian::swap(t.st_name);
+    r.st_value = endian::swap(t.st_value);
+    r.st_size = endian::swap(t.st_size);
+    r.st_info = t.st_info;
+    r.st_other = t.st_other;
+    r.st_shndx = endian::swap(t.st_shndx);
+}
+
+template <class endian>
+struct _Rel_info {
+    static inline void swap(Elf32_Word &t, Elf32_Word &r) { r = endian::swap(t); }
+    static inline void swap(Elf64_Xword &t, Elf64_Xword &r) { r = endian::swap(t); }
+    static inline void swap(Elf64_Xword &t, Elf32_Word &r) {
+        r = endian::swap(ELF32_R_INFO(ELF64_R_SYM(t), ELF64_R_TYPE(t)));
+    }
+    static inline void swap(Elf32_Word &t, Elf64_Xword &r) {
+        r = endian::swap(ELF64_R_INFO(ELF32_R_SYM(t), ELF32_R_TYPE(t)));
+    }
+};
+
+template <class endian, typename R, typename T>
+inline void Elf_Rel_Traits::swap(T &t, R &r)
+{
+    r.r_offset = endian::swap(t.r_offset);
+    _Rel_info<endian>::swap(t.r_info, r.r_info);
+}
+
+template <class endian, typename R, typename T>
+inline void Elf_Rela_Traits::swap(T &t, R &r)
+{
+    r.r_offset = endian::swap(t.r_offset);
+    _Rel_info<endian>::swap(t.r_info, r.r_info);
+    r.r_addend = endian::swap(t.r_addend);
+}
+
+static const Elf32_Shdr null32_section =
+    { 0, SHT_NULL, 0, 0, 0, 0, SHN_UNDEF, 0, 0, 0 };
+
+Elf_Shdr null_section(null32_section);
+
+Elf_Ehdr::Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data)
+: serializable<Elf_Ehdr_Traits>(file, ei_class, ei_data),
+  ElfSection(null_section, NULL, NULL)
+{
+    shdr.sh_size = Elf_Ehdr::size(ei_class);
+}
+
+Elf::Elf(std::ifstream &file)
+{
+    if (!file.is_open())
+        throw std::runtime_error("Error opening file");
+
+    file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit);
+    // Read ELF magic number and identification information
+    char e_ident[EI_VERSION];
+    file.seekg(0);
+    file.read(e_ident, sizeof(e_ident));
+    file.seekg(0);
+    ehdr = new Elf_Ehdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
+
+    // ELFOSABI_LINUX is kept unsupported because I haven't looked whether
+    // STB_GNU_UNIQUE or STT_GNU_IFUNC would need special casing.
+    if ((ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) && (ehdr->e_ident[EI_ABIVERSION] != 0))
+        throw std::runtime_error("unsupported ELF ABI");
+
+    if (ehdr->e_version != 1)
+        throw std::runtime_error("unsupported ELF version");
+
+    // Sanity checks
+    if (ehdr->e_shnum == 0)
+        throw std::runtime_error("sstripped ELF files aren't supported");
+
+    if (ehdr->e_ehsize != Elf_Ehdr::size(e_ident[EI_CLASS]))
+        throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_ehsize != sizeof(ehdr)");
+
+    if (ehdr->e_shentsize != Elf_Shdr::size(e_ident[EI_CLASS]))
+        throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_shentsize != sizeof(shdr)");
+
+    if (ehdr->e_phnum == 0) {
+        if (ehdr->e_phoff != 0)
+            throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phoff != 0");
+        if (ehdr->e_phentsize != 0)
+            throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phentsize != 0");
+    } else if (ehdr->e_phoff != ehdr->e_ehsize)
+        throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phoff != ehdr->e_ehsize");
+    else if (ehdr->e_phentsize != Elf_Phdr::size(e_ident[EI_CLASS]))
+        throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phentsize != sizeof(phdr)");
+
+    // Read section headers
+    Elf_Shdr **shdr = new Elf_Shdr *[ehdr->e_shnum];
+    file.seekg(ehdr->e_shoff);
+    for (int i = 0; i < ehdr->e_shnum; i++)
+        shdr[i] = new Elf_Shdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
+
+    // Sanity check in section header for index 0
+    if ((shdr[0]->sh_name != 0) || (shdr[0]->sh_type != SHT_NULL) ||
+        (shdr[0]->sh_flags != 0) || (shdr[0]->sh_addr != 0) ||
+        (shdr[0]->sh_offset != 0) || (shdr[0]->sh_size != 0) ||
+        (shdr[0]->sh_link != SHN_UNDEF) || (shdr[0]->sh_info != 0) ||
+        (shdr[0]->sh_addralign != 0) || (shdr[0]->sh_entsize != 0))
+        throw std::runtime_error("Section header for index 0 contains unsupported values");
+
+    if ((shdr[ehdr->e_shstrndx]->sh_link != 0) || (shdr[ehdr->e_shstrndx]->sh_info != 0))
+        throw std::runtime_error("unsupported ELF content: string table with sh_link != 0 || sh_info != 0");
+
+    // Store these temporarily
+    tmp_shdr = shdr;
+    tmp_file = &file;
+
+    // Fill sections list
+    sections = new ElfSection *[ehdr->e_shnum];
+    for (int i = 0; i < ehdr->e_shnum; i++)
+        sections[i] = NULL;
+    for (int i = 1; i < ehdr->e_shnum; i++) {
+        if (sections[i] != NULL)
+            continue;
+        getSection(i);
+    }
+    Elf_Shdr s;
+    s.sh_name = 0;
+    s.sh_type = SHT_NULL;
+    s.sh_flags = 0;
+    s.sh_addr = 0;
+    s.sh_offset = ehdr->e_shoff;
+    s.sh_entsize = Elf_Shdr::size(e_ident[EI_CLASS]);
+    s.sh_size = s.sh_entsize * ehdr->e_shnum;
+    s.sh_link = 0;
+    s.sh_info = 0;
+    s.sh_addralign = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8;
+    shdr_section = new ElfSection(s, NULL, NULL);
+
+    // Fake section for program headers
+    s.sh_offset = ehdr->e_phoff;
+    s.sh_entsize = Elf_Phdr::size(e_ident[EI_CLASS]);
+    s.sh_size = s.sh_entsize * ehdr->e_phnum;
+    phdr_section = new ElfSection(s, NULL, NULL);
+
+    phdr_section->insertAfter(ehdr);
+
+    sections[1]->insertAfter(phdr_section);
+    for (int i = 2; i < ehdr->e_shnum; i++) {
+        // TODO: this should be done in a better way
+        if ((shdr_section->getPrevious() == NULL) && (shdr[i]->sh_offset > ehdr->e_shoff)) {
+            shdr_section->insertAfter(sections[i - 1]);
+            sections[i]->insertAfter(shdr_section);
+        } else
+            sections[i]->insertAfter(sections[i - 1]);
+    }
+    if (shdr_section->getPrevious() == NULL)
+        shdr_section->insertAfter(sections[ehdr->e_shnum - 1]);
+
+    tmp_file = NULL;
+    tmp_shdr = NULL;
+    for (int i = 0; i < ehdr->e_shnum; i++)
+        delete shdr[i];
+    delete[] shdr;
+
+    eh_shstrndx = (ElfStrtab_Section *)sections[ehdr->e_shstrndx];
+
+    // Skip reading program headers if there aren't any
+    if (ehdr->e_phnum == 0)
+        return;
+
+    // Read program headers
+    file.seekg(ehdr->e_phoff);
+    for (int i = 0; i < ehdr->e_phnum; i++) {
+        Elf_Phdr phdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
+        ElfSegment *segment = new ElfSegment(&phdr);
+        // Some segments aren't entirely filled (if at all) by sections
+        // For those, we use fake sections
+        if ((phdr.p_type == PT_LOAD) && (phdr.p_offset == 0)) {
+            // Use a fake section for ehdr and phdr
+            ehdr->getShdr().sh_addr = phdr.p_vaddr;
+            phdr_section->getShdr().sh_addr = phdr.p_vaddr + ehdr->e_ehsize;
+            segment->addSection(ehdr);
+            segment->addSection(phdr_section);
+            ehdr->markDirty();
+        }
+        if (phdr.p_type == PT_PHDR)
+            segment->addSection(phdr_section);
+        for (int j = 1; j < ehdr->e_shnum; j++)
+            if (phdr.contains(sections[j]))
+                segment->addSection(sections[j]);
+        // Make sure that our view of segments corresponds to the original
+        // ELF file.
+        assert(segment->getFileSize() == phdr.p_filesz);
+        assert(segment->getMemSize() == phdr.p_memsz);
+        segments.push_back(segment);
+    }
+
+    new (&eh_entry) ElfLocation(ehdr->e_entry, this);
+}
+
+Elf::~Elf()
+{
+    for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+        delete *seg;
+    delete[] sections;
+    ElfSection *section = ehdr;
+    while (section != NULL) {
+        ElfSection *next = section->getNext();
+        delete section;
+        section = next;
+    }
+}
+
+// TODO: This shouldn't fail after inserting sections
+ElfSection *Elf::getSection(int index)
+{
+    if ((index < -1) || (index >= ehdr->e_shnum))
+        throw std::runtime_error("Section index out of bounds");
+    if (index == -1)
+        index = ehdr->e_shstrndx; // TODO: should be fixed to use the actual current number
+    // Special case: the section at index 0 is void
+    if (index == 0)
+        return NULL;
+    // Infinite recursion guard
+    if (sections[index] == (ElfSection *)this)
+        return NULL;
+    if (sections[index] == NULL) {
+        sections[index] = (ElfSection *)this;
+        switch (tmp_shdr[index]->sh_type) {
+        case SHT_DYNAMIC:
+            sections[index] = new ElfDynamic_Section(*tmp_shdr[index], tmp_file, this);
+            break;
+        case SHT_REL:
+            sections[index] = new ElfRel_Section<Elf_Rel>(*tmp_shdr[index], tmp_file, this);
+            break;
+        case SHT_RELA:
+            sections[index] = new ElfRel_Section<Elf_Rela>(*tmp_shdr[index], tmp_file, this);
+            break;
+        case SHT_SYMTAB:
+            sections[index] = new ElfSymtab_Section(*tmp_shdr[index], tmp_file, this);
+            break;
+        case SHT_STRTAB:
+            sections[index] = new ElfStrtab_Section(*tmp_shdr[index], tmp_file, this);
+            break;
+        default:
+            sections[index] = new ElfSection(*tmp_shdr[index], tmp_file, this);
+        }
+    }
+    return sections[index];
+}
+
+ElfSection *Elf::getSectionAt(unsigned int offset)
+{
+    for (int i = 1; i < ehdr->e_shnum; i++) {
+        ElfSection *section = getSection(i);
+        if ((section != NULL) && (section->getFlags() & SHF_ALLOC) && !(section->getFlags() & SHF_TLS) &&
+            (offset >= section->getAddr()) && (offset < section->getAddr() + section->getSize()))
+            return section;
+    }
+    return NULL;
+}
+
+ElfDynamic_Section *Elf::getDynSection()
+{
+    for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+        if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != NULL) &&
+            (*seg)->getFirstSection()->getType() == SHT_DYNAMIC)
+            return (ElfDynamic_Section *)(*seg)->getFirstSection();
+
+    return NULL;
+}
+
+void Elf::write(std::ofstream &file)
+{
+    // fixup section headers sh_name; TODO: that should be done by sections
+    // themselves
+    for (ElfSection *section = ehdr; section != NULL; section = section->getNext()) {
+        if (section->getIndex() == 0)
+            continue;
+        else
+            ehdr->e_shnum = section->getIndex() + 1;
+        section->getShdr().sh_name = eh_shstrndx->getStrIndex(section->getName());
+    }
+    phdr_section->getNext()->markDirty();
+    // Adjust PT_LOAD segments
+    int i = 0;
+    for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) {
+        if ((*seg)->getType() == PT_LOAD) {
+            std::list<ElfSection *>::iterator it = (*seg)->begin();
+            for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) {
+               if (((*it)->getType() != SHT_NOBITS) &&
+                   ((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) {
+                   std::vector<ElfSegment *>::iterator next = seg;
+                   segments.insert(++next, (*seg)->splitBefore(*it));
+                   seg = segments.begin() + i;
+                   break;
+               }
+           }
+        }
+    }
+    // fixup ehdr before writing
+    if (ehdr->e_phnum != segments.size()) {
+        ehdr->e_phnum = segments.size();
+        phdr_section->getShdr().sh_size = segments.size() * Elf_Phdr::size(ehdr->e_ident[EI_CLASS]);
+        phdr_section->getNext()->markDirty();
+    }
+    // fixup shdr before writing
+    if (ehdr->e_shnum != shdr_section->getSize() / shdr_section->getEntSize())
+        shdr_section->getShdr().sh_size = ehdr->e_shnum * Elf_Shdr::size(ehdr->e_ident[EI_CLASS]);
+    ehdr->e_shoff = shdr_section->getOffset();
+    ehdr->e_entry = eh_entry.getValue();
+    ehdr->e_shstrndx = eh_shstrndx->getIndex();
+    for (ElfSection *section = ehdr;
+         section != NULL; section = section->getNext()) {
+        file.seekp(section->getOffset());
+        if (section == phdr_section) {
+            for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) {
+                Elf_Phdr phdr;
+                phdr.p_type = (*seg)->getType();
+                phdr.p_flags = (*seg)->getFlags();
+                if ((*seg)->getFirstSection()) {
+                    phdr.p_offset = (*seg)->getFirstSection()->getOffset();
+                    phdr.p_vaddr = (*seg)->getFirstSection()->getAddr();
+                } else {
+                    phdr.p_offset = 0;
+                    phdr.p_vaddr = 0;
+                }
+                phdr.p_paddr = phdr.p_vaddr + (*seg)->getVPDiff();
+                phdr.p_filesz = (*seg)->getFileSize();
+                phdr.p_memsz = (*seg)->getMemSize();
+                phdr.p_align = (*seg)->getAlign();
+                phdr.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+            }
+        } else if (section == shdr_section) {
+            null_section.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+            for (ElfSection *sec = ehdr; sec!= NULL; sec = sec->getNext()) {
+                if (sec->getType() != SHT_NULL)
+                    sec->getShdr().serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+            }
+        } else
+           section->serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+    }
+}
+
+ElfSection::ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+: shdr(s),
+  link(shdr.sh_link == SHN_UNDEF ? NULL : parent->getSection(shdr.sh_link)),
+  next(NULL), previous(NULL), index(-1)
+{
+    if ((file == NULL) || (shdr.sh_type == SHT_NULL) || (shdr.sh_type == SHT_NOBITS))
+        data = NULL;
+    else {
+        data = new char[shdr.sh_size];
+        int pos = file->tellg();
+        file->seekg(shdr.sh_offset);
+        file->read(data, shdr.sh_size);
+        file->seekg(pos);
+    }
+    if (shdr.sh_name == 0)
+        name = NULL;
+    else {
+        ElfStrtab_Section *strtab = (ElfStrtab_Section *) parent->getSection(-1);
+        // Special case (see elfgeneric.cpp): if strtab is NULL, the
+        // section being created is the strtab.
+        if (strtab == NULL)
+            name = &data[shdr.sh_name];
+        else
+            name = strtab->getStr(shdr.sh_name);
+    }
+    // Only SHT_REL/SHT_RELA sections use sh_info to store a section
+    // number.
+    if ((shdr.sh_type == SHT_REL) || (shdr.sh_type == SHT_RELA))
+        info.section = shdr.sh_info ? parent->getSection(shdr.sh_info) : NULL;
+    else
+        info.index = shdr.sh_info;
+}
+
+unsigned int ElfSection::getAddr()
+{
+    if (shdr.sh_addr != (Elf32_Word)-1)
+        return shdr.sh_addr;
+
+    // It should be safe to adjust sh_addr for all allocated sections that
+    // are neither SHT_NOBITS nor SHT_PROGBITS
+    if ((previous != NULL) && isRelocatable()) {
+        unsigned int addr = previous->getAddr();
+        if (previous->getType() != SHT_NOBITS)
+            addr += previous->getSize();
+
+        if (addr & (getAddrAlign() - 1))
+            addr = (addr | (getAddrAlign() - 1)) + 1;
+
+        return (shdr.sh_addr = addr);
+    }
+    return shdr.sh_addr;
+}
+
+unsigned int ElfSection::getOffset()
+{
+    if (shdr.sh_offset != (Elf32_Word)-1)
+        return shdr.sh_offset;
+
+    if (previous == NULL)
+        return (shdr.sh_offset = 0);
+
+    unsigned int offset = previous->getOffset();
+    if (previous->getType() != SHT_NOBITS)
+        offset += previous->getSize();
+
+    // SHF_TLS is used for .tbss which is some kind of special case.
+    if (((getType() != SHT_NOBITS) || (getFlags() & SHF_TLS)) && (getFlags() & SHF_ALLOC)) {
+        if ((getAddr() & 4095) < (offset & 4095))
+            offset = (offset | 4095) + (getAddr() & 4095) + 1;
+        else
+            offset = (offset & ~4095) + (getAddr() & 4095);
+    }
+    // TODO: carefully handle this, as this isn't always safe, cf. when
+    // resizing .dynamic.
+    if ((getType() != SHT_NOBITS) && (offset & (getAddrAlign() - 1)))
+        offset = (offset | (getAddrAlign() - 1)) + 1;
+
+    return (shdr.sh_offset = offset);
+}
+
+int ElfSection::getIndex()
+{
+    if (index != -1)
+        return index;
+    if (getType() == SHT_NULL)
+        return (index = 0);
+    ElfSection *reference;
+    for (reference = previous; (reference != NULL) && (reference->getType() == SHT_NULL); reference = reference->getPrevious());
+    if (reference == NULL)
+        return (index = 1);
+    return (index = reference->getIndex() + 1);
+}
+
+Elf_Shdr &ElfSection::getShdr()
+{
+    getOffset();
+    if (shdr.sh_link == (Elf32_Word)-1)
+        shdr.sh_link = getLink() ? getLink()->getIndex() : 0;
+    if (shdr.sh_info == (Elf32_Word)-1)
+        shdr.sh_info = ((getType() == SHT_REL) || (getType() == SHT_RELA)) ?
+                       (getInfo().section ? getInfo().section->getIndex() : 0) :
+                       getInfo().index;
+
+    return shdr;
+}
+
+ElfSegment::ElfSegment(Elf_Phdr *phdr)
+: type(phdr->p_type), v_p_diff(phdr->p_paddr - phdr->p_vaddr),
+  flags(phdr->p_flags), align(phdr->p_align) {}
+
+void ElfSegment::addSection(ElfSection *section)
+{
+    //TODO: Check overlapping sections
+    std::list<ElfSection *>::iterator i;
+    for (i = sections.begin(); i != sections.end(); ++i)
+        if ((*i)->getAddr() > section->getAddr())
+            break;
+    sections.insert(i, section);
+}
+
+unsigned int ElfSegment::getFileSize()
+{
+    if (sections.empty())
+        return 0;
+    // Search the last section that is not SHT_NOBITS
+    std::list<ElfSection *>::reverse_iterator i;
+    for (i = sections.rbegin(); (i != sections.rend()) && ((*i)->getType() == SHT_NOBITS); ++i);
+    // All sections are SHT_NOBITS
+    if (i == sections.rend())
+        return 0;
+    return ((*i)->getAddr() - sections.front()->getAddr()) + (*i)->getSize();
+}
+
+unsigned int ElfSegment::getMemSize()
+{
+    if (sections.empty())
+        return 0;
+    return (sections.back()->getAddr() - sections.front()->getAddr()) +
+           (sections.back()->getSize());
+}
+
+ElfSegment *ElfSegment::splitBefore(ElfSection *section)
+{
+    std::list<ElfSection *>::iterator i, rm;
+    for (i = sections.begin(); (*i != section) && (i != sections.end()); ++i);
+    if (i == sections.end())
+        return NULL;
+
+    // Probably very wrong.
+    Elf_Phdr phdr;
+    phdr.p_type = type;
+    phdr.p_vaddr = 0;
+    phdr.p_paddr = phdr.p_vaddr + v_p_diff;
+    phdr.p_flags = flags;
+    phdr.p_align = 0x1000;
+    ElfSegment *segment = new ElfSegment(&phdr);
+
+    for (rm = i; i != sections.end(); ++i)
+        segment->addSection(*i);
+    sections.erase(rm, sections.end());
+
+    return segment;
+}
+
+ElfSection *ElfDynamic_Section::getSectionForType(unsigned int tag)
+{
+    for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
+        if (dyns[i].tag == tag)
+            return dyns[i].value->getSection();
+
+    return NULL;
+}
+
+void ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val)
+{
+    unsigned int i;
+    for (i = 0; (i < shdr.sh_size / shdr.sh_entsize) && (dyns[i].tag != DT_NULL); i++)
+        if (dyns[i].tag == tag) {
+            delete dyns[i].value;
+            dyns[i].value = val;
+            return;
+        }
+    // This should never happen, as the last entry is always tagged DT_NULL
+    assert(i < shdr.sh_size / shdr.sh_entsize);
+    // If we get here, this means we didn't match for the given tag
+    dyns[i].tag = tag;
+    dyns[i++].value = val;
+
+    // If we were on the last entry, we need to grow the section.
+    // Most of the time, though, there are a few DT_NULL entries.
+    if (i < shdr.sh_size / shdr.sh_entsize)
+        return;
+
+    Elf_DynValue value;
+    value.tag = DT_NULL;
+    value.value = NULL;
+    dyns.push_back(value);
+    // Resize the section accordingly
+    shdr.sh_size += shdr.sh_entsize;
+    if (getNext() != NULL)
+        getNext()->markDirty();
+}
+
+ElfDynamic_Section::ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+: ElfSection(s, file, parent)
+{
+    int pos = file->tellg();
+    dyns.resize(s.sh_size / s.sh_entsize);
+    file->seekg(shdr.sh_offset);
+    // Here we assume tags refer to only one section (e.g. DT_RELSZ accounts
+    // for .rel.dyn size)
+    for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
+        Elf_Dyn dyn(*file, parent->getClass(), parent->getData());
+        dyns[i].tag = dyn.d_tag;
+        switch (dyn.d_tag) {
+        case DT_NULL:
+        case DT_SYMBOLIC:
+        case DT_TEXTREL:
+        case DT_BIND_NOW:
+            dyns[i].value = new ElfValue();
+            break;
+        case DT_NEEDED:
+        case DT_SONAME:
+        case DT_RPATH:
+        case DT_PLTREL:
+        case DT_RUNPATH:
+        case DT_FLAGS:
+        case DT_RELACOUNT:
+        case DT_RELCOUNT:
+        case DT_VERDEFNUM:
+        case DT_VERNEEDNUM:
+            dyns[i].value = new ElfPlainValue(dyn.d_un.d_val);
+            break;
+        case DT_PLTGOT:
+        case DT_HASH:
+        case DT_STRTAB:
+        case DT_SYMTAB:
+        case DT_RELA:
+        case DT_INIT:
+        case DT_FINI:
+        case DT_REL:
+        case DT_JMPREL:
+        case DT_INIT_ARRAY:
+        case DT_FINI_ARRAY:
+        case DT_GNU_HASH:
+        case DT_VERSYM:
+        case DT_VERNEED:
+        case DT_VERDEF:
+            dyns[i].value = new ElfLocation(dyn.d_un.d_ptr, parent);
+            break;
+        default:
+            dyns[i].value = NULL;
+        }
+    }
+    // Another loop to get the section sizes
+    for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++)
+        switch (dyns[i].tag) {
+        case DT_PLTRELSZ:
+            dyns[i].value = new ElfSize(getSectionForType(DT_JMPREL));
+            break;
+        case DT_RELASZ:
+            dyns[i].value = new ElfSize(getSectionForType(DT_RELA));
+            break;
+        case DT_STRSZ:
+            dyns[i].value = new ElfSize(getSectionForType(DT_STRTAB));
+            break;
+        case DT_RELSZ:
+            dyns[i].value = new ElfSize(getSectionForType(DT_REL));
+            break;
+        case DT_INIT_ARRAYSZ:
+            dyns[i].value = new ElfSize(getSectionForType(DT_INIT_ARRAY));
+            break;
+        case DT_FINI_ARRAYSZ:
+            dyns[i].value = new ElfSize(getSectionForType(DT_FINI_ARRAY));
+            break;
+        case DT_RELAENT:
+            dyns[i].value = new ElfEntSize(getSectionForType(DT_RELA));
+            break;
+        case DT_SYMENT:
+            dyns[i].value = new ElfEntSize(getSectionForType(DT_SYMTAB));
+            break;
+        case DT_RELENT:
+            dyns[i].value = new ElfEntSize(getSectionForType(DT_REL));
+            break;
+        }
+
+    file->seekg(pos);
+}
+
+ElfDynamic_Section::~ElfDynamic_Section()
+{
+    for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
+        delete dyns[i].value;
+}
+
+void ElfDynamic_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
+{
+    for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+        Elf_Dyn dyn;
+        dyn.d_tag = dyns[i].tag;
+        dyn.d_un.d_val = (dyns[i].value != NULL) ? dyns[i].value->getValue() : 0;
+        dyn.serialize(file, ei_class, ei_data);
+    }
+}
+
+ElfSymtab_Section::ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+: ElfSection(s, file, parent)
+{
+    int pos = file->tellg();
+    file->seekg(shdr.sh_offset);
+    for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+        Elf_Sym sym(*file, parent->getClass(), parent->getData());
+        syms.push_back(sym);
+    }
+    file->seekg(pos);
+}
+
+const char *
+ElfStrtab_Section::getStr(unsigned int index)
+{
+    for (std::vector<table_storage>::iterator t = table.begin();
+         t != table.end(); t++) {
+        if (index < t->used)
+            return t->buf + index;
+        index -= t->used;
+    }
+    assert(1 == 0);
+    return NULL;
+}
+
+const char *
+ElfStrtab_Section::getStr(const char *string)
+{
+    if (string == NULL)
+        return NULL;
+
+    // If the given string is within the section, return it
+    for (std::vector<table_storage>::iterator t = table.begin();
+         t != table.end(); t++)
+        if ((string >= t->buf) && (string < t->buf + t->used))
+            return string;
+
+    // TODO: should scan in the section to find an existing string
+
+    // If not, we need to allocate the string in the section
+    size_t len = strlen(string) + 1;
+
+    if (table.back().size - table.back().used < len)
+        table.resize(table.size() + 1);
+
+    char *alloc_str = table.back().buf + table.back().used;
+    memcpy(alloc_str, string, len);
+    table.back().used += len;
+
+    shdr.sh_size += len;
+    markDirty();
+
+    return alloc_str;
+}
+
+unsigned int
+ElfStrtab_Section::getStrIndex(const char *string)
+{
+    if (string == NULL)
+        return 0;
+
+    unsigned int index = 0;
+    string = getStr(string);
+    for (std::vector<table_storage>::iterator t = table.begin();
+         t != table.end(); t++) {
+        if ((string >= t->buf) && (string < t->buf + t->used))
+            return index + (string - t->buf);
+        index += t->used;
+    }
+
+    assert(1 == 0);
+    return 0;
+}
+
+void
+ElfStrtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
+{
+    file.seekp(getOffset());
+    for (std::vector<table_storage>::iterator t = table.begin();
+         t != table.end(); t++)
+        file.write(t->buf, t->used);
+}
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/elfhack.cpp
@@ -0,0 +1,414 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is elfhack.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mike Hommey <mh@glandium.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <assert.h>
+#include <cstring>
+#include <cstdlib>
+#include <cstdio>
+#include "elfxx.h"
+
+#define ver "0"
+#define elfhack_data ".elfhack.data.v" ver
+#define elfhack_text ".elfhack.text.v" ver
+
+#ifndef R_ARM_V4BX
+#define R_ARM_V4BX 0x28
+#endif
+
+char *rundir = NULL;
+
+class Elf_RelHack_Traits {
+public:
+    typedef Elf32_Rel Type32;
+    typedef Elf32_Rel Type64;
+
+    template <class endian, typename R, typename T>
+    static inline void swap(T &t, R &r) {
+        r.r_offset = endian::swap(t.r_offset);
+        r.r_info = endian::swap(t.r_info);
+    }
+};
+
+typedef serializable<Elf_RelHack_Traits> Elf_RelHack;
+
+class ElfRelHack_Section: public ElfSection {
+public:
+    ElfRelHack_Section(Elf_Shdr &s)
+    : ElfSection(s, NULL, NULL)
+    {
+        name = elfhack_data;
+    };
+
+    void serialize(std::ofstream &file, char ei_class, char ei_data)
+    {
+        for (std::vector<Elf_RelHack>::iterator i = rels.begin();
+             i != rels.end(); ++i)
+            (*i).serialize(file, ei_class, ei_data);
+    }
+
+    bool isRelocatable() {
+        return true;
+    }
+
+    void push_back(Elf_RelHack &r) {
+        rels.push_back(r);
+        shdr.sh_size = rels.size() * shdr.sh_entsize;
+    }
+private:
+    std::vector<Elf_RelHack> rels;
+};
+
+class ElfRelHackCode_Section: public ElfSection {
+public:
+    ElfRelHackCode_Section(Elf_Shdr &s, Elf &e)
+    : ElfSection(s, NULL, NULL), parent(e) {
+        std::string file(rundir);
+        init = parent.getDynSection()->getSectionForType(DT_INIT);
+        file += "/inject/";
+        switch (parent.getMachine()) {
+        case EM_386:
+            file += "x86";
+            break;
+        case EM_X86_64:
+            file += "x86_64";
+            break;
+        case EM_ARM:
+            file += "arm";
+            break;
+        default:
+            throw std::runtime_error("unsupported architecture");
+        }
+        if (init == NULL)
+            file += "-noinit";
+        file += ".o";
+        std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary);
+        elf = new Elf(inject);
+        if (elf->getType() != ET_REL)
+            throw std::runtime_error("object for injected code is not ET_REL");
+        if (elf->getMachine() != parent.getMachine())
+            throw std::runtime_error("architecture of object for injected code doesn't match");
+
+        // Get all executable sections from the injected code object.
+        // Most of the time, there will only be one for the init function,
+        // but on e.g. x86, there is a separate section for
+        // __i686.get_pc_thunk.$reg
+        for (ElfSection *text = elf->getSection(1); text != NULL;
+             text = text->getNext()) {
+            if ((text->getType() == SHT_PROGBITS) &&
+                (text->getFlags() & SHF_EXECINSTR)) {
+                code.push_back(text);
+                // We need to align this section depending on the greater
+                // alignment required by code sections.
+                if (shdr.sh_addralign < text->getAddrAlign())
+                    shdr.sh_addralign = text->getAddrAlign();
+            }
+        }
+        assert(code.size() != 0);
+
+        // Adjust code sections offsets according to their size
+        std::vector<ElfSection *>::iterator c = code.begin();
+        (*c)->getShdr().sh_addr = 0;
+        for(ElfSection *last = *(c++); c != code.end(); c++) {
+            unsigned int addr = last->getShdr().sh_addr + last->getSize();
+            if (addr & ((*c)->getAddrAlign() - 1))
+                addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
+            (*c)->getShdr().sh_addr = addr;
+        }
+        shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
+        data = new char[shdr.sh_size];
+        char *buf = data;
+        for (c = code.begin(); c != code.end(); c++) {
+            memcpy(buf, (*c)->getData(), (*c)->getSize());
+            buf += (*c)->getSize();
+        }
+        name = elfhack_text;
+    }
+
+    ~ElfRelHackCode_Section() {
+        delete elf;
+    }
+
+    void serialize(std::ofstream &file, char ei_class, char ei_data)
+    {
+        // Readjust code offsets
+        for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++)
+            (*c)->getShdr().sh_addr += getAddr();
+
+        // Apply relocations
+        for (ElfSection *rel = elf->getSection(1); rel != NULL; rel = rel->getNext())
+            if ((rel->getType() == SHT_REL) || (rel->getType() == SHT_RELA)) {
+                ElfSection *section = rel->getInfo().section;
+                if ((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR)) {
+                    if (rel->getType() == SHT_REL)
+                        apply_relocations((ElfRel_Section<Elf_Rel> *)rel, section);
+                    else
+                        apply_relocations((ElfRel_Section<Elf_Rela> *)rel, section);
+                }
+            }
+
+        ElfSection::serialize(file, ei_class, ei_data);
+    }
+
+    bool isRelocatable() {
+        return true;
+    }
+
+private:
+    void apply_pc32_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
+    {
+        *(Elf32_Addr *)(base + r->r_offset) += (Elf32_Addr) (addr - r->r_offset - the_code->getAddr());
+    }
+
+    void apply_pc32_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
+    {
+        *(Elf32_Addr *)(base + r->r_offset) = (Elf32_Addr) (addr + r->r_addend - r->r_offset - the_code->getAddr());
+    }
+
+    void apply_arm_plt32_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
+    {
+        // We don't care about sign_extend because the only case where this is
+        // going to be used only jumps forward.
+        Elf32_Addr addend = (Elf32_Addr) (addr - r->r_offset - the_code->getAddr()) >> 2;
+        addend = (*(Elf32_Addr *)(base + r->r_offset) + addend) & 0x00ffffff;
+        *(Elf32_Addr *)(base + r->r_offset) = (*(Elf32_Addr *)(base + r->r_offset) & 0xff000000) | addend;
+    }
+
+    void apply_arm_plt32_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
+    {
+        // We don't care about sign_extend because the only case where this is
+        // going to be used only jumps forward.
+        Elf32_Addr addend = (Elf32_Addr) (addr - r->r_offset - the_code->getAddr()) >> 2;
+        addend = (r->r_addend + addend) & 0x00ffffff;
+        *(Elf32_Addr *)(base + r->r_offset) = (r->r_addend & 0xff000000) | addend;
+    }
+
+    void apply_gotoff_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
+    {
+        *(Elf32_Addr *)(base + r->r_offset) += (Elf32_Addr) addr;
+    }
+
+    void apply_gotoff_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
+    {
+        *(Elf32_Addr *)(base + r->r_offset) = (Elf32_Addr) addr + r->r_addend;
+    }
+
+    template <typename Rel_Type>
+    void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code)
+    {
+        assert(rel->getType() == Rel_Type::sh_type);
+        char *buf = data + (the_code->getAddr() - code.front()->getAddr());
+        // TODO: various checks on the sections
+        ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
+        ElfStrtab_Section *strtab = (ElfStrtab_Section *)symtab->getLink();
+        for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) {
+            // TODO: various checks on the symbol
+            const char *name = strtab->getStr(symtab->syms[ELF32_R_SYM(r->r_info)].st_name);
+            unsigned int addr;
+            if (symtab->syms[ELF32_R_SYM(r->r_info)].st_shndx == 0) {
+                if (strcmp(name, "relhack") == 0) {
+                    addr = getNext()->getAddr();
+                } else if (strcmp(name, "elf_header") == 0) {
+                    // TODO: change this ungly hack to something better
+                    ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
+                    addr = ehdr->getAddr();
+                } else if (strcmp(name, "original_init") == 0) {
+                    addr = init->getAddr();
+                } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
+                    // We actually don't need a GOT, but need it as a reference for
+                    // GOTOFF relocations. We'll just use the start of the ELF file
+                    addr = 0;
+                } else if (strcmp(name, "") == 0) {
+                    // This is for R_ARM_V4BX, until we find something better
+                    addr = -1;
+                } else {
+                    fprintf(stderr, "Unsupported symbol in relocation: %s\n", name);
+                    break;
+                }
+            } else {
+                ElfSection *section = elf->getSection(symtab->syms[ELF32_R_SYM(r->r_info)].st_shndx);
+                assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR));
+                addr = section->getAddr() + symtab->syms[ELF32_R_SYM(r->r_info)].st_value;
+            }
+            // Do the relocation
+#define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8))
+            switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) {
+            case REL(X86_64, PC32):
+            case REL(386, PC32):
+            case REL(386, GOTPC):
+            case REL(ARM, GOTPC):
+                apply_pc32_relocation(the_code, buf, &*r, addr);
+                break;
+            case REL(ARM, PLT32):
+                apply_arm_plt32_relocation(the_code, buf, &*r, addr);
+                break;
+            case REL(386, GOTOFF):
+            case REL(ARM, GOTOFF):
+                apply_gotoff_relocation(the_code, buf, &*r, addr);
+                break;
+            case REL(ARM, V4BX):
+                // Ignore R_ARM_V4BX relocations
+                break;
+            default:
+                fprintf(stderr, "Unsupported relocation type\n");
+            }
+        }
+    }
+
+    Elf *elf, &parent;
+    std::vector<ElfSection *> code;
+    ElfSection *init;
+};
+
+template <typename Rel_Type>
+int do_relocation_section(Elf *elf, unsigned int rel_type)
+{
+    ElfDynamic_Section *dyn = elf->getDynSection();
+    if (dyn ==NULL) {
+        fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
+        return -1;
+    }
+
+    ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
+    assert(section->getType() == Rel_Type::sh_type);
+
+    Elf32_Shdr relhack32_section =
+        { 0, SHT_PROGBITS, SHF_ALLOC, 0, -1, 0, SHN_UNDEF, 0,
+          Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
+    Elf32_Shdr relhackcode32_section =
+        { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, -1, 0, SHN_UNDEF, 0, 1, 0 };
+    Elf_Shdr relhack_section(relhack32_section);
+    Elf_Shdr relhackcode_section(relhackcode32_section);
+    ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
+    ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf);
+    relhackcode->insertAfter(section);
+    relhack->insertAfter(relhackcode);
+
+    std::vector<Rel_Type> new_rels;
+    Elf_RelHack relhack_entry;
+    relhack_entry.r_offset = relhack_entry.r_info = 0;
+    int entry_sz = (elf->getClass() == ELFCLASS32) ? 4 : 8;
+    for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
+         i != section->rels.end(); i++) {
+        // Don't pack relocations happening in non writable sections.
+        // Our injected code is likely not to be allowed to write there.
+        ElfSection *section = elf->getSectionAt(i->r_offset);
+        if (!(section->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type))
+            new_rels.push_back(*i);
+        else {
+            // TODO: check that i->r_addend == *i->r_offset
+            if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
+                relhack_entry.r_info++;
+            } else {
+                if (relhack_entry.r_offset)
+                    relhack->push_back(relhack_entry);
+                relhack_entry.r_offset = i->r_offset;
+                relhack_entry.r_info = 1;
+            }
+        }
+    }
+    if (relhack_entry.r_offset)
+        relhack->push_back(relhack_entry);
+    // Last entry must be NULL
+    relhack_entry.r_offset = relhack_entry.r_info = 0;
+    relhack->push_back(relhack_entry);
+
+    section->rels.assign(new_rels.begin(), new_rels.end());
+    section->shrink(new_rels.size() * section->getEntSize());
+    ElfLocation *init = new ElfLocation(relhackcode, 0);
+    dyn->setValueForType(DT_INIT, init);
+    // TODO: adjust the value according to the remaining number of relative relocations
+    dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));
+    return 0;
+}
+
+static inline int backup_file(const char *name)
+{
+    std::string fname(name);
+    fname += ".bak";
+    return rename(name, fname.c_str());
+}
+
+void do_file(const char *name, bool backup = false)
+{
+    std::ifstream file(name, std::ios::in|std::ios::binary);
+    Elf *elf = new Elf(file);
+    unsigned int size = elf->getSize();
+    fprintf(stderr, "%s: ", name);
+
+    int exit = -1;
+    switch (elf->getMachine()) {
+    case EM_386:
+        exit = do_relocation_section<Elf_Rel>(elf, R_386_RELATIVE);
+        break;
+    case EM_X86_64:
+        exit = do_relocation_section<Elf_Rela>(elf, R_X86_64_RELATIVE);
+        break;
+    case EM_ARM:
+        exit = do_relocation_section<Elf_Rel>(elf, R_ARM_RELATIVE);
+        break;
+    }
+    if (elf->getSize() >= size)
+        fprintf(stderr, "No gain. Aborting\n");
+    else if (exit == 0) {
+        if (backup && backup_file(name) != 0) {
+            fprintf(stderr, "Couln't create backup file\n");
+        } else {
+            std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
+            elf->write(ofile);
+            fprintf(stderr, "Reduced by %d bytes\n", size - elf->getSize());
+        }
+    }
+    delete elf;
+}
+
+int main(int argc, char *argv[])
+{
+    int arg;
+    bool backup = false;
+    char *lastSlash = rindex(argv[0], '/');
+    if (lastSlash != NULL)
+        rundir = strndup(argv[0], lastSlash - argv[0]);
+    for (arg = 1; arg < argc; arg++) {
+        if (strcmp(argv[arg], "-b") == 0)
+            backup = true;
+        else
+            do_file(argv[arg], backup);
+    }
+
+    free(rundir);
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/elfxx.h
@@ -0,0 +1,582 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is elfhack.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mike Hommey <mh@glandium.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <stdexcept>
+#include <list>
+#include <vector>
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <elf.h>
+#include <asm/byteorder.h>
+
+// Technically, __*_to_cpu and __cpu_to* function are equivalent,
+// so swap can use either of both.
+#define def_swap(endian, type, bits) \
+static inline type ## bits ## _t swap(type ## bits ## _t i) { \
+    return __ ## endian ## bits ## _to_cpu(i); \
+}
+
+class little_endian {
+public:
+def_swap(le, uint, 16);
+def_swap(le, uint, 32);
+def_swap(le, uint, 64);
+def_swap(le, int, 16);
+def_swap(le, int, 32);
+def_swap(le, int, 64);
+};
+
+class big_endian {
+public:
+def_swap(be, uint, 16);
+def_swap(be, uint, 32);
+def_swap(be, uint, 64);
+def_swap(be, int, 16);
+def_swap(be, int, 32);
+def_swap(be, int, 64);
+};
+
+// forward declaration
+class ElfSection;
+class ElfSegment;
+// TODO: Rename Elf_* types
+class Elf_Ehdr;
+class Elf_Phdr;
+class Elf;
+class ElfDynamic_Section;
+class ElfStrtab_Section;
+
+class Elf_Ehdr_Traits {
+public:
+    typedef Elf32_Ehdr Type32;
+    typedef Elf64_Ehdr Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class Elf_Phdr_Traits {
+public:
+    typedef Elf32_Phdr Type32;
+    typedef Elf64_Phdr Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class Elf_Shdr_Traits {
+public:
+    typedef Elf32_Shdr Type32;
+    typedef Elf64_Shdr Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class Elf_Dyn_Traits {
+public:
+    typedef Elf32_Dyn Type32;
+    typedef Elf64_Dyn Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class Elf_Sym_Traits {
+public:
+    typedef Elf32_Sym Type32;
+    typedef Elf64_Sym Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class Elf_Rel_Traits {
+public:
+    typedef Elf32_Rel Type32;
+    typedef Elf64_Rel Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class Elf_Rela_Traits {
+public:
+    typedef Elf32_Rela Type32;
+    typedef Elf64_Rela Type64;
+
+    template <class endian, typename R, typename T>
+    static void swap(T &t, R &r);
+};
+
+class ElfValue {
+public:
+    virtual unsigned int getValue() { return 0; }
+    virtual ElfSection *getSection() { return NULL; }
+};
+
+class ElfPlainValue: public ElfValue {
+    unsigned int value;
+public:
+    ElfPlainValue(unsigned int val): value(val) {};
+    unsigned int getValue() { return value; }
+};
+
+class ElfLocation: public ElfValue {
+    ElfSection *section;
+    unsigned int offset;
+public:
+    ElfLocation(): section(NULL), offset(0) {};
+    ElfLocation(ElfSection *section, unsigned int offset): section(section), offset(offset) {};
+    ElfLocation(unsigned int location, Elf *elf);
+    unsigned int getValue();
+    ElfSection *getSection() { return section; }
+};
+
+class ElfSize: public ElfValue {
+    ElfSection *section;
+public:
+    ElfSize(ElfSection *s): section(s) {};
+    unsigned int getValue();
+    ElfSection *getSection() { return section; }
+};
+
+class ElfEntSize: public ElfValue {
+    ElfSection *section;
+public:
+    ElfEntSize(ElfSection *s): section(s) {};
+    unsigned int getValue();
+    ElfSection *getSection() { return section; }
+};
+
+template <typename T>
+class serializable: public T::Type32 {
+public:
+    serializable() {};
+    serializable(const typename T::Type32 &p): T::Type32(p) {};
+    serializable(std::ifstream &file, char ei_class, char ei_data)
+    {
+        if (ei_class == ELFCLASS32) {
+            typename T::Type32 e;
+            file.read((char *)&e, sizeof(e));
+            if (ei_data == ELFDATA2LSB) {
+                T::template swap<little_endian>(e, *this);
+                return;
+            } else if (ei_data == ELFDATA2MSB) {
+                T::template swap<big_endian>(e, *this);
+                return;
+            }
+        } else if (ei_class == ELFCLASS64) {
+            typename T::Type64 e;
+            file.read((char *)&e, sizeof(e));
+            if (ei_data == ELFDATA2LSB) {
+                T::template swap<little_endian>(e, *this);
+                return;
+            } else if (ei_data == ELFDATA2MSB) {
+                T::template swap<big_endian>(e, *this);
+                return;
+            }
+        }
+        throw std::runtime_error("Unsupported ELF class or data encoding");
+    }
+
+    void serialize(std::ofstream &file, char ei_class, char ei_data)
+    {
+        if (ei_class == ELFCLASS32) {
+            typename T::Type32 e;
+            if (ei_data == ELFDATA2LSB) {
+                T::template swap<little_endian>(*this, e);
+                file.write((char *)&e, sizeof(e));
+                return;
+            } else if (ei_data == ELFDATA2MSB) {
+                T::template swap<big_endian>(*this, e);
+                file.write((char *)&e, sizeof(e));
+                return;
+            }
+        } else if (ei_class == ELFCLASS64) {
+            typename T::Type64 e;
+            if (ei_data == ELFDATA2LSB) {
+                T::template swap<little_endian>(*this, e);
+                file.write((char *)&e, sizeof(e));
+                return;
+            } else if (ei_data == ELFDATA2MSB) {
+                T::template swap<big_endian>(*this, e);
+                file.write((char *)&e, sizeof(e));
+                return;
+            }
+        }
+        throw std::runtime_error("Unsupported ELF class or data encoding");
+    }
+
+    static inline int size(char ei_class)
+    {
+        if (ei_class == ELFCLASS32)
+            return sizeof(typename T::Type32);
+        else if (ei_class == ELFCLASS64)
+            return sizeof(typename T::Type64);
+        return 0;
+    }
+};
+
+typedef serializable<Elf_Shdr_Traits> Elf_Shdr;
+
+class Elf {
+public:
+    Elf(std::ifstream &file);
+    ~Elf();
+
+    /* index == -1 is treated as index == ehdr.e_shstrndx */
+    ElfSection *getSection(int index);
+
+    ElfSection *getSectionAt(unsigned int offset);
+
+    ElfDynamic_Section *getDynSection();
+
+    void write(std::ofstream &file);
+
+    char getClass();
+    char getData();
+    char getType();
+    char getMachine();
+    unsigned int getSize();
+private:
+    Elf_Ehdr *ehdr;
+    ElfLocation eh_entry;
+    ElfStrtab_Section *eh_shstrndx;
+    ElfSection **sections;
+    std::vector<ElfSegment *> segments;
+    ElfSection *shdr_section, *phdr_section;
+    /* Values used only during initialization */
+    Elf_Shdr **tmp_shdr;
+    std::ifstream *tmp_file;
+};
+
+class ElfSection {
+public:
+    typedef union {
+        ElfSection *section;
+        int index;
+    } SectionInfo;
+
+    ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent);
+
+    virtual ~ElfSection() {
+        delete[] data;
+    }
+
+    const char *getName() { return name; }
+    unsigned int getType() { return shdr.sh_type; }
+    unsigned int getFlags() { return shdr.sh_flags; }
+    unsigned int getAddr();
+    unsigned int getSize() { return shdr.sh_size; }
+    unsigned int getAddrAlign() { return shdr.sh_addralign; }
+    unsigned int getEntSize() { return shdr.sh_entsize; }
+    const char *getData() { return data; }
+    ElfSection *getLink() { return link; }
+    SectionInfo getInfo() { return info; }
+
+    void shrink(unsigned int newsize) {
+        if (newsize < shdr.sh_size) {
+            shdr.sh_size = newsize;
+            if (next)
+                next->markDirty();
+        }
+    }
+
+    unsigned int getOffset();
+    int getIndex();
+    Elf_Shdr &getShdr();
+
+    ElfSection *getNext() { return next; }
+    ElfSection *getPrevious() { return previous; }
+
+    virtual bool isRelocatable() {
+        return ((getType() != SHT_NULL) &&
+                (getType() != SHT_NOBITS) &&
+                (getType() != SHT_PROGBITS) &&
+                (getFlags() == SHF_ALLOC));
+    }
+
+    void insertAfter(ElfSection *section) {
+        if (previous != NULL)
+            previous->next = next;
+        if (next != NULL)
+            next->previous = previous;
+        previous = section;
+        if (section != NULL) {
+            next = section->next;
+            section->next = this;
+        } else
+            next = NULL;
+        if (next != NULL)
+            next->previous = this;
+        markDirty();
+    }
+
+    void markDirty() {
+        if (link != NULL)
+            shdr.sh_link = -1;
+        if (info.index)
+            shdr.sh_info = -1;
+        shdr.sh_offset = -1;
+        if (isRelocatable())
+            shdr.sh_addr = -1;
+        if (next)
+            next->markDirty();
+    }
+
+    virtual void serialize(std::ofstream &file, char ei_class, char ei_data)
+    {
+        if (getType() == SHT_NOBITS)
+            return;
+        file.seekp(getOffset());
+        file.write(data, getSize());
+    }
+protected:
+    Elf_Shdr shdr;
+    char *data;
+    const char *name;
+private:
+    ElfSection *link;
+    SectionInfo info;
+    ElfSection *next, *previous;
+    int index;
+};
+
+class ElfSegment {
+public:
+    ElfSegment(Elf_Phdr *phdr);
+
+    unsigned int getType() { return type; }
+    unsigned int getFlags() { return flags; }
+    unsigned int getAlign() { return type == PT_LOAD ? 0x1000 : align; /* TODO: remove this gross hack */ }
+
+    ElfSection *getFirstSection() { return sections.empty() ? NULL : sections.front(); }
+    int getVPDiff() { return v_p_diff; }
+    unsigned int getFileSize();
+    unsigned int getMemSize();
+
+    void addSection(ElfSection *section);
+
+    std::list<ElfSection *>::iterator begin() { return sections.begin(); }
+    std::list<ElfSection *>::iterator end() { return sections.end(); }
+
+    ElfSegment *splitBefore(ElfSection *section);
+private:
+    unsigned int type;
+    int v_p_diff; // Difference between physical and virtual address
+    unsigned int flags;
+    unsigned int align;
+    std::list<ElfSection *> sections;
+};
+
+class Elf_Ehdr: public serializable<Elf_Ehdr_Traits>, public ElfSection {
+public:
+    Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data);
+    void serialize(std::ofstream &file, char ei_class, char ei_data)
+    {
+        serializable<Elf_Ehdr_Traits>::serialize(file, ei_class, ei_data);
+    }
+};
+
+class Elf_Phdr: public serializable<Elf_Phdr_Traits> {
+public:
+    Elf_Phdr() {};
+    Elf_Phdr(std::ifstream &file, char ei_class, char ei_data)
+    : serializable<Elf_Phdr_Traits>(file, ei_class, ei_data) {};
+    bool contains(ElfSection *section)
+    {
+        unsigned int size = section->getSize();
+        unsigned int addr = section->getAddr();
+        // This may be biased, but should work in most cases
+        if ((section->getFlags() & SHF_ALLOC) == 0)
+            return false;
+        return (addr >= p_vaddr) &&
+               (addr + size <= p_vaddr + p_memsz);
+
+    }
+};
+
+typedef serializable<Elf_Dyn_Traits> Elf_Dyn;
+
+struct Elf_DynValue {
+    unsigned int tag;
+    ElfValue *value;
+};
+
+class ElfDynamic_Section: public ElfSection {
+public:
+    ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
+    ~ElfDynamic_Section();
+
+    void serialize(std::ofstream &file, char ei_class, char ei_data);
+
+    ElfSection *getSectionForType(unsigned int tag);
+    void setValueForType(unsigned int tag, ElfValue *val);
+private:
+    std::vector<Elf_DynValue> dyns;
+};
+
+typedef serializable<Elf_Sym_Traits> Elf_Sym;
+
+class ElfSymtab_Section: public ElfSection {
+public:
+    ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
+
+//private: // Until we have a real API
+    std::vector<Elf_Sym> syms;
+};
+
+class Elf_Rel: public serializable<Elf_Rel_Traits> {
+public:
+    Elf_Rel(std::ifstream &file, char ei_class, char ei_data)
+    : serializable<Elf_Rel_Traits>(file, ei_class, ei_data) {};
+
+    static const unsigned int sh_type = SHT_REL;
+    static const unsigned int d_tag = DT_REL;
+    static const unsigned int d_tag_count = DT_RELCOUNT;
+};
+
+class Elf_Rela: public serializable<Elf_Rela_Traits> {
+public:
+    Elf_Rela(std::ifstream &file, char ei_class, char ei_data)
+    : serializable<Elf_Rela_Traits>(file, ei_class, ei_data) {};
+
+    static const unsigned int sh_type = SHT_RELA;
+    static const unsigned int d_tag = DT_RELA;
+    static const unsigned int d_tag_count = DT_RELACOUNT;
+};
+
+template <class Rel>
+class ElfRel_Section: public ElfSection {
+public:
+    ElfRel_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+    : ElfSection(s, file, parent)
+    {
+        int pos = file->tellg();
+        file->seekg(shdr.sh_offset);
+        for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
+            Rel r(*file, parent->getClass(), parent->getData());
+            rels.push_back(r);
+        }
+        file->seekg(pos);
+    }
+
+    void serialize(std::ofstream &file, char ei_class, char ei_data)
+    {
+        for (typename std::vector<Rel>::iterator i = rels.begin();
+             i != rels.end(); ++i)
+            (*i).serialize(file, ei_class, ei_data);
+    }
+//private: // Until we have a real API
+    std::vector<Rel> rels;
+};
+
+class ElfStrtab_Section: public ElfSection {
+public:
+    ElfStrtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+    : ElfSection(s, file, parent)
+    {
+        table.push_back(table_storage(data, shdr.sh_size));
+    }
+
+    ~ElfStrtab_Section()
+    {
+        for (std::vector<table_storage>::iterator t = table.begin() + 1;
+             t != table.end(); t++)
+            delete[] t->buf;
+    }
+
+    const char *getStr(unsigned int index);
+
+    const char *getStr(const char *string);
+
+    unsigned int getStrIndex(const char *string);
+
+    void serialize(std::ofstream &file, char ei_class, char ei_data);
+private:
+    struct table_storage {
+        unsigned int size, used;
+        char *buf;
+
+        table_storage(): size(4096), used(0), buf(new char[4096]) {}
+        table_storage(const char *data, unsigned int sz)
+        : size(sz), used(sz), buf(const_cast<char *>(data)) {}
+    };
+    std::vector<table_storage> table;
+};
+
+inline char Elf::getClass() {
+    return ehdr->e_ident[EI_CLASS];
+}
+
+inline char Elf::getData() {
+    return ehdr->e_ident[EI_DATA];
+}
+
+inline char Elf::getType() {
+    return ehdr->e_type;
+}
+
+inline char Elf::getMachine() {
+    return ehdr->e_machine;
+}
+
+inline unsigned int Elf::getSize() {
+    ElfSection *section;
+    for (section = shdr_section /* It's usually not far from the end */;
+        section->getNext() != NULL; section = section->getNext());
+    return section->getOffset() + section->getSize();
+}
+
+inline ElfLocation::ElfLocation(unsigned int location, Elf *elf) {
+    section = elf->getSectionAt(location);
+    offset = location - section->getAddr();
+}
+
+inline unsigned int ElfLocation::getValue() {
+    return section->getAddr() + offset;
+}
+
+inline unsigned int ElfSize::getValue() {
+    return section->getSize();
+}
+
+inline unsigned int ElfEntSize::getValue() {
+    return section->getEntSize();
+}
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/inject.c
@@ -0,0 +1,71 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is elfhack.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mike Hommey <mh@glandium.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <stdint.h>
+#include <elf.h>
+
+/* The Android NDK headers define those */
+#undef Elf_Ehdr
+#undef Elf_Addr
+
+#if BITS == 32
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Addr Elf32_Addr
+#else
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Addr Elf64_Addr
+#endif
+
+extern __attribute__((visibility("hidden"))) void original_init(int argc, char **argv, char **env);
+
+extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[];
+extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header;
+
+void init(int argc, char **argv, char **env)
+{
+    Elf32_Rel *rel;
+    Elf_Addr *ptr, *start;
+    for (rel = relhack; rel->r_offset; rel++) {
+        start = (Elf_Addr *)((intptr_t)&elf_header + rel->r_offset);
+        for (ptr = start; ptr < &start[rel->r_info]; ptr++)
+            *ptr += (intptr_t)&elf_header;
+    }
+
+#ifndef NOINIT
+    original_init(argc, argv, env);
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/test.c
@@ -0,0 +1,148 @@
+#ifdef DEF
+DEF(This)
+DEF(is)
+DEF(a)
+DEF(test)
+DEF(of)
+DEF(string)
+DEF(array)
+DEF(for)
+DEF(use)
+DEF(with)
+DEF(elfhack)
+DEF(to)
+DEF(see)
+DEF(whether)
+DEF(it)
+DEF(breaks)
+DEF(anything)
+DEF(but)
+DEF(one)
+DEF(needs)
+DEF(quite)
+DEF(some)
+DEF(strings)
+DEF(before)
+DEF(the)
+DEF(program)
+DEF(can)
+DEF(do)
+DEF(its)
+DEF(work)
+DEF(efficiently)
+DEF(Without)
+DEF(enough)
+DEF(data)
+DEF(relocation)
+DEF(sections)
+DEF(are)
+DEF(not)
+DEF(sufficiently)
+DEF(large)
+DEF(and)
+DEF(injected)
+DEF(code)
+DEF(wouldnt)
+DEF(fit)
+DEF(Said)
+DEF(otherwise)
+DEF(we)
+DEF(need)
+DEF(more)
+DEF(words)
+DEF(than)
+DEF(up)
+DEF(here)
+DEF(so)
+DEF(that)
+DEF(relocations)
+DEF(take)
+DEF(significant)
+DEF(bytes)
+DEF(amounts)
+DEF(which)
+DEF(isnt)
+DEF(exactly)
+DEF(easily)
+DEF(achieved)
+DEF(like)
+DEF(this)
+DEF(Actually)
+DEF(I)
+DEF(must)
+DEF(cheat)
+DEF(by)
+DEF(including)
+DEF(these)
+DEF(phrases)
+DEF(several)
+DEF(times)
+
+#else
+#include <stdlib.h>
+#include <stdio.h>
+
+#define DEF(w) static const char str_ ## w[] = #w;
+#include "test.c"
+#undef DEF
+
+const char *strings[] = {
+#define DEF(w) str_ ## w,
+#include "test.c"
+#include "test.c"
+#include "test.c"
+};
+
+/* Create a hole between two zones of relative relocations */
+const int hole[] = {
+    42, 42, 42, 42
+};
+
+const char *strings2[] = {
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#undef DEF
+};
+
+/* On ARM, this creates a .tbss section before .init_array, which
+ * elfhack could then pick instead of .init_array */
+__thread int foo;
+
+__attribute__((constructor)) void end_test() {
+    static int count = 0;
+    // Only exit when both constructors have been called
+    if (++count == 2) {
+        fprintf(stderr, "PASS\n");
+        exit(0);
+    }
+}
+
+__attribute__((constructor)) void test() {
+    int i = 0, j = 0;
+#define DEF_(a,i,w) \
+    if (a[i++] != str_ ## w) { \
+        fprintf(stderr, "FAIL\n"); \
+        exit(1); \
+    }
+#define DEF(w) DEF_(strings,i,w)
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#undef DEF
+#define DEF(w) DEF_(strings2,j,w)
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#undef DEF
+    if (i != sizeof(strings)/sizeof(strings[0]) &&
+        j != sizeof(strings2)/sizeof(strings2[0]))
+        fprintf(stderr, "WARNING: Test doesn't cover the whole array\n");
+    end_test();
+}
+
+#endif