//===- subzero/src/IceELFObjectWriter.h - ELF object writer -----*- C++ -*-===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Abstraction for a writer that is responsible for writing an ELF file. /// //===----------------------------------------------------------------------===// #ifndef SUBZERO_SRC_ICEELFOBJECTWRITER_H #define SUBZERO_SRC_ICEELFOBJECTWRITER_H #include "IceDefs.h" #include "IceELFSection.h" #include "IceELFStreamer.h" #include "IceTypes.h" using namespace llvm::ELF; namespace Ice { using VariableDeclarationPartition = std::vector<VariableDeclaration *>; /// Higher level ELF object writer. Manages section information and writes the /// final ELF object. The object writer will write to file the code and data as /// it is being defined (rather than keep a copy). After all definitions are /// written out, it will finalize the bookkeeping sections and write them out. /// Expected usage: /// /// (1) writeInitialELFHeader (invoke once) /// (2) writeDataSection (may be invoked multiple times, as long as /// SectionSuffix is unique) /// (3) writeFunctionCode (must invoke once per function) /// (4) writeConstantPool (must invoke once per pooled primitive type) /// (5) setUndefinedSyms (invoke once) /// (6) writeNonUserSections (invoke once) /// /// The requirement for writeDataSection to be invoked only once can be relaxed /// if using -fdata-sections. The requirement to invoke only once without /// -fdata-sections is so that variables that belong to each possible /// SectionType are contiguous in the file. With -fdata-sections, each global /// variable is in a separate section and therefore the sections will be /// trivially contiguous. class ELFObjectWriter { ELFObjectWriter() = delete; ELFObjectWriter(const ELFObjectWriter &) = delete; ELFObjectWriter &operator=(const ELFObjectWriter &) = delete; public: ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out); /// Write the initial ELF header. This is just to reserve space in the ELF /// file. Reserving space allows the other functions to write text and data /// directly to the file and get the right file offsets. void writeInitialELFHeader(); /// Copy initializer data for globals to file and note the offset and size of /// each global's definition in the symbol table. Use the given target's /// RelocationKind for any relocations. void writeDataSection(const VariableDeclarationList &Vars, FixupKind RelocationKind, const std::string &SectionSuffix, bool IsPIC); /// Copy data of a function's text section to file and note the offset of the /// symbol's definition in the symbol table. Copy the text fixups for use /// after all functions are written. The text buffer and fixups are extracted /// from the Assembler object. void writeFunctionCode(GlobalString FuncName, bool IsInternal, Assembler *Asm); /// Queries the GlobalContext for constant pools of the given type and writes /// out read-only data sections for those constants. This also fills the /// symbol table with labels for each constant pool entry. template <typename ConstType> void writeConstantPool(Type Ty); /// Write a jump table and register fixups for the target addresses. void writeJumpTable(const JumpTableData &JT, FixupKind RelocationKind, bool IsPIC); /// Populate the symbol table with a list of external/undefined symbols. void setUndefinedSyms(const ConstantList &UndefSyms); /// Do final layout and write out the rest of the object file. Finally, patch /// up the initial ELF header with the final info. void writeNonUserSections(); /// Which type of ELF section a global variable initializer belongs to. This /// is used as an array index so should start at 0 and be contiguous. enum SectionType { ROData = 0, Data, BSS, NumSectionTypes }; /// Create target specific section with the given information about section. void writeTargetRODataSection(const std::string &Name, Elf64_Word ShType, Elf64_Xword ShFlags, Elf64_Xword ShAddralign, Elf64_Xword ShEntsize, const llvm::StringRef &SecData); private: GlobalContext &Ctx; ELFStreamer &Str; bool SectionNumbersAssigned = false; bool ELF64; // All created sections, separated into different pools. using SectionList = std::vector<ELFSection *>; using TextSectionList = std::vector<ELFTextSection *>; using DataSectionList = std::vector<ELFDataSection *>; using RelSectionList = std::vector<ELFRelocationSection *>; TextSectionList TextSections; RelSectionList RelTextSections; DataSectionList DataSections; RelSectionList RelDataSections; DataSectionList RODataSections; RelSectionList RelRODataSections; DataSectionList BSSSections; // Handles to special sections that need incremental bookkeeping. ELFSection *NullSection; ELFStringTableSection *ShStrTab; ELFSymbolTableSection *SymTab; ELFStringTableSection *StrTab; template <typename T> T *createSection(const std::string &Name, Elf64_Word ShType, Elf64_Xword ShFlags, Elf64_Xword ShAddralign, Elf64_Xword ShEntsize); /// Create a relocation section, given the related section (e.g., .text, /// .data., .rodata). ELFRelocationSection * createRelocationSection(const ELFSection *RelatedSection); /// Align the file position before writing out a section's data, and return /// the position of the file. Elf64_Off alignFileOffset(Elf64_Xword Align); /// Assign an ordering / section numbers to each section. Fill in other /// information that is only known near the end (such as the size, if it /// wasn't already incrementally updated). This then collects all sections in /// the decided order, into one vector, for conveniently writing out all of /// the section headers. void assignSectionNumbersInfo(SectionList &AllSections); /// This function assigns .foo and .rel.foo consecutive section numbers. It /// also sets the relocation section's sh_info field to the related section's /// number. template <typename UserSectionList> void assignRelSectionNumInPairs(SizeT &CurSectionNumber, UserSectionList &UserSections, RelSectionList &RelSections, SectionList &AllSections); /// Link the relocation sections to the symbol table. void assignRelLinkNum(SizeT SymTabNumber, RelSectionList &RelSections); /// Helper function for writeDataSection. Writes a data section of type /// SectionType, given the global variables Vars belonging to that /// SectionType. void writeDataOfType(SectionType SectionType, const VariableDeclarationPartition &Vars, FixupKind RelocationKind, const std::string &SectionSuffix, bool IsPIC); /// Write the final relocation sections given the final symbol table. May also /// be able to seek around the file and resolve function calls that are for /// functions within the same section. void writeAllRelocationSections(); void writeRelocationSections(RelSectionList &RelSections); /// Write the ELF file header with the given information about sections. template <bool IsELF64> void writeELFHeaderInternal(Elf64_Off SectionHeaderOffset, SizeT SectHeaderStrIndex, SizeT NumSections); }; } // end of namespace Ice #endif // SUBZERO_SRC_ICEELFOBJECTWRITER_H