diff --git a/binaryfile.cpp b/binaryfile.cpp index c7dc9a14e..487014030 100644 --- a/binaryfile.cpp +++ b/binaryfile.cpp @@ -33,7 +33,9 @@ using namespace llvm; using std::make_pair; -BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { +BinaryFile::BinaryFile(std::string FilePath, + bool UseSections, + uint64_t BaseAddress) : BaseAddress(0) { auto BinaryOrErr = object::createBinary(FilePath); assert(BinaryOrErr && "Couldn't open the input file"); @@ -53,6 +55,9 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { llvm::StringRef WriteRegisterAsm = ""; llvm::StringRef ReadRegisterAsm = ""; llvm::StringRef JumpAsm = ""; + bool HasRelocationAddend = false; + uint32_t BaseRelativeRelocation = ~uint32_t(0); + switch (TheBinary->getArch()) { case Triple::x86: InstructionAlignment = 1; @@ -64,7 +69,10 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { 0x01, // exit 0x0b // execve }; + HasRelocationAddend = false; + BaseRelativeRelocation = llvm::ELF::R_386_RELATIVE; break; + case Triple::x86_64: InstructionAlignment = 1; SyscallHelper = "helper_syscall"; @@ -110,6 +118,7 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { // REGISTER_OFFSET(RSP); // REGISTER_OFFSET(RIP); + // TODO: here we're hardcoding the offsets in the QEMU struct ABIRegisters = { { "rax", 0xD }, { "rbx", 0xB }, { "rcx", 0xE }, { "rdx", 0xC }, { "rbp", 0xA }, { "rsp", 0xF }, { "rsi", 0x9 }, { "rdi", 0x8 }, { "r8", 0x0 }, @@ -123,7 +132,10 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { WriteRegisterAsm = "movq $0, %REGISTER"; ReadRegisterAsm = "movq %REGISTER, $0"; JumpAsm = "movq $0, %r11; jmpq *%r11"; + HasRelocationAddend = true; + BaseRelativeRelocation = llvm::ELF::R_X86_64_RELATIVE; break; + case Triple::arm: InstructionAlignment = 4; SyscallHelper = "helper_exception_with_syndrome"; @@ -137,7 +149,11 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { ABIRegisters = { { "r0" }, { "r1" }, { "r2" }, { "r3" }, { "r4" }, { "r5" }, { "r6" }, { "r7" }, { "r8" }, { "r9" }, { "r10" }, { "r11" }, { "r12" }, { "r13" }, { "r14" } }; + HasRelocationAddend = false; + BaseRelativeRelocation = llvm::ELF::R_ARM_RELATIVE; + break; + case Triple::mips: InstructionAlignment = 4; SyscallHelper = "helper_raise_exception"; @@ -153,7 +169,12 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { { "s0" }, { "s1" }, { "s2", }, { "s3" }, { "s4" }, { "s5" }, { "s6" }, { "s7" }, { "gp" }, { "sp" }, { "fp" }, { "ra" } }; + HasRelocationAddend = false; + // TODO: check if this is correct + // BaseRelativeRelocation = llvm::ELF::R_MIPS_REL32; + break; + case Triple::systemz: SyscallHelper = "helper_exception"; SyscallNumberRegister = "r1"; @@ -164,7 +185,10 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { 0x1, // exit 0xb, // execve }; + HasRelocationAddend = true; + BaseRelativeRelocation = llvm::ELF::R_390_RELATIVE; break; + default: assert(false); } @@ -183,35 +207,144 @@ BinaryFile::BinaryFile(std::string FilePath, bool UseSections) { PCMContextIndex, WriteRegisterAsm, ReadRegisterAsm, - JumpAsm); + JumpAsm, + HasRelocationAddend, + BaseRelativeRelocation); assert(TheBinary->getFileFormatName().startswith("ELF") && "Only the ELF file format is currently supported"); if (TheArchitecture.pointerSize() == 32) { if (TheArchitecture.isLittleEndian()) { - parseELF(TheBinary, UseSections); + if (TheArchitecture.hasRelocationAddend()) { + parseELF(TheBinary, UseSections, BaseAddress); + } else { + parseELF(TheBinary, UseSections, BaseAddress); + } } else { - parseELF(TheBinary, UseSections); + if (TheArchitecture.hasRelocationAddend()) { + parseELF(TheBinary, UseSections, BaseAddress); + } else { + parseELF(TheBinary, UseSections, BaseAddress); + } } } else if (TheArchitecture.pointerSize() == 64) { if (TheArchitecture.isLittleEndian()) { - parseELF(TheBinary, UseSections); + if (TheArchitecture.hasRelocationAddend()) { + parseELF(TheBinary, UseSections, BaseAddress); + } else { + parseELF(TheBinary, UseSections, BaseAddress); + } } else { - parseELF(TheBinary, UseSections); + if (TheArchitecture.hasRelocationAddend()) { + parseELF(TheBinary, UseSections, BaseAddress); + } else { + parseELF(TheBinary, UseSections, BaseAddress); + } } } else { assert("Unexpect address size"); } } +class FilePortion { +public: + void setAddress(uint64_t Address) { + HasAddress = true; + this->Address = Address; + } + + void setSize(uint64_t Size) { + HasSize = true; + this->Size = Size; + } + + bool isAvailable() const { + return HasAddress; + } + + bool isExact() const { + assert(HasAddress); + return HasSize; + } + + StringRef extractString(const std::vector &Segments) const { + ArrayRef Data = extractData(Segments); + const char *AsChar = reinterpret_cast(Data.data()); + return StringRef(AsChar, Data.size()); + } + + template + ArrayRef extractAs(const std::vector &Segments) const { + ArrayRef Data = extractData(Segments); + const size_t TypeSize = sizeof(T); + assert(Data.size() % TypeSize == 0); + return ArrayRef(reinterpret_cast(Data.data()), + Data.size() / TypeSize); + } + + ArrayRef + extractData(const std::vector &Segments) const { + assert(HasAddress); + + for (const SegmentInfo &Segment : Segments) { + if (Segment.contains(Address)) { + uint64_t Offset = Address - Segment.StartVirtualAddress; + uint64_t AvailableSize = Segment.size() - Offset; + uint64_t TheSize = AvailableSize; + + if (HasSize) { + assert(AvailableSize >= Size); + TheSize = Size; + } + + return { ArrayRef(Segment.Data.data() + Offset, TheSize) }; + } + } + + abort(); + } + +private: + bool HasAddress; + bool HasSize; + uint64_t Size; + uint64_t Address; + +}; + +template +struct RelocationHelper { + static uint64_t getAddend(llvm::object::Elf_Rel_Impl); +}; + template -void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { +struct RelocationHelper { + static uint64_t getAddend(llvm::object::Elf_Rel_Impl Relocation) { + return Relocation.r_addend; + } +}; + +template +struct RelocationHelper { + static uint64_t getAddend(llvm::object::Elf_Rel_Impl Relocation) { + return 0; + } +}; + +template +void BinaryFile::parseELF(object::ObjectFile *TheBinary, + bool UseSections, + uint64_t BaseAddress) { // Parse the ELF file std::error_code EC; object::ELFFile TheELF(TheBinary->getData(), EC); assert(!EC && "Error while loading the ELF file"); + // BaseAddress makes sense only for shared (relocatable, PIC) objects + if (TheELF.getHeader()->e_type == ELF::ET_DYN) + this->BaseAddress = BaseAddress; + // Look for static or dynamic symbols and relocations using Elf_ShdrPtr = decltype(&(*TheELF.sections().begin())); using Elf_PhdrPtr = decltype(&(*TheELF.program_headers().begin())); @@ -229,11 +362,11 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { SymtabShdr = &Section; } else if (*Name == ".eh_frame") { assert(not EHFrameAddress && "Duplicate .eh_frame"); - EHFrameAddress = static_cast(Section.sh_addr); + EHFrameAddress = relocate(static_cast(Section.sh_addr)); EHFrameSize = static_cast(Section.sh_size); } else if (*Name == ".dynamic") { assert(not DynamicAddress && "Duplicate .dynamic"); - DynamicAddress = static_cast(Section.sh_addr); + DynamicAddress = relocate(static_cast(Section.sh_addr)); } } } @@ -257,7 +390,7 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { } const auto *ElfHeader = TheELF.getHeader(); - EntryPoint = static_cast(ElfHeader->e_entry); + EntryPoint = relocate(static_cast(ElfHeader->e_entry)); ProgramHeaders.Count = ElfHeader->e_phnum; ProgramHeaders.Size = ElfHeader->e_phentsize; @@ -272,7 +405,7 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { case ELF::PT_LOAD: { SegmentInfo Segment; - auto Start = ProgramHeader.p_vaddr; + auto Start = relocate(ProgramHeader.p_vaddr); Segment.StartVirtualAddress = Start; Segment.EndVirtualAddress = Start + ProgramHeader.p_memsz; Segment.IsReadable = ProgramHeader.p_flags & ELF::PF_R; @@ -289,7 +422,7 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { auto Inserter = std::back_inserter(Segment.ExecutableSections); for (Elf_Shdr &SectionHeader : TheELF.sections()) { if (SectionHeader.sh_flags & ELF::SHF_EXECINSTR) { - auto SectionStart = SectionHeader.sh_addr; + auto SectionStart = relocate(SectionHeader.sh_addr); auto SectionEnd = SectionStart + SectionHeader.sh_size; Inserter = make_pair(SectionStart, SectionEnd); } @@ -303,9 +436,9 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { auto ProgramHeaderEnd = ProgramHeader.p_offset + ProgramHeader.p_filesz; if (ProgramHeaderStart <= ElfHeader->e_phoff && ElfHeader->e_phoff < ProgramHeaderEnd) { - auto PhdrAddress = static_cast(ProgramHeader.p_vaddr - + ElfHeader->e_phoff - - ProgramHeader.p_offset); + uint64_t PhdrAddress = (relocate(ProgramHeader.p_vaddr) + + ElfHeader->e_phoff + - ProgramHeader.p_offset); ProgramHeaders.Address = PhdrAddress; } } @@ -313,14 +446,14 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { case ELF::PT_GNU_EH_FRAME: assert(!EHFrameHdrAddress); - EHFrameHdrAddress = ProgramHeader.p_vaddr; + EHFrameHdrAddress = relocate(ProgramHeader.p_vaddr); break; case ELF::PT_DYNAMIC: assert(DynamicPhdr == nullptr && "Duplicate .dynamic program header"); DynamicPhdr = &ProgramHeader; assert(((not DynamicAddress) - or (DynamicPhdr->p_vaddr == *DynamicAddress)) + or (relocate(DynamicPhdr->p_vaddr) == *DynamicAddress)) and ".dynamic and PT_DYNAMIC have different addresses"); break; } @@ -344,42 +477,119 @@ void BinaryFile::parseELF(object::ObjectFile *TheBinary, bool UseSections) { if (EHFrameAddress) parseEHFrame(*EHFrameAddress, FDEsCount, EHFrameSize); - // Search for needed shared libraries in the .dynamic table + // Parse the .dynamic table if (DynamicPhdr != nullptr) { SmallVector NeededLibraryNameOffsets; - StringRef Dynstr; - Optional DynstrSize; + + FilePortion DynstrPortion; + FilePortion DynsymPortion; + FilePortion ReldynPortion; + FilePortion RelpltPortion; + for (Elf_Dyn &DynamicTag : *TheELF.dynamic_table(DynamicPhdr)) { - switch(DynamicTag.getTag()) { - case ELF::DT_NEEDED: - NeededLibraryNameOffsets.push_back(DynamicTag.getVal()); - break; - case ELF::DT_STRTAB: { - Optional> DynstrData; - DynstrData = getAddressData(DynamicTag.getPtr()); - assert(DynstrData.hasValue() && - ".dynamic string table not available in any segment"); - Dynstr = StringRef(reinterpret_cast(DynstrData->data()), - DynstrData->size()); - } break; + auto TheTag = DynamicTag.getTag(); + switch(TheTag) { + case ELF::DT_NEEDED: + NeededLibraryNameOffsets.push_back(DynamicTag.getVal()); + break; - case ELF::DT_STRSZ: - DynstrSize = DynamicTag.getVal(); - break; + case ELF::DT_STRTAB: + DynstrPortion.setAddress(relocate(DynamicTag.getPtr())); + break; + + case ELF::DT_STRSZ: + DynstrPortion.setSize(DynamicTag.getVal()); + break; + + case ELF::DT_SYMTAB: + DynsymPortion.setAddress(relocate(DynamicTag.getPtr())); + break; + + case ELF::DT_JMPREL: + RelpltPortion.setAddress(relocate(DynamicTag.getPtr())); + break; + + case ELF::DT_PLTRELSZ: + RelpltPortion.setSize(DynamicTag.getVal()); + break; + + case ELF::DT_REL: + case ELF::DT_RELA: + assert(TheTag == HasAddend ? ELF::DT_RELA : ELF::DT_REL); + ReldynPortion.setAddress(relocate(DynamicTag.getPtr())); + break; + + case ELF::DT_RELSZ: + case ELF::DT_RELASZ: + assert(TheTag == HasAddend ? ELF::DT_RELASZ : ELF::DT_RELSZ); + ReldynPortion.setSize(DynamicTag.getVal()); + break; } } - assert(DynstrSize.hasValue() && *DynstrSize < Dynstr.size()); - Dynstr = StringRef(Dynstr.data(), *DynstrSize); + if (NeededLibraryNames.size() > 0) + assert(DynstrPortion.isAvailable()); - for(auto Offset : NeededLibraryNameOffsets) - NeededLibraryNames.push_back(Dynstr.slice(Offset, *DynstrSize).data()); + if (DynstrPortion.isAvailable()) { + StringRef Dynstr = DynstrPortion.extractString(Segments); + for(auto Offset : NeededLibraryNameOffsets) + NeededLibraryNames.push_back(Dynstr.slice(Offset, + Dynstr.size()).data()); + } + + // Collect symbols count and code pointers in image base-relative + // relocations + uint32_t ReldynSymbolsCount = parseRelocations(ReldynPortion); + uint32_t RelpltSymbolsCount = parseRelocations(RelpltPortion); + uint32_t SymbolsCount = std::max(ReldynSymbolsCount, RelpltSymbolsCount); + + // Collect function addresses contained in dynamic symbols + if (SymbolsCount > 0 and DynsymPortion.isAvailable()) { + using Elf_Sym = llvm::object::Elf_Sym_Impl; + DynsymPortion.setSize(SymbolsCount * sizeof(Elf_Sym)); + for (Elf_Sym Symbol : DynsymPortion.extractAs(Segments)) + if (Symbol.st_value != 0 and Symbol.getType() == ELF::STT_FUNC) + CodePointers.insert(relocate(Symbol.st_value)); + } } } +template +uint64_t BinaryFile::parseRelocations(const FilePortion &Relocations) { + using Elf_Rel = llvm::object::Elf_Rel_Impl; + + uint32_t SymbolsCount = 0; + if (Relocations.isAvailable()) { + assert(Relocations.isExact()); + for (Elf_Rel Relocation : Relocations.extractAs(Segments)) { + SymbolsCount = std::max(SymbolsCount, Relocation.getSymbol(false) + 1); + auto RelocationType = Relocation.getType(false); + if (RelocationType == TheArchitecture.baseRelativeRelocation()) { + uint64_t Value; + + // If it's a relocation with an addend, use it, otherwise the + // value is taken from the value stored in the relocated + // address (r_offset). + if (HasAddend) { + Value = RelocationHelper::getAddend(Relocation); + } else { + auto Data = getAddressData(relocate(Relocation.r_offset)); + assert(Data && "r_offset is not in any segment."); + Value = ::readPointer(Data->data()); + } + + CodePointers.insert(relocate(Value)); + } + } + } + + return SymbolsCount; +} + + // // .eh_frame-related functions // diff --git a/binaryfile.h b/binaryfile.h index 6c574b2f5..18405ed41 100644 --- a/binaryfile.h +++ b/binaryfile.h @@ -172,6 +172,8 @@ private: }; +class FilePortion; + /// \brief BinaryFile describes an input image file in a semi-architecture /// independent way class BinaryFile { @@ -180,7 +182,9 @@ public: /// \param UseSections whether information in sections, if available, should /// be employed or not. This is useful to precisely identify exeutable /// code. - BinaryFile(std::string FilePath, bool UseSections); + BinaryFile(std::string FilePath, + bool UseSections, + uint64_t BaseAddress); llvm::Optional> getAddressData(uint64_t Address) const { @@ -204,6 +208,7 @@ public: const std::vector &segments() const { return Segments; } const std::vector &symbols() const { return Symbols; } const std::set &landingPads() const { return LandingPads; } + const std::set &codePointers() const { return CodePointers; } uint64_t entryPoint() const { return EntryPoint; } const std::vector &neededLibraryNames() const { @@ -238,8 +243,10 @@ private: // /// \brief Parse an ELF file to load all the required information - template - void parseELF(llvm::object::ObjectFile *TheBinary, bool UseSections); + template + void parseELF(llvm::object::ObjectFile *TheBinary, + bool UseSections, + uint64_t BaseAddress); /// \brief Parse the .eh_frame_hdr section to obtain the address and the /// number of FDEs in .eh_frame @@ -271,6 +278,17 @@ private: template void parseLSDA(uint64_t FDEStart, uint64_t LSDAAddress); + uint64_t relocate(uint64_t Address) const { + return BaseAddress + Address; + } + + /// \brief Collect image base-relative relocation addresses and count symbols + /// + /// \return the index of the highest symbol referenced in the relocations, + /// plus 1 + template + uint64_t parseRelocations(const FilePortion &Relocations); + private: llvm::object::OwningBinary BinaryHandle; Architecture TheArchitecture; @@ -279,8 +297,11 @@ private: std::vector NeededLibraryNames; std::set LandingPads; ///< the set of the landing pad addresses /// collected from .eh_frame + std::set CodePointers; ///< These are taken from dynamic + /// symbols/relocations. uint64_t EntryPoint; ///< the program's entry point + uint64_t BaseAddress; // // ELF specific fields diff --git a/jumptargetmanager.cpp b/jumptargetmanager.cpp index c89fd3e9b..53f03cd25 100644 --- a/jumptargetmanager.cpp +++ b/jumptargetmanager.cpp @@ -522,6 +522,9 @@ void JumpTargetManager::harvestGlobalData() { for (uint64_t LandingPad : Binary.landingPads()) registerJT(LandingPad, GlobalData); + for (uint64_t CodePointer : Binary.codePointers()) + registerJT(CodePointer, GlobalData); + for (auto& Segment : Binary.segments()) { const Constant *Initializer = Segment.Variable->getInitializer(); if (isa(Initializer)) diff --git a/main.cpp b/main.cpp index af480a3f2..bffd1dd5e 100644 --- a/main.cpp +++ b/main.cpp @@ -57,6 +57,7 @@ struct ProgramParameters { int NoLink; int External; bool PrintStats; + uint64_t BaseAddress; }; // When LibraryPointer is destroyed, the destructor calls @@ -75,6 +76,19 @@ static const char *const Usage[] = { nullptr, }; +static bool toNumber(const char *String, uint64_t *Destination) { + const char *End = String + strlen(String); + char *NumberEnd; + unsigned long long Result; + Result = strtoull(String, &NumberEnd, 0); + + if (End != NumberEnd) + return false; + + *Destination = static_cast(Result); + return true; +} + static void findFiles(const char *Architecture) { // TODO: make this optional char *FullPath = realpath("/proc/self/exe", nullptr); @@ -177,7 +191,9 @@ static int parseArgs(int Argc, const char *Argv[], const char *DebugString = nullptr; const char *DebugLoggingString = nullptr; const char *EntryPointAddressString = nullptr; - long long EntryPointAddress = 0; + uint64_t EntryPointAddress = 0; + const char *BaseAddressString = nullptr; + uint64_t BaseAddress = 0x50000000; // Initialize argument parser struct argparse Arguments; @@ -224,6 +240,9 @@ static int parseArgs(int Argc, const char *Argv[], OPT_BOOLEAN('T', "stats", &Parameters->PrintStats, "print statistics upon exit or SIGINT."), + OPT_STRING('B', "base", + &BaseAddressString, + "base address where dynamic objects should be loaded."), OPT_END(), }; @@ -244,14 +263,21 @@ static int parseArgs(int Argc, const char *Argv[], // Check parameters if (EntryPointAddressString != nullptr) { - if (sscanf(EntryPointAddressString, "%lld", &EntryPointAddress) != 1) { + if (not toNumber(EntryPointAddressString, &EntryPointAddress)) { fprintf(stderr, "Entry point parameter (-e, --entry) is not a" " number.\n"); return EXIT_FAILURE; } - - Parameters->EntryPointAddress = static_cast(EntryPointAddress); } + Parameters->EntryPointAddress = static_cast(EntryPointAddress); + + if (BaseAddressString != nullptr) { + if (not toNumber(BaseAddressString, &BaseAddress)) { + fprintf(stderr, "Base address (-B, --base) is not a number.\n"); + return EXIT_FAILURE; + } + } + Parameters->BaseAddress = BaseAddress; if (DebugString != nullptr) { if (strcmp("none", DebugString) == 0) { @@ -302,7 +328,9 @@ int main(int argc, const char *argv[]) { if (parseArgs(argc, argv, &Parameters) != EXIT_SUCCESS) return EXIT_FAILURE; - BinaryFile TheBinary(Parameters.InputPath, Parameters.UseSections); + BinaryFile TheBinary(Parameters.InputPath, + Parameters.UseSections, + Parameters.BaseAddress); findFiles(TheBinary.architecture().name()); diff --git a/merge-dynamic.py b/merge-dynamic.py index 64bf90008..66a4e8bc4 100755 --- a/merge-dynamic.py +++ b/merge-dynamic.py @@ -17,6 +17,15 @@ from pprint import pprint from elftools.elf.elffile import ELFFile from elftools.elf.constants import P_FLAGS from elftools.elf.enums import ENUM_P_TYPE +from elftools.elf.enums import ENUM_RELOC_TYPE_MIPS +from elftools.elf.enums import ENUM_RELOC_TYPE_i386 +from elftools.elf.enums import ENUM_RELOC_TYPE_x64 +from elftools.elf.enums import ENUM_RELOC_TYPE_ARM + +def log(message): + global verbose + if verbose: + sys.stderr.write(message + "\n") def set_executable(path): st = os.stat(path) @@ -37,6 +46,10 @@ def only(iterable): assert len(iterable) == 1 return iterable[0] +def first_or_none(iterable): + iterable = list(iterable) + return iterable[0] if len(iterable) != 0 else None + def read_at_offset(file, start, size): file.seek(start) return file.read(size) @@ -53,6 +66,9 @@ def parse(buffer, struct): def serialize(list, struct): return b"".join(map(struct.build, list)) +def overlaps(start1, size1, start2, size2): + return (start1 + size1) >= start2 and (start2 + size2) >= start1 + class ParsedElf: def __init__(self, file): self.file = file @@ -206,6 +222,24 @@ class ParsedElf: def has_tag(self, tag): return len(self.dt_by_tag(tag)) != 0 + def segment_by_range(self, address, size): + return first_or_none([segment + for segment + in self.segment_by_type("PT_LOAD") + if overlaps(address, + size, + segment.header.p_vaddr, + segment.header.p_memsz)]) + + def dynamic_size(self): + return (len(self.dynsym) + + len(self.dynstr) + + len(self.gnuversion) + + len(self.reldyn) + + self.dynamic.header.p_memsz + + len(self.segments) * self.elf.structs.Elf_Phdr.sizeof() + + len(self.sections) * self.elf.structs.Elf_Shdr.sizeof()) + def align(start, alignment): return ((start + alignment - 1) / alignment) * alignment @@ -215,17 +249,28 @@ def main(): + "one from the host ELF.")) parser.add_argument("to_extend", metavar="TO_EXTEND", - help="The destination ELF.") + help="The ELF extend.") parser.add_argument("source", metavar="SOURCE", - help="The source ELF.") + help="The original ELF.") parser.add_argument("output", metavar="OUTPUT", nargs="?", default="-", help="The output ELF.") + parser.add_argument("--verbose", + action="store_true", + help="Print debug information and warnings.") + parser.add_argument("--base", + metavar="ADDRESS", + default="0x50000000", + help=("The base address where dynamic object have been" + + " loaded.")) args = parser.parse_args() + global verbose + verbose = args.verbose + with (sys.stdout if args.output == "-" else open(args.output, "wb")) as output_file, \ @@ -243,6 +288,26 @@ def main(): return 0 assert to_extend_elf.is_dynamic + assert to_extend_elf.elf.header.e_machine == source_elf.elf.header.e_machine + + relocation_offset = 0 + if source_elf.elf.header.e_type == "ET_DYN": + relocation_offset = int(args.base, base=0) + + machine = to_extend_elf.elf.header.e_machine + if machine == "EM_X86_64": + relative_relocation = ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"] + elif machine == "EM_ARM": + relative_relocation = ENUM_RELOC_TYPE_ARM["R_ARM_RELATIVE"] + elif machine == "EM_386": + relative_relocation = ENUM_RELOC_TYPE_i386["R_386_RELATIVE"] + elif machine == "EM_MIPS": + # TODO: check + relative_relocation = 0xffffffff + elif machine == "EM_S390": + relative_relocation = 12 # R_390_RELATIVE + else: + assert False # Prepare new .dynstr new_dynstr = to_extend_elf.dynstr @@ -251,8 +316,32 @@ def main(): new_dynstr += source_elf.dynstr to_extend_size = file_size(to_extend_file) + base_address = min([segment.header.p_vaddr + for segment + in to_extend_elf.segment_by_type("PT_LOAD")]) alignment = 0x1000 - padding = align(to_extend_size, alignment) - to_extend_size + estimated_size = align(to_extend_elf.dynamic_size() + + source_elf.dynamic_size(), alignment) + start_address = align(base_address + to_extend_size, alignment) + matching_segment = (to_extend_elf.segment_by_range(start_address, + estimated_size) + or source_elf.segment_by_range(start_address, + estimated_size)) + + while matching_segment is not None: + log("Discarding {} since overlaps the following segment:\n" + + " {}".format(hex(start_address), + matching_segment.header)) + start_address = align(matching_segment.header.p_vaddr + + matching_segment.header.p_memsz, + alignment) + matching_segment = (to_extend_elf.segment_by_range(start_address, + estimated_size) + or source_elf.segment_by_range(start_address, + estimated_size)) + + padding = start_address - base_address - to_extend_size + log("Padding size: {}".format(padding)) new_dynstr_offset = to_extend_size + padding # Prepare new .dynsym @@ -261,16 +350,22 @@ def main(): new_symbols = list(source_elf.symbols) for symbol in new_symbols: symbol.st_name += dynstr_offset + if symbol.st_value != 0: + symbol.st_value += relocation_offset + new_dynsym += serialize(new_symbols, source_elf.elf.structs.Elf_Sym) - new_dynsym_offset = (new_dynstr_offset - + len(new_dynstr)) + new_dynsym_offset = new_dynstr_offset + len(new_dynstr) # Prepare new .dynrel new_reldyn = to_extend_elf.reldyn new_relocations = (source_elf.relplt_relocations + source_elf.reldyn_relocations) for relocation in new_relocations: - relocation.r_info_sym += dynsym_offset + if relocation.r_info_sym != 0: + relocation.r_info_sym += dynsym_offset + if relocation.r_info_type == relative_relocation: + relocation.r_addend += relocation_offset + relocation.r_offset += relocation_offset rebuild_r_info(relocation, to_extend_elf.elf.elfclass == 64) new_reldyn += serialize(new_relocations, source_elf.relstruct) new_reldyn_offset = (new_dynsym_offset + len(new_dynsym)) @@ -294,7 +389,7 @@ def main(): new_gnuversion += source_elf.serialize_ints(new_gnuversion_indices, 2) # 4. Go through .gnu.version_r and, for each verneed add the string - # table offset to the library name. + # table offset to the library name. # 5. Go through each Vernaux and increment vna_name new_verneeds = to_extend_elf.verneeds @@ -318,13 +413,6 @@ def main(): new_verneeds += source_elf.verneeds new_gnuversion_r = source_elf.serialize_verneeds(new_verneeds) - # Prepare new section headers - start_address = min([segment.header.p_vaddr - for segment - in to_extend_elf.segment_by_type("PT_LOAD")]) - start_address += new_dynstr_offset - assert start_address == align(start_address, alignment) - # Prepare new .dynamic new_dynamic_tags = [dt for dt in to_extend_elf.dynamic.iter_tags()] @@ -422,6 +510,11 @@ def main(): + new_program_headers_size - new_dynstr_offset) + if new_segment_size > estimated_size: + log("Warning: the new segment for dynamic sections is larger than" + + " expected:\n Expected: {}\n Actual: {}".format(estimated_size, + new_segment_size)) + zeros = "\x00" * segment_header_size new_segment = source_elf.elf.structs.Elf_Phdr.parse(zeros) new_segment.p_type = ENUM_P_TYPE["PT_LOAD"] diff --git a/revamb.h b/revamb.h index d02896235..695fe8ca5 100644 --- a/revamb.h +++ b/revamb.h @@ -15,6 +15,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" +#include "llvm/Support/ELF.h" // Local includes #include "ir-helpers.h" @@ -114,7 +115,9 @@ public: unsigned PCMContextIndex, llvm::StringRef WriteRegisterAsm, llvm::StringRef ReadRegisterAsm, - llvm::StringRef JumpAsm) : + llvm::StringRef JumpAsm, + bool HasRelocationAddend, + uint32_t BaseRelativeRelocation) : Type(static_cast(Type)), InstructionAlignment(InstructionAlignment), DefaultAlignment(DefaultAlignment), @@ -129,7 +132,9 @@ public: PCMContextIndex(PCMContextIndex), WriteRegisterAsm(WriteRegisterAsm), ReadRegisterAsm(ReadRegisterAsm), - JumpAsm(JumpAsm) { } + JumpAsm(JumpAsm), + HasRelocationAddend(HasRelocationAddend), + BaseRelativeRelocation(BaseRelativeRelocation) { } unsigned instructionAlignment() const { return InstructionAlignment; } unsigned defaultAlignment() const { return DefaultAlignment; } @@ -160,6 +165,8 @@ public: && IsSupported == (JumpAsm.size() != 0)); return IsSupported; } + bool hasRelocationAddend() const { return HasRelocationAddend; } + uint32_t baseRelativeRelocation() const { return BaseRelativeRelocation; } private: llvm::Triple::ArchType Type; @@ -179,6 +186,8 @@ private: llvm::StringRef WriteRegisterAsm; llvm::StringRef ReadRegisterAsm; llvm::StringRef JumpAsm; + bool HasRelocationAddend; + uint32_t BaseRelativeRelocation; }; // TODO: move me somewhere more appropriate @@ -193,11 +202,6 @@ static inline T *notNull(T *Pointer) { return Pointer; } -template -static inline bool contains(T Range, typename T::value_type V) { - return std::find(std::begin(Range), std::end(Range), V) != std::end(Range); -} - static const std::array MarkerFunctionNames = { "newpc", "function_call", diff --git a/support.c b/support.c index 83fda75e4..f451de7fc 100644 --- a/support.c +++ b/support.c @@ -20,6 +20,11 @@ #include #include +#ifdef TARGET_x86_64 +#include +#include +#endif + // Local includes #include "commonconstants.h" #include "support.h" @@ -418,6 +423,13 @@ int main(int argc, char *argv[]) { // Implant custom SIGSEGV handler install_sigsegv_handler(); +#ifdef TARGET_x86_64 + unsigned long fs_value; + int result = arch_prctl(ARCH_GET_FS, &fs_value); + assert(result == 0); + set_register(REGISTER_FS, fs_value); +#endif + // Run the translated program SAFE_CAST(stack); root((target_reg) stack); diff --git a/support.h b/support.h index 7ba99f36d..016eb9c49 100644 --- a/support.h +++ b/support.h @@ -26,6 +26,35 @@ typedef uint64_t target_reg; #define SWAP(x) (htole64(x)) #define TARGET_REG_FORMAT PRIx64 +// TODO: these have been recovered by hand +enum { + REGISTER_RAX = 0x8250, + REGISTER_RCX = 0x8258, + REGISTER_RDX = 0x8260, + REGISTER_RBX = 0x8268, + REGISTER_RSP = 0x8270, + REGISTER_RBP = 0x8278, + REGISTER_RSI = 0x8280, + REGISTER_RDI = 0x8288, + REGISTER_R8 = 0x8290, + REGISTER_R9 = 0x8298, + REGISTER_R12 = 0x82b0, + REGISTER_R13 = 0x82b8, + REGISTER_R14 = 0x82c0, + REGISTER_R15 = 0x82c8, + + REGISTER_FS = 0x8370, + + REGISTER_XMM0 = 0x8558, + REGISTER_XMM1 = 0x8598, + REGISTER_XMM2 = 0x85d8, + REGISTER_XMM3 = 0x8618, + REGISTER_XMM4 = 0x8658, + REGISTER_XMM5 = 0x8698, + REGISTER_XMM6 = 0x86d8, + REGISTER_XMM7 = 0x8718 +}; + #elif defined(TARGET_i386) typedef uint32_t target_reg; @@ -65,5 +94,6 @@ extern target_reg e_phnum; extern jmp_buf jmp_buffer; bool is_executable(uint64_t pc); +void set_register(uint32_t register_id, uint64_t value); #endif // _SUPPORT_H diff --git a/translate b/translate index 65880e13b..5b16ffd79 100755 --- a/translate +++ b/translate @@ -12,6 +12,7 @@ SKIP=0 ISOLATE=0 SUPPORT_CONFIG=normal EXTRA_OPTIONS="" +BASE="" set -e set -o pipefail @@ -44,6 +45,11 @@ do ISOLATE="1" shift # past argument ;; + --base) + shift # past argument + BASE="--base $1" + shift + ;; --) shift break @@ -126,7 +132,7 @@ fi if [ "$SKIP" -eq 0 ]; then REVAMB_LOG="$LL.log" CSV="$LL.ll.li.csv" - "$REVAMB" -g ll --debug jtcount,osrjts --use-sections $EXTRA_OPTIONS "$INPUT" "$LL.ll" "$@" |& tee "$REVAMB_LOG" + "$REVAMB" -g ll --debug jtcount,osrjts --use-sections $BASE $EXTRA_OPTIONS "$INPUT" "$LL.ll" "$@" |& tee "$REVAMB_LOG" fi if [ "$ISOLATE" -eq 1 ]; then @@ -166,5 +172,5 @@ OBJ="$LL.o" UNPATCHED_OUTPUT="$OUTPUT.tmp" mv "$OUTPUT" "$UNPATCHED_OUTPUT" -merge-dynamic.py "$UNPATCHED_OUTPUT" "$INPUT" "$OUTPUT" +merge-dynamic.py $BASE "$UNPATCHED_OUTPUT" "$INPUT" "$OUTPUT" rm "$UNPATCHED_OUTPUT" diff --git a/variablemanager.h b/variablemanager.h index 85f2f2d3c..c3353d603 100644 --- a/variablemanager.h +++ b/variablemanager.h @@ -147,12 +147,75 @@ public: /// /// \param ExternalCSVs true if CSVs linkage should not be turned into static. void finalize(bool ExternalCSVs) { + using namespace llvm; + if (!ExternalCSVs) { - for (auto P : CPUStateGlobals) - P.second->setLinkage(llvm::GlobalValue::InternalLinkage); - for (auto P : OtherGlobals) - P.second->setLinkage(llvm::GlobalValue::InternalLinkage); + for (auto &P : CPUStateGlobals) + P.second->setLinkage(GlobalValue::InternalLinkage); + for (auto &P : OtherGlobals) + P.second->setLinkage(GlobalValue::InternalLinkage); } + + LLVMContext &Context = getContext(&TheModule); + IRBuilder<> Builder(Context); + + // Create the setRegister function + auto *SetRegisterTy = FunctionType::get(Builder.getVoidTy(), + { + Builder.getInt32Ty(), + Builder.getInt64Ty() + }, + false); + auto *Temp = TheModule.getOrInsertFunction("set_register", SetRegisterTy); + auto *SetRegister = cast(Temp); + SetRegister->setLinkage(GlobalValue::ExternalLinkage); + + // Collect arguments + auto ArgIt = SetRegister->arg_begin(); + auto ArgEnd = SetRegister->arg_end(); + assert(ArgIt != ArgEnd); + Argument *RegisterID = &*ArgIt; + ArgIt++; + assert(ArgIt != ArgEnd); + Argument *NewValue = &*ArgIt; + ArgIt++; + assert(ArgIt == ArgEnd); + + // Create main basic blocks + using BasicBlock = BasicBlock; + auto *EntryBB = BasicBlock::Create(Context, "", SetRegister); + auto *DefaultBB = BasicBlock::Create(Context, "", SetRegister); + auto *ReturnBB = BasicBlock::Create(Context, "", SetRegister); + + // Populate the default case of the switch + Builder.SetInsertPoint(DefaultBB); + Builder.CreateCall(TheModule.getFunction("abort")); + Builder.CreateUnreachable(); + + // Create the switch statement + Builder.SetInsertPoint(EntryBB); + auto *Switch = Builder.CreateSwitch(RegisterID, + DefaultBB, + CPUStateGlobals.size()); + for (auto &P : CPUStateGlobals) { + Type *CSVTy = P.second->getType(); + auto *CSVIntTy = cast(CSVTy->getPointerElementType()); + if (CSVIntTy->getBitWidth() <= 64) { + // Set the value of the CSV + auto *SetRegisterBB = BasicBlock::Create(Context, "", SetRegister); + Builder.SetInsertPoint(SetRegisterBB); + Builder.CreateStore(Builder.CreateTrunc(NewValue, CSVIntTy), + P.second); + Builder.CreateBr(ReturnBB); + + // Add the case to the switch + Switch->addCase(Builder.getInt32(P.first), SetRegisterBB); + } + } + + // Finally, populate the return basic block + Builder.SetInsertPoint(ReturnBB); + Builder.CreateRetVoid(); } /// \brief Gets the CPUStateType @@ -177,7 +240,7 @@ private: std::string Name=""); private: - llvm::Module& TheModule; + llvm::Module &TheModule; llvm::IRBuilder<> Builder; using TemporariesMap = std::map; using GlobalsMap = std::map;