зеркало из https://github.com/mozilla/pjs.git
Bug 606145 part 2 - Import elfhack code. r=tglek,a=bsmedberg
This commit is contained in:
Родитель
035800de28
Коммит
0198b3bfc0
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
Загрузка…
Ссылка в новой задаче