michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #undef NDEBUG michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "elfxx.h" michael@0: michael@0: #define ver "0" michael@0: #define elfhack_data ".elfhack.data.v" ver michael@0: #define elfhack_text ".elfhack.text.v" ver michael@0: michael@0: #ifndef R_ARM_V4BX michael@0: #define R_ARM_V4BX 0x28 michael@0: #endif michael@0: #ifndef R_ARM_CALL michael@0: #define R_ARM_CALL 0x1c michael@0: #endif michael@0: #ifndef R_ARM_JUMP24 michael@0: #define R_ARM_JUMP24 0x1d michael@0: #endif michael@0: #ifndef R_ARM_THM_JUMP24 michael@0: #define R_ARM_THM_JUMP24 0x1e michael@0: #endif michael@0: michael@0: char *rundir = nullptr; michael@0: michael@0: template michael@0: struct wrapped { michael@0: T value; michael@0: }; michael@0: michael@0: class Elf_Addr_Traits { michael@0: public: michael@0: typedef wrapped Type32; michael@0: typedef wrapped Type64; michael@0: michael@0: template michael@0: static inline void swap(T &t, R &r) { michael@0: r.value = endian::swap(t.value); michael@0: } michael@0: }; michael@0: michael@0: typedef serializable Elf_Addr; michael@0: michael@0: class Elf_RelHack_Traits { michael@0: public: michael@0: typedef Elf32_Rel Type32; michael@0: typedef Elf32_Rel Type64; michael@0: michael@0: template michael@0: static inline void swap(T &t, R &r) { michael@0: r.r_offset = endian::swap(t.r_offset); michael@0: r.r_info = endian::swap(t.r_info); michael@0: } michael@0: }; michael@0: michael@0: typedef serializable Elf_RelHack; michael@0: michael@0: class ElfRelHack_Section: public ElfSection { michael@0: public: michael@0: ElfRelHack_Section(Elf_Shdr &s) michael@0: : ElfSection(s, nullptr, nullptr) michael@0: { michael@0: name = elfhack_data; michael@0: }; michael@0: michael@0: void serialize(std::ofstream &file, char ei_class, char ei_data) michael@0: { michael@0: for (std::vector::iterator i = rels.begin(); michael@0: i != rels.end(); ++i) michael@0: (*i).serialize(file, ei_class, ei_data); michael@0: } michael@0: michael@0: bool isRelocatable() { michael@0: return true; michael@0: } michael@0: michael@0: void push_back(Elf_RelHack &r) { michael@0: rels.push_back(r); michael@0: shdr.sh_size = rels.size() * shdr.sh_entsize; michael@0: } michael@0: private: michael@0: std::vector rels; michael@0: }; michael@0: michael@0: class ElfRelHackCode_Section: public ElfSection { michael@0: public: michael@0: ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init) michael@0: : ElfSection(s, nullptr, nullptr), parent(e), init(init) { michael@0: std::string file(rundir); michael@0: file += "/inject/"; michael@0: switch (parent.getMachine()) { michael@0: case EM_386: michael@0: file += "x86"; michael@0: break; michael@0: case EM_X86_64: michael@0: file += "x86_64"; michael@0: break; michael@0: case EM_ARM: michael@0: file += "arm"; michael@0: break; michael@0: default: michael@0: throw std::runtime_error("unsupported architecture"); michael@0: } michael@0: file += ".o"; michael@0: std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary); michael@0: elf = new Elf(inject); michael@0: if (elf->getType() != ET_REL) michael@0: throw std::runtime_error("object for injected code is not ET_REL"); michael@0: if (elf->getMachine() != parent.getMachine()) michael@0: throw std::runtime_error("architecture of object for injected code doesn't match"); michael@0: michael@0: ElfSymtab_Section *symtab = nullptr; michael@0: michael@0: // Find the symbol table. michael@0: for (ElfSection *section = elf->getSection(1); section != nullptr; michael@0: section = section->getNext()) { michael@0: if (section->getType() == SHT_SYMTAB) michael@0: symtab = (ElfSymtab_Section *) section; michael@0: } michael@0: if (symtab == nullptr) michael@0: throw std::runtime_error("Couldn't find a symbol table for the injected code"); michael@0: michael@0: // Find the init symbol michael@0: entry_point = -1; michael@0: Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit"); michael@0: if (!sym) michael@0: throw std::runtime_error("Couldn't find an 'init' symbol in the injected code"); michael@0: michael@0: entry_point = sym->value.getValue(); michael@0: michael@0: // Get all relevant sections from the injected code object. michael@0: add_code_section(sym->value.getSection()); michael@0: michael@0: // Adjust code sections offsets according to their size michael@0: std::vector::iterator c = code.begin(); michael@0: (*c)->getShdr().sh_addr = 0; michael@0: for(ElfSection *last = *(c++); c != code.end(); c++) { michael@0: unsigned int addr = last->getShdr().sh_addr + last->getSize(); michael@0: if (addr & ((*c)->getAddrAlign() - 1)) michael@0: addr = (addr | ((*c)->getAddrAlign() - 1)) + 1; michael@0: (*c)->getShdr().sh_addr = addr; michael@0: // We need to align this section depending on the greater michael@0: // alignment required by code sections. michael@0: if (shdr.sh_addralign < (*c)->getAddrAlign()) michael@0: shdr.sh_addralign = (*c)->getAddrAlign(); michael@0: } michael@0: shdr.sh_size = code.back()->getAddr() + code.back()->getSize(); michael@0: data = new char[shdr.sh_size]; michael@0: char *buf = data; michael@0: for (c = code.begin(); c != code.end(); c++) { michael@0: memcpy(buf, (*c)->getData(), (*c)->getSize()); michael@0: buf += (*c)->getSize(); michael@0: } michael@0: name = elfhack_text; michael@0: } michael@0: michael@0: ~ElfRelHackCode_Section() { michael@0: delete elf; michael@0: } michael@0: michael@0: void serialize(std::ofstream &file, char ei_class, char ei_data) michael@0: { michael@0: // Readjust code offsets michael@0: for (std::vector::iterator c = code.begin(); c != code.end(); c++) michael@0: (*c)->getShdr().sh_addr += getAddr(); michael@0: michael@0: // Apply relocations michael@0: for (std::vector::iterator c = code.begin(); c != code.end(); c++) { michael@0: for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext()) michael@0: if (((rel->getType() == SHT_REL) || michael@0: (rel->getType() == SHT_RELA)) && michael@0: (rel->getInfo().section == *c)) { michael@0: if (rel->getType() == SHT_REL) michael@0: apply_relocations((ElfRel_Section *)rel, *c); michael@0: else michael@0: apply_relocations((ElfRel_Section *)rel, *c); michael@0: } michael@0: } michael@0: michael@0: ElfSection::serialize(file, ei_class, ei_data); michael@0: } michael@0: michael@0: bool isRelocatable() { michael@0: return true; michael@0: } michael@0: michael@0: unsigned int getEntryPoint() { michael@0: return entry_point; michael@0: } michael@0: private: michael@0: void add_code_section(ElfSection *section) michael@0: { michael@0: if (section) { michael@0: /* Don't add section if it's already been added in the past */ michael@0: for (auto s = code.begin(); s != code.end(); ++s) { michael@0: if (section == *s) michael@0: return; michael@0: } michael@0: code.push_back(section); michael@0: find_code(section); michael@0: } michael@0: } michael@0: michael@0: /* Look at the relocations associated to the given section to find other michael@0: * sections that it requires */ michael@0: void find_code(ElfSection *section) michael@0: { michael@0: for (ElfSection *s = elf->getSection(1); s != nullptr; michael@0: s = s->getNext()) { michael@0: if (((s->getType() == SHT_REL) || michael@0: (s->getType() == SHT_RELA)) && michael@0: (s->getInfo().section == section)) { michael@0: if (s->getType() == SHT_REL) michael@0: scan_relocs_for_code((ElfRel_Section *)s); michael@0: else michael@0: scan_relocs_for_code((ElfRel_Section *)s); michael@0: } michael@0: } michael@0: } michael@0: michael@0: template michael@0: void scan_relocs_for_code(ElfRel_Section *rel) michael@0: { michael@0: ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink(); michael@0: for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) { michael@0: ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection(); michael@0: add_code_section(section); michael@0: } michael@0: } michael@0: michael@0: class pc32_relocation { michael@0: public: michael@0: Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, michael@0: Elf32_Word addend, unsigned int addr) michael@0: { michael@0: return addr + addend - offset - base_addr; michael@0: } michael@0: }; michael@0: michael@0: class arm_plt32_relocation { michael@0: public: michael@0: Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, michael@0: Elf32_Word addend, unsigned int addr) michael@0: { michael@0: // We don't care about sign_extend because the only case where this is michael@0: // going to be used only jumps forward. michael@0: Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2; michael@0: tmp = (addend + tmp) & 0x00ffffff; michael@0: return (addend & 0xff000000) | tmp; michael@0: } michael@0: }; michael@0: michael@0: class arm_thm_jump24_relocation { michael@0: public: michael@0: Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, michael@0: Elf32_Word addend, unsigned int addr) michael@0: { michael@0: /* Follows description of b.w and bl instructions as per michael@0: ARM Architecture Reference Manual ARMĀ® v7-A and ARMĀ® v7-R edition, A8.6.16 michael@0: We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl. michael@0: We don't care about sign_extend because the only case where this is michael@0: going to be used only jumps forward. */ michael@0: Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr); michael@0: unsigned int word0 = addend & 0xffff, michael@0: word1 = addend >> 16; michael@0: michael@0: /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */ michael@0: unsigned int type = (word1 & 0xd000) >> 12; michael@0: if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9)) michael@0: throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W