summaryrefslogtreecommitdiffstats
path: root/build/unix/elfhack/elfhack.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /build/unix/elfhack/elfhack.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'build/unix/elfhack/elfhack.cpp')
-rw-r--r--build/unix/elfhack/elfhack.cpp823
1 files changed, 823 insertions, 0 deletions
diff --git a/build/unix/elfhack/elfhack.cpp b/build/unix/elfhack/elfhack.cpp
new file mode 100644
index 000000000..8c1184237
--- /dev/null
+++ b/build/unix/elfhack/elfhack.cpp
@@ -0,0 +1,823 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#undef NDEBUG
+#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
+#ifndef R_ARM_CALL
+#define R_ARM_CALL 0x1c
+#endif
+#ifndef R_ARM_JUMP24
+#define R_ARM_JUMP24 0x1d
+#endif
+#ifndef R_ARM_THM_JUMP24
+#define R_ARM_THM_JUMP24 0x1e
+#endif
+
+char *rundir = nullptr;
+
+template <typename T>
+struct wrapped {
+ T value;
+};
+
+class Elf_Addr_Traits {
+public:
+ typedef wrapped<Elf32_Addr> Type32;
+ typedef wrapped<Elf64_Addr> Type64;
+
+ template <class endian, typename R, typename T>
+ static inline void swap(T &t, R &r) {
+ r.value = endian::swap(t.value);
+ }
+};
+
+typedef serializable<Elf_Addr_Traits> Elf_Addr;
+
+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, nullptr, nullptr)
+ {
+ 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, unsigned int init)
+ : ElfSection(s, nullptr, nullptr), parent(e), init(init) {
+ std::string file(rundir);
+ 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");
+ }
+ 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");
+
+ ElfSymtab_Section *symtab = nullptr;
+
+ // Find the symbol table.
+ for (ElfSection *section = elf->getSection(1); section != nullptr;
+ section = section->getNext()) {
+ if (section->getType() == SHT_SYMTAB)
+ symtab = (ElfSymtab_Section *) section;
+ }
+ if (symtab == nullptr)
+ throw std::runtime_error("Couldn't find a symbol table for the injected code");
+
+ // Find the init symbol
+ entry_point = -1;
+ Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit");
+ if (!sym)
+ throw std::runtime_error("Couldn't find an 'init' symbol in the injected code");
+
+ entry_point = sym->value.getValue();
+
+ // Get all relevant sections from the injected code object.
+ add_code_section(sym->value.getSection());
+
+ // 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;
+ // We need to align this section depending on the greater
+ // alignment required by code sections.
+ if (shdr.sh_addralign < (*c)->getAddrAlign())
+ shdr.sh_addralign = (*c)->getAddrAlign();
+ }
+ 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 (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) {
+ for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext())
+ if (((rel->getType() == SHT_REL) ||
+ (rel->getType() == SHT_RELA)) &&
+ (rel->getInfo().section == *c)) {
+ if (rel->getType() == SHT_REL)
+ apply_relocations((ElfRel_Section<Elf_Rel> *)rel, *c);
+ else
+ apply_relocations((ElfRel_Section<Elf_Rela> *)rel, *c);
+ }
+ }
+
+ ElfSection::serialize(file, ei_class, ei_data);
+ }
+
+ bool isRelocatable() {
+ return true;
+ }
+
+ unsigned int getEntryPoint() {
+ return entry_point;
+ }
+private:
+ void add_code_section(ElfSection *section)
+ {
+ if (section) {
+ /* Don't add section if it's already been added in the past */
+ for (auto s = code.begin(); s != code.end(); ++s) {
+ if (section == *s)
+ return;
+ }
+ code.push_back(section);
+ find_code(section);
+ }
+ }
+
+ /* Look at the relocations associated to the given section to find other
+ * sections that it requires */
+ void find_code(ElfSection *section)
+ {
+ for (ElfSection *s = elf->getSection(1); s != nullptr;
+ s = s->getNext()) {
+ if (((s->getType() == SHT_REL) ||
+ (s->getType() == SHT_RELA)) &&
+ (s->getInfo().section == section)) {
+ if (s->getType() == SHT_REL)
+ scan_relocs_for_code((ElfRel_Section<Elf_Rel> *)s);
+ else
+ scan_relocs_for_code((ElfRel_Section<Elf_Rela> *)s);
+ }
+ }
+ }
+
+ template <typename Rel_Type>
+ void scan_relocs_for_code(ElfRel_Section<Rel_Type> *rel)
+ {
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
+ for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) {
+ ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
+ add_code_section(section);
+ }
+ }
+
+ class pc32_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ return addr + addend - offset - base_addr;
+ }
+ };
+
+ class arm_plt32_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, 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 tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2;
+ tmp = (addend + tmp) & 0x00ffffff;
+ return (addend & 0xff000000) | tmp;
+ }
+ };
+
+ class arm_thm_jump24_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ /* Follows description of b.w and bl instructions as per
+ ARM Architecture Reference Manual ARMĀ® v7-A and ARMĀ® v7-R edition, A8.6.16
+ We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl.
+ We don't care about sign_extend because the only case where this is
+ going to be used only jumps forward. */
+ Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr);
+ unsigned int word0 = addend & 0xffff,
+ word1 = addend >> 16;
+
+ /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */
+ unsigned int type = (word1 & 0xd000) >> 12;
+ if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9))
+ throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>");
+
+ /* When the target address points to ARM code, switch a BL to a
+ * BLX. This however can't be done with a B.W without adding a
+ * trampoline, which is not supported as of now. */
+ if ((addr & 0x1) == 0) {
+ if (type == 0x9)
+ throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code");
+ /* The address of the target is always relative to a 4-bytes
+ * aligned address, so if the address of the BL instruction is
+ * not 4-bytes aligned, adjust for it. */
+ if ((base_addr + offset) & 0x2)
+ tmp += 2;
+ /* Encoding T2 of BLX is 11x0. */
+ type = 0xc;
+ }
+
+ unsigned int s = (word0 & (1 << 10)) >> 10;
+ unsigned int j1 = (word1 & (1 << 13)) >> 13;
+ unsigned int j2 = (word1 & (1 << 11)) >> 11;
+ unsigned int i1 = j1 ^ s ? 0 : 1;
+ unsigned int i2 = j2 ^ s ? 0 : 1;
+
+ tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1));
+
+ s = (tmp & (1 << 24)) >> 24;
+ j1 = ((tmp & (1 << 23)) >> 23) ^ !s;
+ j2 = ((tmp & (1 << 22)) >> 22) ^ !s;
+
+ return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) |
+ (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
+ }
+ };
+
+ class gotoff_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ return addr + addend;
+ }
+ };
+
+ template <class relocation_type>
+ void apply_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
+ {
+ relocation_type relocation;
+ Elf32_Addr value;
+ memcpy(&value, base + r->r_offset, 4);
+ value = relocation(the_code->getAddr(), r->r_offset, value, addr);
+ memcpy(base + r->r_offset, &value, 4);
+ }
+
+ template <class relocation_type>
+ void apply_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
+ {
+ relocation_type relocation;
+ Elf32_Addr value = relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr);
+ memcpy(base + r->r_offset, &value, 4);
+ }
+
+ 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();
+ 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 = symtab->syms[ELF32_R_SYM(r->r_info)].name;
+ unsigned int addr;
+ if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == nullptr) {
+ 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;
+ } 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 {
+ throw std::runtime_error("Unsupported symbol in relocation");
+ }
+ } else {
+ ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
+ assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR));
+ addr = symtab->syms[ELF32_R_SYM(r->r_info)].value.getValue();
+ }
+ // 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):
+ case REL(ARM, REL32):
+ apply_relocation<pc32_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(ARM, CALL):
+ case REL(ARM, JUMP24):
+ case REL(ARM, PLT32):
+ apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(ARM, THM_PC22 /* THM_CALL */):
+ case REL(ARM, THM_JUMP24):
+ apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(386, GOTOFF):
+ case REL(ARM, GOTOFF):
+ apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(ARM, V4BX):
+ // Ignore R_ARM_V4BX relocations
+ break;
+ default:
+ throw std::runtime_error("Unsupported relocation type");
+ }
+ }
+ }
+
+ Elf *elf, &parent;
+ std::vector<ElfSection *> code;
+ unsigned int init;
+ int entry_point;
+};
+
+unsigned int get_addend(Elf_Rel *rel, Elf *elf) {
+ ElfLocation loc(rel->r_offset, elf);
+ Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
+ return addr.value;
+}
+
+unsigned int get_addend(Elf_Rela *rel, Elf *elf) {
+ return rel->r_addend;
+}
+
+void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) {
+ ElfLocation loc(rel->r_offset, elf);
+ Elf_Addr addr;
+ addr.value = value;
+ addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
+}
+
+void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) {
+ // ld puts the value of relocated relocations both in the addend and
+ // at r_offset. For consistency, keep it that way.
+ set_relative_reloc((Elf_Rel *)rel, elf, value);
+ rel->r_addend = value;
+}
+
+void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill)
+{
+ std::list<ElfSection *>::iterator it = segment->begin();
+ for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) {
+ // When two consecutive non-SHT_NOBITS sections are apart by more
+ // than the alignment of the section, the second can be moved closer
+ // to the first, but this requires the segment to be split.
+ if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) &&
+ ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) {
+ // Probably very wrong.
+ Elf_Phdr phdr;
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0;
+ phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
+ phdr.p_flags = segment->getFlags();
+ phdr.p_align = segment->getAlign();
+ phdr.p_filesz = (unsigned int)-1;
+ phdr.p_memsz = (unsigned int)-1;
+ ElfSegment *newSegment = new ElfSegment(&phdr);
+ elf->insertSegmentAfter(segment, newSegment);
+ ElfSection *section = *it;
+ for (; it != segment->end(); ++it) {
+ newSegment->addSection(*it);
+ }
+ for (it = newSegment->begin(); it != newSegment->end(); it++) {
+ segment->removeSection(*it);
+ }
+ // Fill the virtual address space gap left between the two PT_LOADs
+ // with a new PT_LOAD with no permissions. This avoids the linker
+ // (especially bionic's) filling the gap with anonymous memory,
+ // which breakpad doesn't like.
+ // /!\ running strip on a elfhacked binary will break this filler
+ // PT_LOAD.
+ if (!fill)
+ break;
+ // Insert dummy segment to normalize the entire Elf with the header
+ // sizes adjusted, before inserting a filler segment.
+ {
+ memset(&phdr, 0, sizeof(phdr));
+ ElfSegment dummySegment(&phdr);
+ elf->insertSegmentAfter(segment, &dummySegment);
+ elf->normalize();
+ elf->removeSegment(&dummySegment);
+ }
+ ElfSection *previous = section->getPrevious();
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1);
+ phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
+ phdr.p_flags = 0;
+ phdr.p_align = 0;
+ phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr;
+ phdr.p_memsz = phdr.p_filesz;
+ if (phdr.p_filesz) {
+ newSegment = new ElfSegment(&phdr);
+ assert(newSegment->isElfHackFillerSegment());
+ elf->insertSegmentAfter(segment, newSegment);
+ } else {
+ elf->normalize();
+ }
+ break;
+ }
+ }
+}
+
+template <typename Rel_Type>
+int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill)
+{
+ ElfDynamic_Section *dyn = elf->getDynSection();
+ if (dyn == nullptr) {
+ fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
+ return -1;
+ }
+
+ ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);
+
+ 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, (Elf32_Off)-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, (Elf32_Off)-1, 0,
+ SHN_UNDEF, 0, 1, 0 };
+
+ unsigned int entry_sz = Elf_Addr::size(elf->getClass());
+
+ // The injected code needs to be executed before any init code in the
+ // binary. There are three possible cases:
+ // - The binary has no init code at all. In this case, we will add a
+ // DT_INIT entry pointing to the injected code.
+ // - The binary has a DT_INIT entry. In this case, we will interpose:
+ // we change DT_INIT to point to the injected code, and have the
+ // injected code call the original DT_INIT entry point.
+ // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this
+ // case, we interpose as well, by replacing the first entry in the
+ // array to point to the injected code, and have the injected code
+ // call the original first entry.
+ // The binary may have .ctors instead of DT_INIT_ARRAY, for its init
+ // functions, but this falls into the second case above, since .ctors
+ // are actually run by DT_INIT code.
+ ElfValue *value = dyn->getValueForType(DT_INIT);
+ unsigned int original_init = value ? value->getValue() : 0;
+ ElfSection *init_array = nullptr;
+ if (!value || !value->getValue()) {
+ value = dyn->getValueForType(DT_INIT_ARRAYSZ);
+ if (value && value->getValue() >= entry_sz)
+ init_array = dyn->getSectionForType(DT_INIT_ARRAY);
+ }
+
+ Elf_Shdr relhack_section(relhack32_section);
+ Elf_Shdr relhackcode_section(relhackcode32_section);
+ ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
+
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink();
+ Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual");
+
+ std::vector<Rel_Type> new_rels;
+ Elf_RelHack relhack_entry;
+ relhack_entry.r_offset = relhack_entry.r_info = 0;
+ size_t init_array_reloc = 0;
+ for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
+ i != section->rels.end(); i++) {
+ // We don't need to keep R_*_NONE relocations
+ if (!ELF32_R_TYPE(i->r_info))
+ continue;
+ ElfLocation loc(i->r_offset, elf);
+ // __cxa_pure_virtual is a function used in vtables to point at pure
+ // virtual methods. The __cxa_pure_virtual function usually abort()s.
+ // These functions are however normally never called. In the case
+ // where they would, jumping to the null address instead of calling
+ // __cxa_pure_virtual is going to work just as well. So we can remove
+ // relocations for the __cxa_pure_virtual symbol and null out the
+ // content at the offset pointed by the relocation.
+ if (sym) {
+ if (sym->defined) {
+ // If we are statically linked to libstdc++, the
+ // __cxa_pure_virtual symbol is defined in our lib, and we
+ // have relative relocations (rel_type) for it.
+ if (ELF32_R_TYPE(i->r_info) == rel_type) {
+ Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData());
+ if (addr.value == sym->value.getValue()) {
+ memset((char *)loc.getBuffer(), 0, entry_sz);
+ continue;
+ }
+ }
+ } else {
+ // If we are dynamically linked to libstdc++, the
+ // __cxa_pure_virtual symbol is undefined in our lib, and we
+ // have absolute relocations (rel_type2) for it.
+ if ((ELF32_R_TYPE(i->r_info) == rel_type2) &&
+ (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) {
+ memset((char *)loc.getBuffer(), 0, entry_sz);
+ continue;
+ }
+ }
+ }
+ // Keep track of the relocation associated with the first init_array entry.
+ if (init_array && i->r_offset == init_array->getAddr()) {
+ if (init_array_reloc) {
+ fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n");
+ return -1;
+ }
+ new_rels.push_back(*i);
+ init_array_reloc = new_rels.size();
+ } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
+ (relro && (i->r_offset >= relro->getAddr()) &&
+ (i->r_offset < relro->getAddr() + relro->getMemSize()))) {
+ // Don't pack relocations happening in non writable sections.
+ // Our injected code is likely not to be allowed to write there.
+ 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 nullptr
+ relhack_entry.r_offset = relhack_entry.r_info = 0;
+ relhack->push_back(relhack_entry);
+
+ unsigned int old_end = section->getOffset() + section->getSize();
+
+ if (init_array) {
+ if (! init_array_reloc) {
+ fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n");
+ return -1;
+ }
+ Rel_Type *rel = &new_rels[init_array_reloc - 1];
+ unsigned int addend = get_addend(rel, elf);
+ // Use relocated value of DT_INIT_ARRAY's first entry for the
+ // function to be called by the injected code.
+ if (ELF32_R_TYPE(rel->r_info) == rel_type) {
+ original_init = addend;
+ } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) {
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
+ original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend;
+ } else {
+ fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n");
+ return -1;
+ }
+ }
+
+ section->rels.assign(new_rels.begin(), new_rels.end());
+ section->shrink(new_rels.size() * section->getEntSize());
+
+ ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init);
+ relhackcode->insertBefore(section);
+ relhack->insertAfter(relhackcode);
+ if (section->getOffset() + section->getSize() >= old_end) {
+ fprintf(stderr, "No gain. Skipping\n");
+ return -1;
+ }
+
+ // Adjust PT_LOAD segments
+ for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
+ segment = elf->getSegmentByType(PT_LOAD, segment)) {
+ maybe_split_segment(elf, segment, fill);
+ }
+
+ // Ensure Elf sections will be at their final location.
+ elf->normalize();
+ ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
+ if (init_array) {
+ // Adjust the first DT_INIT_ARRAY entry to point at the injected code
+ // by transforming its relocation into a relative one pointing to the
+ // address of the injected code.
+ Rel_Type *rel = &section->rels[init_array_reloc - 1];
+ rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation
+ set_relative_reloc(&section->rels[init_array_reloc - 1], elf, init->getValue());
+ } else if (!dyn->setValueForType(DT_INIT, init)) {
+ fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n");
+ return -1;
+ }
+ // TODO: adjust the value according to the remaining number of relative relocations
+ if (dyn->getValueForType(Rel_Type::d_tag_count))
+ 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, bool force = false, bool fill = false)
+{
+ std::ifstream file(name, std::ios::in|std::ios::binary);
+ Elf elf(file);
+ unsigned int size = elf.getSize();
+ fprintf(stderr, "%s: ", name);
+ if (elf.getType() != ET_DYN) {
+ fprintf(stderr, "Not a shared object. Skipping\n");
+ return;
+ }
+
+ for (ElfSection *section = elf.getSection(1); section != nullptr;
+ section = section->getNext()) {
+ if (section->getName() &&
+ (strncmp(section->getName(), ".elfhack.", 9) == 0)) {
+ fprintf(stderr, "Already elfhacked. Skipping\n");
+ return;
+ }
+ }
+
+ int exit = -1;
+ switch (elf.getMachine()) {
+ case EM_386:
+ exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill);
+ break;
+ case EM_X86_64:
+ exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE, R_X86_64_64, force, fill);
+ break;
+ case EM_ARM:
+ exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32, force, fill);
+ break;
+ }
+ if (exit == 0) {
+ if (!force && (elf.getSize() >= size)) {
+ fprintf(stderr, "No gain. Skipping\n");
+ } else 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());
+ }
+ }
+}
+
+void undo_file(const char *name, bool backup = false)
+{
+ std::ifstream file(name, std::ios::in|std::ios::binary);
+ Elf elf(file);
+ unsigned int size = elf.getSize();
+ fprintf(stderr, "%s: ", name);
+ if (elf.getType() != ET_DYN) {
+ fprintf(stderr, "Not a shared object. Skipping\n");
+ return;
+ }
+
+ ElfSection *data = nullptr, *text = nullptr;
+ for (ElfSection *section = elf.getSection(1); section != nullptr;
+ section = section->getNext()) {
+ if (section->getName() &&
+ (strcmp(section->getName(), elfhack_data) == 0))
+ data = section;
+ if (section->getName() &&
+ (strcmp(section->getName(), elfhack_text) == 0))
+ text = section;
+ }
+
+ if (!data || !text) {
+ fprintf(stderr, "Not elfhacked. Skipping\n");
+ return;
+ }
+ if (data != text->getNext()) {
+ fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n");
+ return;
+ }
+
+ ElfSegment *first = elf.getSegmentByType(PT_LOAD);
+ ElfSegment *second = elf.getSegmentByType(PT_LOAD, first);
+ ElfSegment *filler = nullptr;
+ // If the second PT_LOAD is a filler from elfhack --fill, check the third.
+ if (second->isElfHackFillerSegment()) {
+ filler = second;
+ second = elf.getSegmentByType(PT_LOAD, filler);
+ }
+ if (second->getFlags() != first->getFlags()) {
+ fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n");
+ return;
+ }
+ // Move sections from the second PT_LOAD to the first, and remove the
+ // second PT_LOAD segment.
+ for (std::list<ElfSection *>::iterator section = second->begin();
+ section != second->end(); ++section)
+ first->addSection(*section);
+
+ elf.removeSegment(second);
+ if (filler)
+ elf.removeSegment(filler);
+
+ 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, "Grown by %d bytes\n", elf.getSize() - size);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int arg;
+ bool backup = false;
+ bool force = false;
+ bool revert = false;
+ bool fill = false;
+ char *lastSlash = rindex(argv[0], '/');
+ if (lastSlash != nullptr)
+ rundir = strndup(argv[0], lastSlash - argv[0]);
+ for (arg = 1; arg < argc; arg++) {
+ if (strcmp(argv[arg], "-f") == 0)
+ force = true;
+ else if (strcmp(argv[arg], "-b") == 0)
+ backup = true;
+ else if (strcmp(argv[arg], "-r") == 0)
+ revert = true;
+ else if (strcmp(argv[arg], "--fill") == 0)
+ fill = true;
+ else if (revert) {
+ undo_file(argv[arg], backup);
+ } else
+ do_file(argv[arg], backup, force, fill);
+ }
+
+ free(rundir);
+ return 0;
+}