author | Mike Hommey <mh+mozilla@glandium.org> |
Mon, 24 Jan 2011 09:56:40 +0100 | |
changeset 61170 | a1ccb1c489baeeb2e738ce9173b5ee079b0243f9 |
parent 61169 | 0d947763fe67c2a728141f97f328ce3b8055e270 |
child 61171 | 9a6de1e28d4bf476870fec0d01f29e4be6f05dcc |
push id | unknown |
push user | unknown |
push date | unknown |
reviewers | tglek, bsmedberg |
bugs | 606145 |
milestone | 2.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
|
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