/// \file BinaryFile.cpp /// \brief // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ELF.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/LEB128.h" #include "revng/Lift/BinaryFile.h" #include "revng/Support/CommandLine.h" #include "revng/Support/Debug.h" // using directives using namespace llvm; using std::make_pair; using LabelList = BinaryFile::LabelList; static Logger<> EhFrameLog("ehframe"); static Logger<> LabelsLog("labels"); const unsigned char R_MIPS_IMPLICIT_RELATIVE = 255; namespace nooverflow { template auto add(T LHS, U RHS) -> Optional { using V = decltype(LHS + RHS); V Result = LHS + RHS; if (Result < LHS) return {}; return Result; } } // namespace nooverflow template static void logAddress(T &Logger, const char *Name, MetaAddress Address) { if (Logger.isEnabled()) { Logger << Name; Address.dump(Logger); Logger << DoLog; } } template bool contains(const ArrayRef &Container, const ArrayRef &Contained) { return (Container.begin() <= Contained.begin() and Container.end() >= Contained.end()); } template static void swapBytes(R &Value) { swapStruct(Value); } template<> void swapBytes(uint32_t &Value) { sys::swapByteOrder(Value); } template class ArrayRefReader { private: ArrayRef Array; const T *Cursor; bool Swap; public: ArrayRefReader(ArrayRef Array, bool Swap) : Array(Array), Cursor(Array.begin()), Swap(Swap) {} bool eof() const { return Cursor == Array.end(); } template R read() { revng_check(Cursor + sizeof(R) > Cursor); revng_check(Cursor + sizeof(R) <= Array.end()); R Result; memcpy(&Result, Cursor, sizeof(R)); if (Swap) swapBytes(Result); Cursor += sizeof(R); return Result; } }; static MetaAddress getInitialPC(Triple::ArchType Arch, bool Swap, ArrayRef Command) { using namespace llvm::MachO; ArrayRefReader Reader(Command, Swap); uint32_t Flavor = Reader.read(); uint32_t Count = Reader.read(); Optional PC; switch (Arch) { case Triple::x86: { switch (Flavor) { case MachO::x86_THREAD_STATE32: revng_check(Count == MachO::x86_THREAD_STATE32_COUNT); PC = Reader.read().eip; break; case MachO::x86_THREAD_STATE: revng_check(Count == MachO::x86_THREAD_STATE_COUNT); PC = Reader.read().uts.ts32.eip; break; default: revng_abort(); } revng_check(Reader.eof()); } break; case Triple::x86_64: { switch (Flavor) { case MachO::x86_THREAD_STATE64: revng_check(Count == MachO::x86_THREAD_STATE64_COUNT); PC = Reader.read().rip; break; case MachO::x86_THREAD_STATE: revng_check(Count == MachO::x86_THREAD_STATE_COUNT); PC = Reader.read().uts.ts64.rip; break; default: revng_abort(); } } break; case Triple::arm: { switch (Flavor) { case MachO::ARM_THREAD_STATE: revng_check(Count == MachO::ARM_THREAD_STATE_COUNT); PC = Reader.read().uts.ts32.pc; break; default: revng_abort(); } } break; case Triple::aarch64: { switch (Flavor) { case MachO::ARM_THREAD_STATE64: revng_check(Count == MachO::ARM_THREAD_STATE64_COUNT); PC = Reader.read().pc; break; default: revng_abort(); } } break; default: revng_abort("Unexpected architecture for Mach-O"); break; } revng_check(Reader.eof()); if (PC) return MetaAddress::fromPC(Arch, *PC); else return MetaAddress::invalid(); } BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) : EntryPoint(MetaAddress::invalid()), BaseAddress(0) { auto BinaryOrErr = object::createBinary(FilePath); revng_check(BinaryOrErr, "Couldn't open the input file"); BinaryHandle = std::move(BinaryOrErr.get()); initialize(PreferedBaseAddress); } BinaryFile::BinaryFile(Handle BinaryHandle, uint64_t PreferedBaseAddress) : BinaryHandle(std::move(BinaryHandle)), EntryPoint(MetaAddress::invalid()), BaseAddress(0) { initialize(PreferedBaseAddress); } void BinaryFile::initialize(uint64_t PreferedBaseAddress) { auto *TheBinary = cast(BinaryHandle.getBinary()); // TODO: QEMU should provide this information uint32_t InstructionAlignment = 0; StringRef SyscallHelper = ""; StringRef SyscallNumberRegister = ""; StringRef StackPointerRegister = ""; StringRef ReturnAddressRegister = ""; int64_t MinimalFinalStackOffset = 0; ArrayRef NoReturnSyscalls = {}; SmallVector ABIRegisters; uint32_t DelaySlotSize = 0; unsigned PCMContextIndex = ABIRegister::NotInMContext; llvm::StringRef WriteRegisterAsm = ""; llvm::StringRef ReadRegisterAsm = ""; llvm::StringRef JumpAsm = ""; bool HasRelocationAddend; llvm::ArrayRef BasicBlockEndingPattern; using RD = RelocationDescription; using namespace llvm::ELF; using namespace model::Register; Architecture::RelocationTypesMap RelocationTypes; model::ABI::Values DefaultABI = model::ABI::Invalid; auto Arch = TheBinary->getArch(); switch (Arch) { case Triple::x86: InstructionAlignment = 1; SyscallHelper = "helper_raise_interrupt"; SyscallNumberRegister = "eax"; StackPointerRegister = "esp"; MinimalFinalStackOffset = 4; NoReturnSyscalls = { 0xfc, // exit_group 0x01, // exit 0x0b // execve }; HasRelocationAddend = false; RelocationTypes[R_386_RELATIVE] = RD(RD::BaseRelative, RD::TargetValue); RelocationTypes[R_386_JUMP_SLOT] = RD(RD::SymbolRelative); RelocationTypes[R_386_GLOB_DAT] = RD(RD::SymbolRelative); RelocationTypes[R_386_32] = RD(RD::SymbolRelative, RD::TargetValue); RelocationTypes[R_386_COPY] = RD(RD::LabelOnly, RD::TargetValue); ABIRegisters = { { eax_x86 }, { ebx_x86 }, { ecx_x86 }, { edx_x86 }, { esi_x86 }, { edi_x86 }, { ebp_x86 }, { esp_x86 } }; BasicBlockEndingPattern = "\xcc"; DefaultABI = model::ABI::getDefault(model::Architecture::x86); break; case Triple::x86_64: InstructionAlignment = 1; SyscallHelper = "helper_syscall"; SyscallNumberRegister = "rax"; StackPointerRegister = "rsp"; MinimalFinalStackOffset = 8; NoReturnSyscalls = { 0xe7, // exit_group 0x3c, // exit 0x3b // execve }; PCMContextIndex = 0x10; // The offsets associated to the registers have been obtained running the // following command: // // scripts/compile-time-constants.py gcc ucontext.c // // where `ucontext.c` is: // // #define _GNU_SOURCE // #include // #include // // static ucontext_t UContext; // // #define REGISTER_OFFSET(reg) const int MContextIndex ## reg = REG_ ## reg // // REGISTER_OFFSET(R8); // REGISTER_OFFSET(R9); // REGISTER_OFFSET(R10); // REGISTER_OFFSET(R11); // REGISTER_OFFSET(R12); // REGISTER_OFFSET(R13); // REGISTER_OFFSET(R14); // REGISTER_OFFSET(R15); // REGISTER_OFFSET(RDI); // REGISTER_OFFSET(RSI); // REGISTER_OFFSET(RBP); // REGISTER_OFFSET(RBX); // REGISTER_OFFSET(RDX); // REGISTER_OFFSET(RAX); // REGISTER_OFFSET(RCX); // REGISTER_OFFSET(RSP); // REGISTER_OFFSET(RIP); // TODO: here we're hardcoding the offsets in the QEMU struct ABIRegisters = { { rax_x86_64, 0xD }, { rbx_x86_64, 0xB }, { rcx_x86_64, 0xE }, { rdx_x86_64, 0xC }, { rbp_x86_64, 0xA }, { rsp_x86_64, 0xF }, { rsi_x86_64, 0x9 }, { rdi_x86_64, 0x8 }, { r8_x86_64, 0x0 }, { r9_x86_64, 0x1 }, { r10_x86_64, 0x2 }, { r11_x86_64, 0x3 }, { r12_x86_64, 0x4 }, { r13_x86_64, 0x5 }, { r14_x86_64, 0x6 }, { r15_x86_64, 0x7 }, { xmm0_x86_64 }, { xmm1_x86_64 }, { xmm2_x86_64 }, { xmm3_x86_64 }, { xmm4_x86_64 }, { xmm5_x86_64 }, { xmm6_x86_64 }, { xmm7_x86_64 } }; WriteRegisterAsm = "movq $0, %REGISTER"; ReadRegisterAsm = "movq %REGISTER, $0"; JumpAsm = "jmpq *$0"; HasRelocationAddend = true; RelocationTypes[R_X86_64_RELATIVE] = RD(RD::BaseRelative, RD::Addend); RelocationTypes[R_X86_64_JUMP_SLOT] = RD(RD::SymbolRelative); RelocationTypes[R_X86_64_GLOB_DAT] = RD(RD::SymbolRelative); RelocationTypes[R_X86_64_COPY] = RD(RD::LabelOnly, RD::TargetValue); // TODO: encode relocation size RelocationTypes[R_X86_64_32] = RD(RD::SymbolRelative, RD::Addend); RelocationTypes[R_X86_64_64] = RD(RD::SymbolRelative, RD::Addend); BasicBlockEndingPattern = "\xcc"; DefaultABI = model::ABI::getDefault(model::Architecture::x86_64); break; case Triple::arm: InstructionAlignment = 4; SyscallHelper = "helper_exception_with_syndrome"; SyscallNumberRegister = "r7"; StackPointerRegister = "r13"; ReturnAddressRegister = "r14"; NoReturnSyscalls = { 0xf8, // exit_group 0x1, // exit 0xb // execve }; ABIRegisters = { { r0_arm }, { r1_arm }, { r2_arm }, { r3_arm }, { r4_arm }, { r5_arm }, { r6_arm }, { r7_arm }, { r8_arm }, { r9_arm }, { r10_arm }, { r11_arm }, { r12_arm }, { r13_arm }, { r14_arm } }; PCMContextIndex = 18; HasRelocationAddend = false; RelocationTypes[R_ARM_RELATIVE] = RD(RD::BaseRelative, RD::TargetValue); RelocationTypes[R_ARM_JUMP_SLOT] = RD(RD::SymbolRelative); RelocationTypes[R_ARM_GLOB_DAT] = RD(RD::SymbolRelative); RelocationTypes[R_ARM_COPY] = RD(RD::LabelOnly, RD::TargetValue); // bx lr BasicBlockEndingPattern = "\x1e\xff\x2f\xe1"; DefaultABI = model::ABI::getDefault(model::Architecture::arm); break; case Triple::aarch64: HasRelocationAddend = false; InstructionAlignment = 4; SyscallHelper = "helper_exception_with_syndrome"; SyscallNumberRegister = "x8"; StackPointerRegister = "sp"; ReturnAddressRegister = "lr"; NoReturnSyscalls = { 0x5e, // exit_group 0x5d, // exit 0xdd // execve }; ABIRegisters = { { x0_aarch64 }, { x1_aarch64 }, { x2_aarch64 }, { x3_aarch64 }, { x4_aarch64 }, { x5_aarch64 }, { x6_aarch64 }, { x7_aarch64 }, { x8_aarch64 }, { x9_aarch64 }, { x10_aarch64 }, { x11_aarch64 }, { x12_aarch64 }, { x13_aarch64 }, { x14_aarch64 }, { x15_aarch64 }, { x16_aarch64 }, { x17_aarch64 }, { x18_aarch64 }, { x19_aarch64 }, { x20_aarch64 }, { x21_aarch64 }, { x22_aarch64 }, { x23_aarch64 }, { x24_aarch64 }, { x25_aarch64 }, { x26_aarch64 }, { x27_aarch64 }, { x28_aarch64 }, { x29_aarch64 }, { lr_aarch64 }, { sp_aarch64 } }; HasRelocationAddend = false; // ret BasicBlockEndingPattern = "\xc0\x03\x5f\xd6"; DefaultABI = model::ABI::getDefault(model::Architecture::aarch64); break; case Triple::mips: case Triple::mipsel: InstructionAlignment = 4; SyscallHelper = "helper_raise_exception"; SyscallNumberRegister = "v0"; StackPointerRegister = "sp"; ReturnAddressRegister = "ra"; NoReturnSyscalls = { 0x1096, // exit_group 0xfa1, // exit 0xfab // execve }; DelaySlotSize = 1; ABIRegisters = { { v0_mips }, { v1_mips }, { a0_mips }, { a1_mips }, { a2_mips }, { a3_mips }, { s0_mips }, { s1_mips }, { s2_mips }, { s3_mips }, { s4_mips }, { s5_mips }, { s6_mips }, { s7_mips }, { gp_mips }, { sp_mips }, { fp_mips }, { ra_mips } }; HasRelocationAddend = false; // R_MIPS_RELATIVE does not exist since the GOT has implicit base-relative // relocations RelocationTypes[R_MIPS_IMPLICIT_RELATIVE] = RD(RD::BaseRelative, RD::TargetValue); RelocationTypes[R_MIPS_JUMP_SLOT] = RD(RD::SymbolRelative); RelocationTypes[R_MIPS_GLOB_DAT] = RD(RD::SymbolRelative); RelocationTypes[R_MIPS_COPY] = RD(RD::LabelOnly, RD::TargetValue); // jr ra BasicBlockEndingPattern = ((Arch == Triple::mips) ? "\x08\x00\xe0\x03" : "\x03\xe0\x00\x08"); DefaultABI = model::ABI::getDefault(model::Architecture::mips); break; case Triple::systemz: SyscallHelper = "helper_exception"; SyscallNumberRegister = "r1"; StackPointerRegister = "r15"; ReturnAddressRegister = "r14"; InstructionAlignment = 2; NoReturnSyscalls = { 0xf8, // exit_group 0x1, // exit 0xb, // execve }; HasRelocationAddend = true; // TODO: investigate (R_390_RELATIVE does not exist) RelocationTypes[R_390_GLOB_DAT] = RD(RD::SymbolRelative); RelocationTypes[R_390_COPY] = RD(RD::LabelOnly, RD::TargetValue); ABIRegisters = { { r0_systemz }, { r1_systemz }, { r2_systemz }, { r3_systemz }, { r4_systemz }, { r5_systemz }, { r6_systemz }, { r7_systemz }, { r8_systemz }, { r9_systemz }, { r10_systemz }, { r11_systemz }, { r12_systemz }, { r13_systemz }, { r14_systemz }, { r15_systemz }, { f0_systemz }, { f1_systemz }, { f2_systemz }, { f3_systemz }, { f4_systemz }, { f5_systemz }, { f6_systemz }, { f7_systemz }, { f8_systemz }, { f9_systemz }, { f10_systemz }, { f11_systemz }, { f12_systemz }, { f13_systemz }, { f14_systemz }, { f15_systemz } }; DefaultABI = model::ABI::getDefault(model::Architecture::systemz); break; default: revng_abort(); } TheArchitecture = Architecture(TheBinary->getArch(), InstructionAlignment, 1, TheBinary->isLittleEndian(), TheBinary->getBytesInAddress() * 8, SyscallHelper, SyscallNumberRegister, NoReturnSyscalls, DelaySlotSize, StackPointerRegister, ReturnAddressRegister, MinimalFinalStackOffset, ABIRegisters, PCMContextIndex, WriteRegisterAsm, ReadRegisterAsm, JumpAsm, HasRelocationAddend, std::move(RelocationTypes), BasicBlockEndingPattern, DefaultABI); if (TheBinary->isELF()) { if (TheArchitecture.pointerSize() == 32) { if (TheArchitecture.isLittleEndian()) { if (TheArchitecture.hasRelocationAddend()) { parseELF(TheBinary, PreferedBaseAddress); } else { parseELF(TheBinary, PreferedBaseAddress); } } else { if (TheArchitecture.hasRelocationAddend()) { parseELF(TheBinary, PreferedBaseAddress); } else { parseELF(TheBinary, PreferedBaseAddress); } } } else if (TheArchitecture.pointerSize() == 64) { if (TheArchitecture.isLittleEndian()) { if (TheArchitecture.hasRelocationAddend()) { parseELF(TheBinary, PreferedBaseAddress); } else { parseELF(TheBinary, PreferedBaseAddress); } } else { if (TheArchitecture.hasRelocationAddend()) { parseELF(TheBinary, PreferedBaseAddress); } else { parseELF(TheBinary, PreferedBaseAddress); } } } else { revng_assert("Unexpect address size"); } } else if (TheBinary->isCOFF()) { revng_assert(TheArchitecture.pointerSize() == 32 || TheArchitecture.pointerSize() == 64, "Only 32/64-bit COFF files are supported"); revng_assert(TheArchitecture.isLittleEndian() == true, "Only Little-Endian COFF files are supported"); // TODO handle relocations parseCOFF(TheBinary, PreferedBaseAddress); } else if (auto *MachO = dyn_cast(TheBinary)) { using namespace llvm::MachO; using namespace llvm::object; using LoadCommandInfo = MachOObjectFile::LoadCommandInfo; Triple::ArchType Arch = TheBinary->getArch(); StringRef StringDataRef = TheBinary->getData(); auto RawDataRef = ArrayRef(StringDataRef.bytes_begin(), StringDataRef.size()); bool MustSwap = TheArchitecture.isLittleEndian() != sys::IsLittleEndianHost; bool EntryPointFound = false; Optional EntryPointOffset; for (const LoadCommandInfo &LCI : MachO->load_commands()) { switch (LCI.C.cmd) { case LC_SEGMENT: parseMachOSegment(RawDataRef, MachO->getSegmentLoadCommand(LCI)); break; case LC_SEGMENT_64: parseMachOSegment(RawDataRef, MachO->getSegment64LoadCommand(LCI)); break; case LC_UNIXTHREAD: { revng_check(not EntryPointFound); EntryPointFound = true; const uint8_t *Pointer = reinterpret_cast(LCI.Ptr); ArrayRef CommandBuffer(Pointer + sizeof(thread_command), LCI.C.cmdsize - sizeof(thread_command)); revng_check(contains(RawDataRef, CommandBuffer)); EntryPoint = getInitialPC(Arch, MustSwap, CommandBuffer); } break; case LC_MAIN: revng_check(not EntryPointFound); EntryPointFound = true; // This is an offset, delay translation to code for later EntryPointOffset = MachO->getEntryPointCommand(LCI).entryoff; break; case LC_FUNCTION_STARTS: case LC_DATA_IN_CODE: case LC_SYMTAB: case LC_DYSYMTAB: // TODO: very interesting break; } } if (EntryPointOffset) EntryPoint = virtualAddressFromOffset(*EntryPointOffset).toPC(Arch); const uint64_t PointerSize = TheArchitecture.pointerSize() / 8; Error TheError = Error::success(); for (const MachOBindEntry &U : MachO->bindTable(TheError)) registerBindEntry(&U, PointerSize); revng_check(not TheError); for (const MachOBindEntry &U : MachO->lazyBindTable(TheError)) registerBindEntry(&U, PointerSize); revng_check(not TheError); // TODO: we should handle weak symbols for (const MachOBindEntry &U : MachO->weakBindTable(TheError)) registerBindEntry(&U, PointerSize); revng_check(not TheError); } else { revng_assert("Unsupported file format."); } rebuildLabelsMap(); } void BinaryFile::registerBindEntry(const object::MachOBindEntry *Entry, uint64_t PointerSize) { using namespace llvm::MachO; using namespace llvm::object; const auto Origin = LabelOrigin::DynamicRelocation; MetaAddress Target = MetaAddress::invalid(); uint64_t Addend = static_cast(Entry->addend()); uint64_t Size = 0; switch (Entry->type()) { case BIND_TYPE_INVALID: case BIND_TYPE_POINTER: Target = fromGeneric(Entry->address()); Size = PointerSize; break; case BIND_TYPE_TEXT_ABSOLUTE32: Target = fromPC(Entry->address()); Size = 32 / 8; break; case BIND_TYPE_TEXT_PCREL32: Target = fromPC(Entry->address()); Size = 32 / 8; Addend -= Target.address(); break; default: revng_abort(); } registerLabel(Label::createSymbolRelativeValue(Origin, Target, Size, Entry->symbolName(), SymbolType::Unknown, Addend)); } class FilePortion { private: bool HasAddress; bool HasSize; uint64_t Size; MetaAddress Address; public: FilePortion() : HasAddress(false), HasSize(false), Size(0), Address(MetaAddress::invalid()) {} public: void setAddress(MetaAddress Address) { HasAddress = true; this->Address = Address; } void setSize(uint64_t Size) { HasSize = true; this->Size = Size; } MetaAddress addressAtOffset(uint64_t Offset) { revng_assert(HasAddress and HasSize); revng_assert(Offset <= Size); return Address + Offset; } template MetaAddress addressAtIndex(uint64_t Index) { revng_assert(HasAddress and HasSize); uint64_t Offset = Index * sizeof(T); revng_assert(Offset <= Size); return Address + Offset; } bool isAvailable() const { return HasAddress; } bool isExact() const { revng_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); revng_assert(Data.size() % TypeSize == 0); return ArrayRef(reinterpret_cast(Data.data()), Data.size() / TypeSize); } ArrayRef extractData(const std::vector &Segments) const { revng_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) { revng_assert(AvailableSize >= Size); TheSize = Size; } return { ArrayRef(Segment.Data.data() + Offset, TheSize) }; } } revng_abort(); } }; template struct RelocationHelper { static uint64_t getAddend(llvm::object::Elf_Rel_Impl); }; template 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) { return 0; } }; static bool shouldIgnoreSymbol(StringRef Name) { return Name == "$a" or Name == "$d"; } static uint64_t u64(uint64_t Value) { return Value; } void BinaryFile::parseCOFF(object::ObjectFile *TheBinary, uint64_t) { using object::COFFObjectFile; auto TheCOFFOrErr = COFFObjectFile::create(TheBinary->getMemoryBufferRef()); if (not TheCOFFOrErr) { logAllUnhandledErrors(TheCOFFOrErr.takeError(), errs(), ""); revng_abort(); } COFFObjectFile &TheCOFF = *TheCOFFOrErr.get(); const object::pe32_header *PE32Header = TheCOFF.getPE32Header(); MetaAddress ImageBase = MetaAddress::invalid(); if (PE32Header) { // TODO: ImageBase should aligned to 4kb pages, should we check that? ImageBase = fromPC(PE32Header->ImageBase); EntryPoint = ImageBase + u64(PE32Header->AddressOfEntryPoint); ProgramHeaders.Count = PE32Header->NumberOfRvaAndSize; ProgramHeaders.Size = PE32Header->SizeOfHeaders; } else { const object::pe32plus_header *PE32PlusHeader = TheCOFF.getPE32PlusHeader(); if (!PE32PlusHeader) { revng_assert("Invalid PE Header.\n"); return; } // PE32+ Header ImageBase = fromPC(PE32PlusHeader->ImageBase); EntryPoint = ImageBase + u64(PE32PlusHeader->AddressOfEntryPoint); ProgramHeaders.Count = PE32PlusHeader->NumberOfRvaAndSize; ProgramHeaders.Size = PE32PlusHeader->SizeOfHeaders; } // Read sections for (const llvm::object::SectionRef &SecRef : TheCOFF.sections()) { unsigned Id = TheCOFF.getSectionID(SecRef); Expected SecOrErr = TheCOFF.getSection(Id); if (not SecOrErr) { logAllUnhandledErrors(SecOrErr.takeError(), errs(), ""); revng_abort(); } const object::coff_section *CoffRef = *SecOrErr; // VirtualSize might be larger than SizeOfRawData (extra data at the end of // the section) or viceversa (data mapped in memory but not present in // memory, e.g., .bss) uint64_t SegmentSize = std::min(CoffRef->VirtualSize, CoffRef->SizeOfRawData); using namespace nooverflow; SegmentInfo Segment; Segment.StartVirtualAddress = ImageBase + u64(CoffRef->VirtualAddress); Segment.EndVirtualAddress = Segment.StartVirtualAddress + u64(CoffRef->VirtualSize); Segment.StartFileOffset = CoffRef->PointerToRawData; Segment.EndFileOffset = Segment.StartFileOffset + SegmentSize; Segment.IsExecutable = CoffRef->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE; Segment.IsReadable = CoffRef->Characteristics & COFF::IMAGE_SCN_MEM_READ; Segment.IsWriteable = CoffRef->Characteristics & COFF::IMAGE_SCN_MEM_WRITE; StringRef StringDataRef = SecRef.getObject()->getData(); auto RawDataRef = ArrayRef(StringDataRef.bytes_begin(), StringDataRef.size()); Segment.Data = ArrayRef(*add(RawDataRef.begin(), CoffRef->PointerToRawData), SegmentSize); revng_assert(contains(RawDataRef, Segment.Data)); Segments.push_back(Segment); } } template void BinaryFile::parseMachOSegment(ArrayRef RawDataRef, const T &SegmentCommand) { using namespace llvm::MachO; using namespace llvm::object; using namespace nooverflow; SegmentInfo Segment; Segment.StartVirtualAddress = fromGeneric(SegmentCommand.vmaddr); Segment.EndVirtualAddress = fromGeneric(SegmentCommand.vmaddr) + SegmentCommand.vmsize; Segment.StartFileOffset = SegmentCommand.fileoff; Segment.EndFileOffset = *add(SegmentCommand.fileoff, SegmentCommand.filesize); Segment.IsExecutable = SegmentCommand.initprot & VM_PROT_EXECUTE; Segment.IsReadable = SegmentCommand.initprot & VM_PROT_READ; Segment.IsWriteable = SegmentCommand.initprot & VM_PROT_WRITE; Segment.Data = ArrayRef(*add(RawDataRef.begin(), SegmentCommand.fileoff), SegmentCommand.filesize); revng_assert(contains(RawDataRef, Segment.Data)); Segments.push_back(Segment); } template void BinaryFile::parseELF(object::ObjectFile *TheBinary, uint64_t PreferedBaseAddress) { // Parse the ELF file auto TheELFOrErr = object::ELFFile::create(TheBinary->getData()); if (not TheELFOrErr) { logAllUnhandledErrors(TheELFOrErr.takeError(), errs(), ""); revng_abort(); } object::ELFFile &TheELF = *TheELFOrErr; // BaseAddress makes sense only for shared (relocatable, PIC) objects auto Type = TheELF.getHeader().e_type; if (Type == ELF::ET_DYN) { BaseAddress = PreferedBaseAddress; } revng_assert(Type == ELF::ET_DYN or Type == ELF::ET_EXEC, "rev.ng currently handles executables and " "dynamic libraries only."); // Look for static or dynamic symbols and relocations using ConstElf_ShdrPtr = const typename object::ELFFile::Elf_Shdr *; using Elf_PhdrPtr = const typename object::ELFFile::Elf_Phdr *; ConstElf_ShdrPtr SymtabShdr = nullptr; Elf_PhdrPtr DynamicPhdr = nullptr; Optional DynamicAddress; Optional EHFrameAddress; Optional EHFrameSize; Optional EHFrameHdrAddress; auto Sections = TheELF.sections(); if (not Sections) { logAllUnhandledErrors(std::move(Sections.takeError()), errs(), ""); } else { for (auto &Section : *Sections) { auto NameOrErr = TheELF.getSectionName(Section); if (NameOrErr) { auto &Name = *NameOrErr; if (Name == ".symtab") { // TODO: check dedicated field in section header revng_assert(SymtabShdr == nullptr, "Duplicate .symtab"); SymtabShdr = &Section; } else if (Name == ".eh_frame") { revng_assert(not EHFrameAddress, "Duplicate .eh_frame"); EHFrameAddress = relocate(fromGeneric(Section.sh_addr)); EHFrameSize = static_cast(Section.sh_size); } else if (Name == ".dynamic") { revng_assert(not DynamicAddress, "Duplicate .dynamic"); DynamicAddress = relocate(fromGeneric(Section.sh_addr)); } } } } // If we found a symbol table if (SymtabShdr != nullptr && SymtabShdr->sh_link != 0) { // Obtain a reference to the string table auto Strtab = TheELF.getSection(SymtabShdr->sh_link); if (not Strtab) { logAllUnhandledErrors(std::move(Strtab.takeError()), errs(), ""); revng_abort(); } auto StrtabArray = TheELF.getSectionContents(**Strtab); if (not StrtabArray) { logAllUnhandledErrors(std::move(StrtabArray.takeError()), errs(), ""); revng_abort(); } StringRef StrtabContent(reinterpret_cast(StrtabArray->data()), StrtabArray->size()); // Collect symbol names auto ELFSymbols = TheELF.symbols(SymtabShdr); if (not ELFSymbols) { logAllUnhandledErrors(std::move(ELFSymbols.takeError()), errs(), ""); revng_abort(); } for (auto &Symbol : *ELFSymbols) { auto Name = Symbol.getName(StrtabContent); if (not Name) { logAllUnhandledErrors(std::move(Name.takeError()), errs(), ""); revng_abort(); } auto SymbolType = SymbolType::fromELF(Symbol.getType()); if (shouldIgnoreSymbol(*Name) or Symbol.st_shndx == ELF::SHN_UNDEF) continue; MetaAddress Address = MetaAddress::invalid(); if (SymbolType == SymbolType::Code) Address = relocate(fromPC(Symbol.st_value)); else Address = relocate(fromGeneric(Symbol.st_value)); registerLabel(Label::createSymbol(LabelOrigin::StaticSymbol, Address, Symbol.st_size, *Name, SymbolType)); } } const auto &ElfHeader = TheELF.getHeader(); EntryPoint = relocate(fromPC(ElfHeader.e_entry)); ProgramHeaders.Count = ElfHeader.e_phnum; ProgramHeaders.Size = ElfHeader.e_phentsize; // Loop over the program headers looking for PT_LOAD segments, read them out // and create a global variable for each one of them (writable or read-only), // assign them a section and output information about them in the linking info // CSV using Elf_Phdr = const typename object::ELFFile::Elf_Phdr; using Elf_Dyn = const typename object::ELFFile::Elf_Dyn; using Elf_Addr = const typename object::ELFFile::Elf_Addr; auto ProgHeaders = TheELF.program_headers(); if (not ProgHeaders) { logAllUnhandledErrors(std::move(ProgHeaders.takeError()), errs(), ""); revng_abort(); } auto RawDataRef = ArrayRef(TheELF.base(), TheELF.getBufSize()); for (Elf_Phdr &ProgramHeader : *ProgHeaders) { switch (ProgramHeader.p_type) { case ELF::PT_LOAD: { using namespace nooverflow; SegmentInfo Segment; auto Start = relocate(fromGeneric(ProgramHeader.p_vaddr)); Segment.StartVirtualAddress = Start; Segment.EndVirtualAddress = Start + u64(ProgramHeader.p_memsz); Segment.StartFileOffset = ProgramHeader.p_offset; Segment.EndFileOffset = *add(ProgramHeader.p_offset, ProgramHeader.p_filesz); Segment.IsReadable = ProgramHeader.p_flags & ELF::PF_R; Segment.IsWriteable = ProgramHeader.p_flags & ELF::PF_W; Segment.IsExecutable = ProgramHeader.p_flags & ELF::PF_X; Segment.Data = ArrayRef(*add(RawDataRef.begin(), ProgramHeader.p_offset), ProgramHeader.p_filesz); revng_assert(contains(RawDataRef, Segment.Data)); // If it's an executable segment, and we've been asked so, register // which sections actually contain code if (Sections and not IgnoreDebugSymbols and Segment.IsExecutable) { using Elf_Shdr = const typename object::ELFFile::Elf_Shdr; auto Inserter = std::back_inserter(Segment.ExecutableSections); for (Elf_Shdr &SectionHeader : *Sections) { if (SectionHeader.sh_flags & ELF::SHF_EXECINSTR) { auto SectionStart = relocate(fromGeneric(SectionHeader.sh_addr)); auto SectionEnd = SectionStart + u64(SectionHeader.sh_size); Inserter = make_pair(SectionStart, SectionEnd); } } } Segments.push_back(Segment); // Check if it's the segment containing the program headers auto ProgramHeaderStart = ProgramHeader.p_offset; auto ProgramHeaderEnd = ProgramHeader.p_offset + u64(ProgramHeader.p_filesz); if (ProgramHeaderStart <= ElfHeader.e_phoff && ElfHeader.e_phoff < ProgramHeaderEnd) { MetaAddress PhdrAddress = (relocate(fromGeneric(ProgramHeader.p_vaddr)) + u64(ElfHeader.e_phoff) - u64(ProgramHeader.p_offset)); ProgramHeaders.Address = PhdrAddress; } } break; case ELF::PT_GNU_EH_FRAME: revng_assert(!EHFrameHdrAddress); EHFrameHdrAddress = relocate(fromGeneric(ProgramHeader.p_vaddr)); break; case ELF::PT_DYNAMIC: revng_assert(DynamicPhdr == nullptr, "Duplicate .dynamic program header"); DynamicPhdr = &ProgramHeader; MetaAddress DynamicPhdrMA = relocate(fromGeneric(DynamicPhdr->p_vaddr)); revng_assert(not DynamicAddress or DynamicPhdrMA == *DynamicAddress, ".dynamic and PT_DYNAMIC have different addresses"); DynamicAddress = relocate(DynamicPhdrMA); break; } } revng_assert((DynamicPhdr != nullptr) == (DynamicAddress.hasValue())); Optional FDEsCount; if (EHFrameHdrAddress) { MetaAddress Address = MetaAddress::invalid(); std::tie(Address, FDEsCount) = ehFrameFromEhFrameHdr(*EHFrameHdrAddress); if (EHFrameAddress) { revng_assert(*EHFrameAddress == Address); } EHFrameAddress = Address; } if (EHFrameAddress) parseEHFrame(*EHFrameAddress, FDEsCount, EHFrameSize); // Parse the .dynamic table if (DynamicPhdr != nullptr) { SmallVector NeededLibraryNameOffsets; FilePortion DynstrPortion; FilePortion DynsymPortion; FilePortion ReldynPortion; FilePortion RelpltPortion; FilePortion GotPortion; Optional SymbolsCount; Optional MIPSFirstGotSymbol; Optional MIPSLocalGotEntries; bool IsMIPS = (TheArchitecture.type() == Triple::mips or TheArchitecture.type() == Triple::mipsel); auto DynamicEntries = TheELF.dynamicEntries(); if (not DynamicEntries) { logAllUnhandledErrors(std::move(DynamicEntries.takeError()), errs(), ""); revng_abort(); } for (Elf_Dyn &DynamicTag : *DynamicEntries) { auto TheTag = DynamicTag.getTag(); MetaAddress Relocated = relocate(fromGeneric(DynamicTag.getPtr())); switch (TheTag) { case ELF::DT_NEEDED: NeededLibraryNameOffsets.push_back(DynamicTag.getVal()); break; case ELF::DT_STRTAB: DynstrPortion.setAddress(Relocated); break; case ELF::DT_STRSZ: DynstrPortion.setSize(DynamicTag.getVal()); break; case ELF::DT_SYMTAB: DynsymPortion.setAddress(Relocated); break; case ELF::DT_JMPREL: RelpltPortion.setAddress(Relocated); break; case ELF::DT_PLTRELSZ: RelpltPortion.setSize(DynamicTag.getVal()); break; case ELF::DT_REL: case ELF::DT_RELA: revng_assert(TheTag == (HasAddend ? ELF::DT_RELA : ELF::DT_REL)); ReldynPortion.setAddress(Relocated); break; case ELF::DT_RELSZ: case ELF::DT_RELASZ: revng_assert(TheTag == (HasAddend ? ELF::DT_RELASZ : ELF::DT_RELSZ)); ReldynPortion.setSize(DynamicTag.getVal()); break; case ELF::DT_PLTGOT: GotPortion.setAddress(Relocated); // Obtaint the canonical value of the global pointer in MIPS if (IsMIPS) CanonicalValues["gp"] = (Relocated + 0x7ff0).address(); break; case ELF::DT_MIPS_SYMTABNO: if (IsMIPS) SymbolsCount = DynamicTag.getVal(); break; case ELF::DT_MIPS_GOTSYM: if (IsMIPS) MIPSFirstGotSymbol = DynamicTag.getVal(); break; case ELF::DT_MIPS_LOCAL_GOTNO: if (IsMIPS) MIPSLocalGotEntries = DynamicTag.getVal(); break; } } if (NeededLibraryNames.size() > 0) revng_assert(DynstrPortion.isAvailable()); // In MIPS the GOT has one entry per symbol if (IsMIPS and SymbolsCount and MIPSFirstGotSymbol and MIPSLocalGotEntries) { uint32_t GotEntries = (*MIPSLocalGotEntries + (*SymbolsCount - *MIPSFirstGotSymbol)); GotPortion.setSize(GotEntries * sizeof(Elf_Addr)); } StringRef Dynstr; if (DynstrPortion.isAvailable()) { Dynstr = DynstrPortion.extractString(Segments); for (auto Offset : NeededLibraryNameOffsets) { StringRef LibraryName = Dynstr.slice(Offset, Dynstr.size()); NeededLibraryNames.push_back(LibraryName.data()); } } // Collect symbols count and code pointers in image base-relative // relocations if (not SymbolsCount) { SymbolsCount = std::max(symbolsCount(ReldynPortion), symbolsCount(RelpltPortion)); } // Collect function addresses contained in dynamic symbols if (SymbolsCount and *SymbolsCount > 0 and DynsymPortion.isAvailable()) { using Elf_Sym = llvm::object::Elf_Sym_Impl; DynsymPortion.setSize(*SymbolsCount * sizeof(Elf_Sym)); ArrayRef Symbols = DynsymPortion.extractAs(Segments); for (Elf_Sym Symbol : Symbols) { auto Name = Symbol.getName(Dynstr); if (not Name) { logAllUnhandledErrors(std::move(Name.takeError()), errs(), ""); revng_abort(); } auto SymbolType = SymbolType::fromELF(Symbol.getType()); if (shouldIgnoreSymbol(*Name) or Symbol.st_shndx == ELF::SHN_UNDEF) continue; MetaAddress Address = MetaAddress::invalid(); if (SymbolType == SymbolType::Code) Address = relocate(fromPC(Symbol.st_value)); else Address = relocate(fromGeneric(Symbol.st_value)); registerLabel(Label::createSymbol(LabelOrigin::DynamicSymbol, Address, Symbol.st_size, *Name, SymbolType)); } using Elf_Rel = llvm::object::Elf_Rel_Impl; if (ReldynPortion.isAvailable()) { auto Relocations = ReldynPortion.extractAs(Segments); registerRelocations(Relocations, DynsymPortion, DynstrPortion); } if (RelpltPortion.isAvailable()) { auto Relocations = RelpltPortion.extractAs(Segments); registerRelocations(Relocations, DynsymPortion, DynstrPortion); } if (IsMIPS and GotPortion.isAvailable()) { std::vector MIPSImplicitRelocations; uint32_t GotIndex = 0; // Perform local relocations on GOT if (MIPSLocalGotEntries) { for (; GotIndex < *MIPSLocalGotEntries; GotIndex++) { auto Address = GotPortion.addressAtIndex(GotIndex); Elf_Rel NewRelocation; NewRelocation.r_offset = Address.address(); NewRelocation.setSymbolAndType(0, R_MIPS_IMPLICIT_RELATIVE, false); MIPSImplicitRelocations.push_back(NewRelocation); } } // Relocate the remaining entries of the GOT with global symbols if (MIPSFirstGotSymbol and SymbolsCount and DynstrPortion.isAvailable() and DynsymPortion.isAvailable()) { for (uint32_t SymbolIndex = *MIPSFirstGotSymbol; SymbolIndex < *SymbolsCount; SymbolIndex++, GotIndex++) { auto Address = GotPortion.addressAtIndex(GotIndex); Elf_Rel NewRelocation; NewRelocation.r_offset = Address.address(); NewRelocation.setSymbolAndType(SymbolIndex, llvm::ELF::R_MIPS_JUMP_SLOT, false); MIPSImplicitRelocations.push_back(NewRelocation); } } auto Relocations = ArrayRef(MIPSImplicitRelocations); registerRelocations(Relocations, DynsymPortion, DynstrPortion); } } } for (Label &L : Labels) { MetaAddress MA = MetaAddress::invalid(); if (L.isSymbol() and L.isCode()) MA = relocate(L.address()); else if (L.isBaseRelativeValue()) MA = relocate(fromPC(L.value())); if (MA.isValid()) CodePointers.insert(MA); } } template uint64_t BinaryFile::symbolsCount(const FilePortion &Relocations) { using Elf_Rel = llvm::object::Elf_Rel_Impl; if (not Relocations.isAvailable()) return 0; uint32_t SymbolsCount = 0; revng_assert(Relocations.isExact()); for (Elf_Rel Relocation : Relocations.extractAs(Segments)) SymbolsCount = std::max(SymbolsCount, Relocation.getSymbol(false) + 1); return SymbolsCount; } Optional BinaryFile::readRawValue(MetaAddress Address, unsigned Size, Endianess E) const { bool IsLittleEndian = ((E == OriginalEndianess) ? architecture().isLittleEndian() : E == LittleEndian); for (auto &Segment : segments()) { // Note: we also consider writeable memory areas because, despite being // modifiable, can contain useful information if (Segment.contains(Address, Size) && Segment.IsReadable) { uint64_t Offset = Address - Segment.StartVirtualAddress; // Handle the [p_filesz, p_memsz] portion of the segment if (Offset > Segment.Data.size()) return 0; const unsigned char *Start = Segment.Data.data() + Offset; char Buffer[8] = { 0 }; memcpy(&Buffer, Start, std::min(static_cast(Size), Segment.Data.size() - Offset)); using support::endianness; using support::endian::read; switch (Size) { case 1: return read(&Buffer); case 2: if (IsLittleEndian) return read(&Buffer); else return read(&Buffer); case 4: if (IsLittleEndian) return read(&Buffer); else return read(&Buffer); case 8: if (IsLittleEndian) return read(&Buffer); else return read(&Buffer); default: revng_abort("Unexpected read size"); } } } return Optional(); } Label BinaryFile::parseRelocation(unsigned char RelocationType, MetaAddress Target, uint64_t Addend, StringRef SymbolName, uint64_t SymbolSize, SymbolType::Values SymbolType) { const auto &RelocationTypes = TheArchitecture.relocationTypes(); auto It = RelocationTypes.find(RelocationType); if (It == RelocationTypes.end()) { dbg << "Warning: unhandled relocation type " << static_cast(RelocationType) << "\n"; return Label::createInvalid(); } uint64_t Offset; using RD = RelocationDescription; const RD &Description = It->second; uint64_t PointerSize = TheArchitecture.pointerSize() / 8; switch (Description.Offset) { case RD::None: Offset = 0; break; case RD::Addend: Offset = Addend; break; case RD::TargetValue: Optional ReadResult = readRawValue(Target, PointerSize); if (not ReadResult) return Label::createInvalid(); Offset = *ReadResult; break; } const auto Origin = LabelOrigin::DynamicRelocation; switch (Description.Type) { case RD::BaseRelative: return Label::createBaseRelativeValue(Origin, Target, PointerSize, Offset); case RD::LabelOnly: if (shouldIgnoreSymbol(SymbolName)) return Label::createInvalid(); return Label::createSymbol(Origin, Target, SymbolSize, SymbolName, SymbolType); case RD::SymbolRelative: if (shouldIgnoreSymbol(SymbolName)) return Label::createInvalid(); return Label::createSymbolRelativeValue(Origin, Target, PointerSize, SymbolName, SymbolType, Offset); case RD::Invalid: revng_abort("Invalid relocation type"); break; } revng_abort(); } template void BinaryFile::registerRelocations(Elf_Rel_Array Relocations, const FilePortion &Dynsym, const FilePortion &Dynstr) { using Elf_Rel = llvm::object::Elf_Rel_Impl; using Elf_Sym = llvm::object::Elf_Sym_Impl; ArrayRef Symbols; if (Dynsym.isAvailable()) Symbols = Dynsym.extractAs(Segments); for (Elf_Rel Relocation : Relocations) { auto Type = static_cast(Relocation.getType(false)); uint64_t Addend = RelocationHelper::getAddend(Relocation); MetaAddress Address = relocate(fromGeneric(Relocation.r_offset)); StringRef SymbolName; uint64_t SymbolSize = 0; unsigned char SymbolType = llvm::ELF::STT_NOTYPE; if (Dynsym.isAvailable() and Dynstr.isAvailable()) { uint32_t SymbolIndex = Relocation.getSymbol(false); revng_check(SymbolIndex < Symbols.size()); const Elf_Sym &Symbol = Symbols[SymbolIndex]; auto Result = Symbol.getName(Dynstr.extractString(Segments)); if (Result) SymbolName = *Result; SymbolSize = Symbol.st_size; SymbolType = Symbol.getType(); } registerLabel(parseRelocation(Type, Address, Addend, SymbolName, SymbolSize, SymbolType::fromELF(SymbolType))); } } static LabelList &operator+=(LabelList &This, const LabelList &Other) { This.insert(std::end(This), std::begin(Other), std::end(Other)); return This; } void BinaryFile::rebuildLabelsMap() { using Interval = boost::icl::interval; // Clear the map LabelsMap.clear(); // Identify all the 0-sized labels std::vector