/* 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