| //===- bolt/Rewrite/RewriteInstance.h - ELF rewriter ------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Interface to control an instance of a binary rewriting process. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef BOLT_REWRITE_REWRITE_INSTANCE_H |
| #define BOLT_REWRITE_REWRITE_INSTANCE_H |
| |
| #include "bolt/Core/BinaryContext.h" |
| #include "bolt/Core/Linker.h" |
| #include "bolt/Rewrite/MetadataManager.h" |
| #include "bolt/Utils/NameResolver.h" |
| #include "llvm/MC/StringTableBuilder.h" |
| #include "llvm/Object/ELFObjectFile.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/Regex.h" |
| #include <map> |
| #include <set> |
| #include <unordered_map> |
| |
| namespace llvm { |
| |
| class ToolOutputFile; |
| |
| namespace bolt { |
| |
| class BoltAddressTranslation; |
| class CFIReaderWriter; |
| class DWARFRewriter; |
| class ProfileReaderBase; |
| |
| /// This class encapsulates all data necessary to carry on binary reading, |
| /// disassembly, CFG building, BB reordering (among other binary-level |
| /// optimizations) and rewriting. It also has the logic to coordinate such |
| /// events. |
| class RewriteInstance { |
| public: |
| // This constructor has complex initialization that can fail during |
| // construction. Constructors can’t return errors, so clients must test \p Err |
| // after the object is constructed. Use `create` method instead. |
| RewriteInstance(llvm::object::ELFObjectFileBase *File, const int Argc, |
| const char *const *Argv, StringRef ToolPath, |
| raw_ostream &Stdout, raw_ostream &Stderr, Error &Err); |
| |
| static Expected<std::unique_ptr<RewriteInstance>> |
| create(llvm::object::ELFObjectFileBase *File, const int Argc, |
| const char *const *Argv, StringRef ToolPath, |
| raw_ostream &Stdout = llvm::outs(), |
| raw_ostream &Stderr = llvm::errs()); |
| ~RewriteInstance(); |
| |
| /// Assign profile from \p Filename to this instance. |
| Error setProfile(StringRef Filename); |
| |
| /// Run all the necessary steps to read, optimize and rewrite the binary. |
| Error run(); |
| |
| /// Diff this instance against another one. Non-const since we may run passes |
| /// to fold identical functions. |
| void compare(RewriteInstance &RI2); |
| |
| /// Return binary context. |
| const BinaryContext &getBinaryContext() const { return *BC; } |
| |
| /// Return total score of all functions for this instance. |
| uint64_t getTotalScore() const { return BC->TotalScore; } |
| |
| /// Return the name of the input file. |
| StringRef getInputFilename() const { |
| assert(InputFile && "cannot have an instance without a file"); |
| return InputFile->getFileName(); |
| } |
| |
| /// If this instance uses a profile, return appropriate profile reader. |
| const ProfileReaderBase *getProfileReader() const { |
| return ProfileReader.get(); |
| } |
| |
| private: |
| /// Populate array of binary functions and other objects of interest |
| /// from meta data in the file. |
| void discoverFileObjects(); |
| |
| /// Check if the input binary has a space reserved for BOLT and use it for new |
| /// section allocations if found. |
| void discoverBOLTReserved(); |
| |
| /// Check whether we should use DT_FINI or DT_FINI_ARRAY for instrumentation. |
| /// DT_FINI is preferred; DT_FINI_ARRAY is only used when no DT_FINI entry was |
| /// found. |
| Error discoverRtFiniAddress(); |
| |
| /// If DT_FINI_ARRAY is used for instrumentation, update the relocation of its |
| /// first entry to point to the instrumentation library's fini address. |
| void updateRtFiniReloc(); |
| |
| /// Create and initialize metadata rewriters for this instance. |
| void initializeMetadataManager(); |
| |
| /// Process fragments, locate parent functions. |
| void registerFragments(); |
| |
| /// Read info from special sections. E.g. eh_frame and .gcc_except_table |
| /// for exception and stack unwinding information. |
| Error readSpecialSections(); |
| |
| /// Adjust supplied command-line options based on input data. |
| void adjustCommandLineOptions(); |
| |
| /// Process runtime relocations. |
| void processDynamicRelocations(); |
| |
| /// Process input relocations. |
| void processRelocations(); |
| |
| /// Read relocations from a given section. |
| void readDynamicRelocations(const object::SectionRef &Section, bool IsJmpRel); |
| |
| /// Read relocations from a given RELR section. |
| void readDynamicRelrRelocations(BinarySection &Section); |
| |
| /// Print relocation information. |
| void printRelocationInfo(const RelocationRef &Rel, StringRef SymbolName, |
| uint64_t SymbolAddress, uint64_t Addend, |
| uint64_t ExtractedValue) const; |
| |
| /// Read relocations from a given section. |
| void readRelocations(const object::SectionRef &Section); |
| |
| /// Handle one relocation. |
| void handleRelocation(const object::SectionRef &RelocatedSection, |
| const RelocationRef &Rel); |
| |
| /// Mark functions that are not meant for processing as ignored. |
| void selectFunctionsToProcess(); |
| |
| /// Read information from debug sections. |
| void readDebugInfo(); |
| |
| /// Read profile data without having disassembled functions available. |
| void preprocessProfileData(); |
| |
| void processProfileDataPreCFG(); |
| |
| /// Associate profile data with functions and data objects. |
| void processProfileData(); |
| |
| /// Disassemble each function in the binary and associate it with a |
| /// BinaryFunction object, preparing all information necessary for binary |
| /// optimization. |
| void disassembleFunctions(); |
| |
| void buildFunctionsCFG(); |
| |
| void postProcessFunctions(); |
| |
| void preregisterSections(); |
| |
| /// run analyses requested in binary analysis mode. |
| void runBinaryAnalyses(); |
| |
| /// Run optimizations that operate at the binary, or post-linker, level. |
| void runOptimizationPasses(); |
| |
| /// Write code and data into an intermediary object file, map virtual to real |
| /// addresses and link the object file, resolving all relocations and |
| /// performing final relaxation. |
| void emitAndLink(); |
| |
| /// Link additional runtime code to support instrumentation. |
| void linkRuntime(); |
| |
| /// Process metadata in sections before functions are discovered. |
| void processSectionMetadata(); |
| |
| /// Process metadata in special sections before CFG is built for functions. |
| void processMetadataPreCFG(); |
| |
| /// Process metadata in special sections after CFG is built for functions. |
| void processMetadataPostCFG(); |
| |
| /// Make changes to metadata before the binary is emitted. |
| void finalizeMetadataPreEmit(); |
| |
| /// Update debug and other auxiliary information in the file. |
| void updateMetadata(); |
| |
| /// Return the list of code sections in the output order. |
| std::vector<BinarySection *> getCodeSections(); |
| |
| /// Map all sections to their final addresses. |
| void mapFileSections(BOLTLinker::SectionMapper MapSection); |
| |
| /// Map code sections generated by BOLT. |
| void mapCodeSections(BOLTLinker::SectionMapper MapSection); |
| |
| /// Map the rest of allocatable sections. |
| void mapAllocatableSections(BOLTLinker::SectionMapper MapSection); |
| |
| /// Update output object's values based on the final \p Layout. |
| void updateOutputValues(const BOLTLinker &Linker); |
| |
| /// Rewrite back all functions (hopefully optimized) that fit in the original |
| /// memory footprint for that function. If the function is now larger and does |
| /// not fit in the binary, reject it and preserve the original version of the |
| /// function. If we couldn't understand the function for some reason in |
| /// disassembleFunctions(), also preserve the original version. |
| void rewriteFile(); |
| |
| /// Return address of a function in the new binary corresponding to |
| /// \p OldAddress address in the original binary. |
| uint64_t getNewFunctionAddress(uint64_t OldAddress); |
| |
| /// Return address of a function or moved data in the new binary |
| /// corresponding to \p OldAddress address in the original binary. |
| uint64_t getNewFunctionOrDataAddress(uint64_t OldAddress); |
| |
| /// Return value for the symbol \p Name in the output. |
| uint64_t getNewValueForSymbol(const StringRef Name); |
| |
| /// Check for PT_GNU_RELRO segment presence, mark covered sections as |
| /// (dynamically) read-only (written once), as specified in LSB Chapter 12: |
| /// "segment which may be made read-only after relocations have been |
| /// processed". |
| void markGnuRelroSections(); |
| |
| /// Detect addresses and offsets available in the binary for allocating |
| /// new sections. |
| Error discoverStorage(); |
| |
| /// Adjust function sizes and set proper maximum size values after the whole |
| /// symbol table has been processed. |
| void adjustFunctionBoundaries(); |
| |
| /// Make .eh_frame section relocatable. |
| void relocateEHFrameSection(); |
| |
| /// Analyze relocation \p Rel. |
| /// Return true if the relocation was successfully processed, false otherwise. |
| /// The \p SymbolName, \p SymbolAddress, \p Addend and \p ExtractedValue |
| /// parameters will be set on success. The \p Skip argument indicates |
| /// that the relocation was analyzed, but it must not be processed. |
| bool analyzeRelocation(const object::RelocationRef &Rel, uint32_t &RType, |
| std::string &SymbolName, bool &IsSectionRelocation, |
| uint64_t &SymbolAddress, int64_t &Addend, |
| uint64_t &ExtractedValue, bool &Skip) const; |
| |
| /// Rewrite non-allocatable sections with modifications. |
| void rewriteNoteSections(); |
| |
| /// Write .eh_frame_hdr. |
| void writeEHFrameHeader(); |
| |
| /// Disassemble and create function entries for PLT. |
| void disassemblePLT(); |
| |
| /// Auxiliary function to create .plt BinaryFunction on \p EntryAddres |
| /// with the \p EntrySize size. \p TargetAddress is the .got entry |
| /// associated address. |
| void createPLTBinaryFunction(uint64_t TargetAddress, uint64_t EntryAddress, |
| uint64_t EntrySize); |
| |
| /// Disassemble PLT instruction. |
| void disassemblePLTInstruction(const BinarySection &Section, |
| uint64_t InstrOffset, MCInst &Instruction, |
| uint64_t &InstrSize); |
| |
| /// Disassemble aarch64-specific .plt \p Section auxiliary function |
| void disassemblePLTSectionAArch64(BinarySection &Section); |
| |
| /// Disassemble X86-specific .plt \p Section auxiliary function. \p EntrySize |
| /// is the expected .plt \p Section entry function size. |
| void disassemblePLTSectionX86(BinarySection &Section, uint64_t EntrySize); |
| |
| /// Disassemble riscv-specific .plt \p Section auxiliary function |
| void disassemblePLTSectionRISCV(BinarySection &Section); |
| |
| /// ELF-specific part. TODO: refactor into new class. |
| #define ELF_FUNCTION(TYPE, FUNC) \ |
| template <typename ELFT> TYPE FUNC(object::ELFObjectFile<ELFT> *Obj); \ |
| TYPE FUNC() { \ |
| if (auto *ELF32LE = dyn_cast<object::ELF32LEObjectFile>(InputFile)) \ |
| return FUNC(ELF32LE); \ |
| if (auto *ELF64LE = dyn_cast<object::ELF64LEObjectFile>(InputFile)) \ |
| return FUNC(ELF64LE); \ |
| if (auto *ELF32BE = dyn_cast<object::ELF32BEObjectFile>(InputFile)) \ |
| return FUNC(ELF32BE); \ |
| auto *ELF64BE = cast<object::ELF64BEObjectFile>(InputFile); \ |
| return FUNC(ELF64BE); \ |
| } |
| |
| /// Patch ELF book-keeping info. |
| void patchELFPHDRTable(); |
| |
| /// Create section header table. |
| ELF_FUNCTION(void, patchELFSectionHeaderTable); |
| |
| /// Create the regular symbol table and patch dyn symbol tables. |
| ELF_FUNCTION(void, patchELFSymTabs); |
| |
| /// Read dynamic section/segment of ELF. |
| ELF_FUNCTION(Error, readELFDynamic); |
| |
| /// Patch dynamic section/segment of ELF. |
| ELF_FUNCTION(void, patchELFDynamic); |
| |
| /// Patch .got |
| ELF_FUNCTION(void, patchELFGOT); |
| |
| /// Patch allocatable relocation sections. |
| ELF_FUNCTION(void, patchELFAllocatableRelaSections); |
| |
| /// Patch allocatable relr section. |
| ELF_FUNCTION(void, patchELFAllocatableRelrSection); |
| |
| /// Finalize memory image of section header string table. |
| ELF_FUNCTION(void, finalizeSectionStringTable); |
| |
| /// Return a list of all sections to include in the output binary. |
| /// Populate \p NewSectionIndex with a map of input to output indices. |
| template <typename ELFT> |
| std::vector<typename object::ELFObjectFile<ELFT>::Elf_Shdr> |
| getOutputSections(object::ELFObjectFile<ELFT> *File, |
| std::vector<uint32_t> &NewSectionIndex); |
| |
| /// Return true if \p Section should be stripped from the output binary. |
| template <typename ELFShdrTy> |
| bool shouldStrip(const ELFShdrTy &Section, StringRef SectionName); |
| |
| /// Write ELF symbol table using \p Write and \p AddToStrTab functions |
| /// based on the input file symbol table passed in \p SymTabSection. |
| /// \p IsDynSym is set to true for dynamic symbol table since we |
| /// are updating it in-place with minimal modifications. |
| template <typename ELFT, typename WriteFuncTy, typename StrTabFuncTy> |
| void updateELFSymbolTable( |
| object::ELFObjectFile<ELFT> *File, bool IsDynSym, |
| const typename object::ELFObjectFile<ELFT>::Elf_Shdr &SymTabSection, |
| const std::vector<uint32_t> &NewSectionIndex, WriteFuncTy Write, |
| StrTabFuncTy AddToStrTab); |
| |
| /// Get output index in dynamic symbol table. |
| uint32_t getOutputDynamicSymbolIndex(const MCSymbol *Symbol) { |
| auto It = SymbolIndex.find(Symbol); |
| if (It != SymbolIndex.end()) |
| return It->second; |
| return 0; |
| } |
| |
| /// Add a notes section containing the BOLT revision and command line options. |
| void addBoltInfoSection(); |
| |
| /// Add a notes section containing the serialized BOLT Address Translation |
| /// maps that can be used to enable sampling of the output binary for the |
| /// purpose of generating BOLT profile data for the input binary. |
| void addBATSection(); |
| |
| /// Loop over now emitted functions to write translation maps |
| void encodeBATSection(); |
| |
| /// Return file offset corresponding to a virtual \p Address. |
| /// Return 0 if the address has no mapping in the file, including being |
| /// part of .bss section. |
| uint64_t getFileOffsetForAddress(uint64_t Address) const; |
| |
| /// Return true if we will overwrite contents of the section instead |
| /// of appending contents to it. |
| bool willOverwriteSection(StringRef SectionName); |
| |
| public: |
| /// Standard ELF sections we overwrite. |
| static constexpr const char *SectionsToOverwrite[] = { |
| ".shstrtab", |
| ".symtab", |
| ".strtab", |
| }; |
| |
| /// Debug section to we overwrite while updating the debug info. |
| static std::vector<std::string> DebugSectionsToOverwrite; |
| |
| /// Return true if the section holds debug information. |
| static bool isDebugSection(StringRef SectionName); |
| |
| /// Adds Debug section to overwrite. |
| static void addToDebugSectionsToOverwrite(const char *Section) { |
| DebugSectionsToOverwrite.emplace_back(Section); |
| } |
| |
| private: |
| /// Manage a pipeline of metadata handlers. |
| class MetadataManager MetadataManager; |
| |
| static const char TimerGroupName[]; |
| |
| static const char TimerGroupDesc[]; |
| |
| /// Alignment value used for .eh_frame_hdr. |
| static constexpr uint64_t EHFrameHdrAlign = 4; |
| |
| /// Sections created by BOLT will have an internal name that starts with the |
| /// following prefix. Note that the prefix is used for a section lookup |
| /// internally and the section name in the output might be different. |
| static StringRef getNewSecPrefix() { return ".bolt.new"; } |
| |
| /// String to be added before the original section name. |
| /// |
| /// When BOLT creates a new section with the same name as the one in the |
| /// input file, it may need to preserve the original section. This prefix |
| /// will be added to the name of the original section. |
| static StringRef getOrgSecPrefix() { return ".bolt.org"; } |
| |
| /// Section name used for extra BOLT code in addition to .text. |
| static StringRef getBOLTTextSectionName() { return ".bolt.text"; } |
| |
| /// Symbol markers for BOLT reserved area. |
| static StringRef getBOLTReservedStart() { return "__bolt_reserved_start"; } |
| static StringRef getBOLTReservedEnd() { return "__bolt_reserved_end"; } |
| |
| /// Common section names. |
| static StringRef getEHFrameSectionName() { return ".eh_frame"; } |
| static StringRef getEHFrameHdrSectionName() { return ".eh_frame_hdr"; } |
| static StringRef getRelaDynSectionName() { return ".rela.dyn"; } |
| |
| /// FILE symbol name used for local fragments of global functions. |
| static StringRef getBOLTFileSymbolName() { return "bolt-pseudo.o"; } |
| |
| /// An instance of the input binary we are processing, externally owned. |
| llvm::object::ELFObjectFileBase *InputFile; |
| |
| /// Command line args used to process binary. |
| const int Argc; |
| const char *const *Argv; |
| StringRef ToolPath; |
| |
| std::unique_ptr<ProfileReaderBase> ProfileReader; |
| |
| std::unique_ptr<BinaryContext> BC; |
| std::unique_ptr<CFIReaderWriter> CFIRdWrt; |
| |
| // Run ExecutionEngine linker with custom memory manager and symbol resolver. |
| std::unique_ptr<BOLTLinker> Linker; |
| |
| /// Output file where we mix original code from the input binary and |
| /// optimized code for selected functions. |
| std::unique_ptr<ToolOutputFile> Out; |
| |
| /// Offset in the input file where non-allocatable sections start. |
| uint64_t FirstNonAllocatableOffset{0}; |
| |
| /// Information about program header table. |
| uint64_t PHDRTableAddress{0}; |
| uint64_t PHDRTableOffset{0}; |
| unsigned Phnum{0}; |
| |
| /// New code segment info. |
| uint64_t NewTextSegmentAddress{0}; |
| uint64_t NewTextSegmentOffset{0}; |
| uint64_t NewTextSegmentSize{0}; |
| |
| /// New writable segment info. |
| uint64_t NewWritableSegmentAddress{0}; |
| uint64_t NewWritableSegmentSize{0}; |
| |
| /// Track next available address for new allocatable sections. |
| uint64_t NextAvailableAddress{0}; |
| |
| /// Location and size of dynamic relocations. |
| std::optional<uint64_t> DynamicRelocationsAddress; |
| uint64_t DynamicRelocationsSize{0}; |
| uint64_t DynamicRelativeRelocationsCount{0}; |
| |
| // Location and size of .relr.dyn relocations. |
| std::optional<uint64_t> DynamicRelrAddress; |
| uint64_t DynamicRelrSize{0}; |
| uint64_t DynamicRelrEntrySize{0}; |
| |
| /// PLT relocations are special kind of dynamic relocations stored separately. |
| std::optional<uint64_t> PLTRelocationsAddress; |
| uint64_t PLTRelocationsSize{0}; |
| |
| /// True if relocation of specified type came from .rela.plt |
| DenseMap<uint64_t, bool> IsJmpRelocation; |
| |
| /// Index of specified symbol in the dynamic symbol table. NOTE Currently it |
| /// is filled and used only with the relocations-related symbols. |
| std::unordered_map<const MCSymbol *, uint32_t> SymbolIndex; |
| |
| /// Store all non-zero symbols in this map for a quick address lookup. |
| std::multimap<uint64_t, llvm::object::SymbolRef> FileSymRefs; |
| |
| /// FILE symbols used for disambiguating split function parents. |
| std::vector<ELFSymbolRef> FileSymbols; |
| |
| std::unique_ptr<DWARFRewriter> DebugInfoRewriter; |
| |
| std::unique_ptr<BoltAddressTranslation> BAT; |
| |
| /// Number of local symbols in newly written symbol table. |
| uint64_t NumLocalSymbols{0}; |
| |
| /// Flag indicating runtime library linking just started. |
| bool StartLinkingRuntimeLib{false}; |
| |
| /// Information on special Procedure Linkage Table sections. There are |
| /// multiple variants generated by different linkers. |
| struct PLTSectionInfo { |
| const char *Name; |
| uint64_t EntrySize{0}; |
| }; |
| |
| /// Different types of X86-64 PLT sections. |
| const PLTSectionInfo X86_64_PLTSections[5] = {{".plt", 16}, |
| {".plt.got", 8}, |
| {".plt.sec", 8}, |
| {".iplt", 16}, |
| {nullptr, 0}}; |
| |
| /// AArch64 PLT sections. |
| const PLTSectionInfo AArch64_PLTSections[4] = { |
| {".plt"}, {".plt.got"}, {".iplt"}, {nullptr}}; |
| |
| /// RISCV PLT sections. |
| const PLTSectionInfo RISCV_PLTSections[2] = {{".plt"}, {nullptr}}; |
| |
| /// Return PLT information for a section with \p SectionName or nullptr |
| /// if the section is not PLT. |
| const PLTSectionInfo *getPLTSectionInfo(StringRef SectionName) { |
| const PLTSectionInfo *PLTSI = nullptr; |
| switch (BC->TheTriple->getArch()) { |
| default: |
| break; |
| case Triple::x86_64: |
| PLTSI = X86_64_PLTSections; |
| break; |
| case Triple::aarch64: |
| PLTSI = AArch64_PLTSections; |
| break; |
| case Triple::riscv64: |
| PLTSI = RISCV_PLTSections; |
| break; |
| } |
| for (; PLTSI && PLTSI->Name; ++PLTSI) |
| if (SectionName == PLTSI->Name) |
| return PLTSI; |
| |
| return nullptr; |
| } |
| |
| /// Exception handling and stack unwinding information in this binary. |
| ErrorOr<BinarySection &> EHFrameSection{std::errc::bad_address}; |
| |
| /// Helper for accessing sections by name. |
| BinarySection *getSection(const Twine &Name) { |
| ErrorOr<BinarySection &> ErrOrSection = BC->getUniqueSectionByName(Name); |
| return ErrOrSection ? &ErrOrSection.get() : nullptr; |
| } |
| |
| /// Section header string table. |
| StringTableBuilder SHStrTab; |
| |
| /// A rewrite of strtab |
| std::string NewStrTab; |
| |
| /// Number of processed to data relocations. Used to implement the |
| /// -max-relocations debugging option. |
| uint64_t NumDataRelocations{0}; |
| |
| /// Number of failed to process relocations. |
| uint64_t NumFailedRelocations{0}; |
| |
| NameResolver NR; |
| |
| // Regex object matching split function names. |
| const Regex FunctionFragmentTemplate{"(.*)\\.(cold|warm)(\\.[0-9]+)?"}; |
| |
| friend class RewriteInstanceDiff; |
| }; |
| |
| MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch, |
| const MCInstrAnalysis *Analysis, |
| const MCInstrInfo *Info, |
| const MCRegisterInfo *RegInfo, |
| const MCSubtargetInfo *STI); |
| |
| } // namespace bolt |
| } // namespace llvm |
| |
| #endif |