diff -r 000000000000 -r 6474c204b198 build/unix/elfhack/elfhack.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build/unix/elfhack/elfhack.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -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 +#include +#include +#include +#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 +struct wrapped { + T value; +}; + +class Elf_Addr_Traits { +public: + typedef wrapped Type32; + typedef wrapped Type64; + + template + static inline void swap(T &t, R &r) { + r.value = endian::swap(t.value); + } +}; + +typedef serializable Elf_Addr; + +class Elf_RelHack_Traits { +public: + typedef Elf32_Rel Type32; + typedef Elf32_Rel Type64; + + template + 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; + +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::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 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::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::iterator c = code.begin(); c != code.end(); c++) + (*c)->getShdr().sh_addr += getAddr(); + + // Apply relocations + for (std::vector::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 *)rel, *c); + else + apply_relocations((ElfRel_Section *)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 *)s); + else + scan_relocs_for_code((ElfRel_Section *)s); + } + } + } + + template + void scan_relocs_for_code(ElfRel_Section *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