Bug 606145 part 2 - Import elfhack code. r=tglek,a=bsmedberg

This commit is contained in:
Mike Hommey 2011-01-24 09:56:40 +01:00
Родитель 035800de28
Коммит 0198b3bfc0
7 изменённых файлов: 2068 добавлений и 0 удалений

28
build/unix/elfhack/README Normal file
Просмотреть файл

@ -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.

Просмотреть файл

@ -0,0 +1,3 @@
int main() {
return 1;
}

822
build/unix/elfhack/elf.cpp Normal file
Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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;
}

582
build/unix/elfhack/elfxx.h Normal file
Просмотреть файл

@ -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();
}

Просмотреть файл

@ -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
}

148
build/unix/elfhack/test.c Normal file
Просмотреть файл

@ -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