blob: 7cfdb57dfc9f3f78377258f7b8d34e43b5306948 [file] [log] [blame]
//===- 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/Utils/NameResolver.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Error.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 createRewriteInstance instead.
RewriteInstance(llvm::object::ELFObjectFileBase *File, const int Argc,
const char *const *Argv, StringRef ToolPath, Error &Err);
static Expected<std::unique_ptr<RewriteInstance>>
createRewriteInstance(llvm::object::ELFObjectFileBase *File, const int Argc,
const char *const *Argv, StringRef ToolPath);
~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();
}
/// Set the build-id string if we did not fail to parse the contents of the
/// ELF note section containing build-id information.
void parseBuildID();
/// The build-id is typically a stream of 20 bytes. Return these bytes in
/// printable hexadecimal form if they are available, or std::nullopt
/// otherwise.
std::optional<std::string> getPrintableBuildID() const;
/// If this instance uses a profile, return appropriate profile reader.
const ProfileReaderBase *getProfileReader() const {
return ProfileReader.get();
}
private:
using ELF64LEPhdrTy = object::ELF64LEFile::Elf_Phdr;
/// Populate array of binary functions and other objects of interest
/// from meta data in the file.
void discoverFileObjects();
/// 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();
/// Insert an LKMarker for a given code pointer \p PC from a non-code section
/// \p SectionName.
void insertLKMarker(uint64_t PC, uint64_t SectionOffset,
int32_t PCRelativeOffset, bool IsPCRelative,
StringRef SectionName);
/// Process linux kernel special sections and their relocations.
void processLKSections();
/// Process special linux kernel section, __ex_table.
void processLKExTable();
/// Process special linux kernel section, .pci_fixup.
void processLKPCIFixup();
/// Process __ksymtab and __ksymtab_gpl.
void processLKKSymtab(bool IsGPL = false);
/// Process special linux kernel section, __bug_table.
void processLKBugTable();
/// Process special linux kernel section, .smp_locks.
void processLKSMPLocks();
/// Read relocations from a given section.
void readDynamicRelocations(const object::SectionRef &Section, bool IsJmpRel);
/// 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 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();
/// Update debug and other auxiliary information in the file.
void updateMetadata();
/// Update SDTMarkers' locations for the output binary.
void updateSDTMarkers();
/// Update LKMarkers' locations for the output binary.
void updateLKMarkers();
/// Update address of MCDecodedPseudoProbe.
void updatePseudoProbes();
/// Encode MCDecodedPseudoProbe.
void encodePseudoProbes();
/// Return the list of code sections in the output order.
std::vector<BinarySection *> getCodeSections();
/// Map all sections to their final addresses.
void mapFileSections(RuntimeDyld &RTDyld);
/// Map code sections generated by BOLT.
void mapCodeSections(RuntimeDyld &RTDyld);
/// Map the rest of allocatable sections.
void mapAllocatableSections(RuntimeDyld &RTDyld);
/// Update output object's values based on the final \p Layout.
void updateOutputValues(const MCAsmLayout &Layout);
/// 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);
/// 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, uint64_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 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);
/// 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);
/// 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();
/// Update the ELF note section containing the binary build-id to reflect
/// a new build-id, so tools can differentiate between the old and the
/// rewritten binary.
void patchBuildID();
/// Return file offset corresponding to a given virtual address.
uint64_t getFileOffsetFor(uint64_t Address) {
assert(Address >= NewTextSegmentAddress &&
"address in not in the new text segment");
return Address - NewTextSegmentAddress + NewTextSegmentOffset;
}
/// 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);
/// Parse .note.stapsdt section
void parseSDTNotes();
/// Parse .pseudo_probe_desc section and .pseudo_probe section
/// Setup Pseudo probe decoder
void parsePseudoProbe();
/// Print all SDT markers
void printSDTMarkers();
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);
/// Return true if the section holds linux kernel symbol information.
static bool isKSymtabSection(StringRef SectionName);
/// Adds Debug section to overwrite.
static void addToDebugSectionsToOverwrite(const char *Section) {
DebugSectionsToOverwrite.emplace_back(Section);
}
private:
/// Get the contents of the LSDA section for this binary.
ArrayRef<uint8_t> getLSDAData();
/// Get the mapped address of the LSDA section for this binary.
uint64_t getLSDAAddress();
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"; }
/// Common section names.
static StringRef getEHFrameSectionName() { return ".eh_frame"; }
/// 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<RuntimeDyld> RTDyld;
/// 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};
/// 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};
/// 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::map<uint64_t, llvm::object::SymbolRef> FileSymRefs;
std::unique_ptr<DWARFRewriter> DebugInfoRewriter;
std::unique_ptr<BoltAddressTranslation> BAT;
/// Number of local symbols in newly written symbol table.
uint64_t NumLocalSymbols{0};
/// 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[4] = {
{ ".plt", 16 },
{ ".plt.got", 8 },
{ ".plt.sec", 8 },
{ nullptr, 0 }
};
/// AArch64 PLT sections.
const PLTSectionInfo AArch64_PLTSections[3] = {
{".plt"}, {".iplt"}, {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;
}
for (; PLTSI && PLTSI->Name; ++PLTSI)
if (SectionName == PLTSI->Name)
return PLTSI;
return nullptr;
}
/// Exception handling and stack unwinding information in this binary.
ErrorOr<BinarySection &> LSDASection{std::errc::bad_address};
ErrorOr<BinarySection &> EHFrameSection{std::errc::bad_address};
/// .got.plt sections.
///
/// Contains jump slots (addresses) indirectly referenced by
/// instructions in .plt section.
ErrorOr<BinarySection &> GOTPLTSection{std::errc::bad_address};
/// .rela.plt section.
///
/// Contains relocations against .got.plt.
ErrorOr<BinarySection &> RelaPLTSection{std::errc::bad_address};
ErrorOr<BinarySection &> RelaDynSection{std::errc::bad_address};
/// .note.gnu.build-id section.
ErrorOr<BinarySection &> BuildIDSection{std::errc::bad_address};
/// .note.stapsdt section.
/// Contains information about statically defined tracing points
ErrorOr<BinarySection &> SDTSection{std::errc::bad_address};
/// .pseudo_probe_desc section.
/// Contains information about pseudo probe description, like its related
/// function
ErrorOr<BinarySection &> PseudoProbeDescSection{std::errc::bad_address};
/// .pseudo_probe section.
/// Contains information about pseudo probe details, like its address
ErrorOr<BinarySection &> PseudoProbeSection{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;
}
/// A reference to the build-id bytes in the original binary
StringRef BuildID;
/// Keep track of functions we fail to write in the binary. We need to avoid
/// rewriting CFI info for these functions.
std::vector<uint64_t> FailedAddresses;
/// Keep track of which functions didn't fit in their original space in the
/// last emission, so that we may either decide to split or not optimize them.
std::set<uint64_t> LargeFunctions;
/// 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;
friend class RewriteInstanceDiff;
};
MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
const MCInstrAnalysis *Analysis,
const MCInstrInfo *Info,
const MCRegisterInfo *RegInfo);
} // namespace bolt
} // namespace llvm
#endif