//===- subzero/src/IceELFSection.h - Model of ELF sections ------*- C++ -*-===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Representation of ELF sections. /// //===----------------------------------------------------------------------===// #ifndef SUBZERO_SRC_ICEELFSECTION_H #define SUBZERO_SRC_ICEELFSECTION_H #include "IceDefs.h" #include "IceELFStreamer.h" #include "IceFixups.h" #include "IceOperand.h" #include "IceStringPool.h" using namespace llvm::ELF; namespace Ice { class ELFStreamer; class ELFStringTableSection; /// Base representation of an ELF section. class ELFSection { ELFSection() = delete; ELFSection(const ELFSection &) = delete; ELFSection &operator=(const ELFSection &) = delete; public: virtual ~ELFSection() = default; /// Sentinel value for a section number/index for before the final section /// index is actually known. The dummy NULL section will be assigned number 0, /// and it is referenced by the dummy 0-th symbol in the symbol table, so use /// max() instead of 0. enum { NoSectionNumber = std::numeric_limits<SizeT>::max() }; /// Constructs an ELF section, filling in fields that will be known once the /// *type* of section is decided. Other fields may be updated incrementally or /// only after the program is completely defined. ELFSection(const std::string &Name, Elf64_Word ShType, Elf64_Xword ShFlags, Elf64_Xword ShAddralign, Elf64_Xword ShEntsize) : Name(Name), Header() { Header.sh_type = ShType; Header.sh_flags = ShFlags; Header.sh_addralign = ShAddralign; Header.sh_entsize = ShEntsize; } /// Set the section number/index after it is finally known. void setNumber(SizeT N) { // Should only set the number once: from NoSectionNumber -> N. assert(Number == NoSectionNumber); Number = N; } SizeT getNumber() const { assert(Number != NoSectionNumber); return Number; } void setSize(Elf64_Xword sh_size) { Header.sh_size = sh_size; } SizeT getCurrentSize() const { return Header.sh_size; } void setNameStrIndex(Elf64_Word sh_name) { Header.sh_name = sh_name; } const std::string &getName() const { return Name; } void setLinkNum(Elf64_Word sh_link) { Header.sh_link = sh_link; } void setInfoNum(Elf64_Word sh_info) { Header.sh_info = sh_info; } void setFileOffset(Elf64_Off sh_offset) { Header.sh_offset = sh_offset; } Elf64_Xword getSectionAlign() const { return Header.sh_addralign; } /// Write the section header out with the given streamer. template <bool IsELF64> void writeHeader(ELFStreamer &Str); protected: /// Name of the section in convenient string form (instead of a index into the /// Section Header String Table, which is not known till later). const std::string Name; // The fields of the header. May only be partially initialized, but should // be fully initialized before writing. Elf64_Shdr Header; /// The number of the section after laying out sections. SizeT Number = NoSectionNumber; }; /// Models text/code sections. Code is written out incrementally and the size of /// the section is then updated incrementally. class ELFTextSection : public ELFSection { ELFTextSection() = delete; ELFTextSection(const ELFTextSection &) = delete; ELFTextSection &operator=(const ELFTextSection &) = delete; public: using ELFSection::ELFSection; void appendData(ELFStreamer &Str, const llvm::StringRef MoreData); }; /// Models data/rodata sections. Data is written out incrementally and the size /// of the section is then updated incrementally. Some rodata sections may have /// fixed entsize and duplicates may be mergeable. class ELFDataSection : public ELFSection { ELFDataSection() = delete; ELFDataSection(const ELFDataSection &) = delete; ELFDataSection &operator=(const ELFDataSection &) = delete; public: using ELFSection::ELFSection; void appendData(ELFStreamer &Str, const llvm::StringRef MoreData); void appendZeros(ELFStreamer &Str, SizeT NumBytes); void appendRelocationOffset(ELFStreamer &Str, bool IsRela, RelocOffsetT RelocOffset); /// Pad the next section offset for writing data elements to the requested /// alignment. If the section is NOBITS then do not actually write out the /// padding and only update the section size. void padToAlignment(ELFStreamer &Str, Elf64_Xword Align); }; /// Model of ELF symbol table entries. Besides keeping track of the fields /// required for an elf symbol table entry it also tracks the number that /// represents the symbol's final index in the symbol table. struct ELFSym { Elf64_Sym Sym; ELFSection *Section; SizeT Number; /// Sentinel value for symbols that haven't been assigned a number yet. The /// dummy 0-th symbol will be assigned number 0, so don't use that. enum { UnknownNumber = std::numeric_limits<SizeT>::max() }; void setNumber(SizeT N) { assert(Number == UnknownNumber); Number = N; } SizeT getNumber() const { assert(Number != UnknownNumber); return Number; } }; /// Models a symbol table. Symbols may be added up until updateIndices is /// called. At that point the indices of each symbol will be finalized. class ELFSymbolTableSection : public ELFSection { ELFSymbolTableSection() = delete; ELFSymbolTableSection(const ELFSymbolTableSection &) = delete; ELFSymbolTableSection &operator=(const ELFSymbolTableSection &) = delete; public: ELFSymbolTableSection(const std::string &Name, Elf64_Word ShType, Elf64_Xword ShFlags, Elf64_Xword ShAddralign, Elf64_Xword ShEntsize) : ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize), NullSymbolName(), NullSymbol(nullptr) {} /// Create initial entry for a symbol when it is defined. Each entry should /// only be defined once. We might want to allow Name to be a dummy name /// initially, then get updated to the real thing, since Data initializers are /// read before the bitcode's symbol table is read. void createDefinedSym(GlobalString Name, uint8_t Type, uint8_t Binding, ELFSection *Section, RelocOffsetT Offset, SizeT Size); /// Note that a symbol table entry needs to be created for the given symbol /// because it is undefined. void noteUndefinedSym(GlobalString Name, ELFSection *NullSection); const ELFSym *findSymbol(GlobalString Name) const; void createNullSymbol(ELFSection *NullSection, GlobalContext *Ctx); const ELFSym *getNullSymbol() const { return NullSymbol; } size_t getSectionDataSize() const { return (LocalSymbols.size() + GlobalSymbols.size()) * Header.sh_entsize; } size_t getNumLocals() const { return LocalSymbols.size(); } void updateIndices(const ELFStringTableSection *StrTab); void writeData(ELFStreamer &Str, bool IsELF64); private: // Map from symbol name to its symbol information. This assumes symbols are // unique across all sections. using SymtabKey = GlobalString; using SymMap = std::map<SymtabKey, ELFSym>; template <bool IsELF64> void writeSymbolMap(ELFStreamer &Str, const SymMap &Map); GlobalString NullSymbolName; const ELFSym *NullSymbol; // Keep Local and Global symbols separate, since the sh_info needs to know // the index of the last LOCAL. SymMap LocalSymbols; SymMap GlobalSymbols; }; /// Models a relocation section. class ELFRelocationSection : public ELFSection { ELFRelocationSection() = delete; ELFRelocationSection(const ELFRelocationSection &) = delete; ELFRelocationSection &operator=(const ELFRelocationSection &) = delete; public: ELFRelocationSection(const std::string &Name, Elf64_Word ShType, Elf64_Xword ShFlags, Elf64_Xword ShAddralign, Elf64_Xword ShEntsize) : ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize), RelatedSection(nullptr) {} const ELFSection *getRelatedSection() const { return RelatedSection; } void setRelatedSection(const ELFSection *Section) { RelatedSection = Section; } /// Track additional relocations which start out relative to offset 0, but /// should be adjusted to be relative to BaseOff. void addRelocations(RelocOffsetT BaseOff, const FixupRefList &FixupRefs, ELFSymbolTableSection *SymTab); /// Track a single additional relocation. void addRelocation(const AssemblerFixup &Fixup) { Fixups.push_back(Fixup); } size_t getSectionDataSize() const; template <bool IsELF64> void writeData(ELFStreamer &Str, const ELFSymbolTableSection *SymTab); bool isRela() const { return Header.sh_type == SHT_RELA; } private: const ELFSection *RelatedSection; FixupList Fixups; }; /// Models a string table. The user will build the string table by adding /// strings incrementally. At some point, all strings should be known and /// doLayout() should be called. After that, no other strings may be added. /// However, the final offsets of the strings can be discovered and used to fill /// out section headers and symbol table entries. class ELFStringTableSection : public ELFSection { ELFStringTableSection() = delete; ELFStringTableSection(const ELFStringTableSection &) = delete; ELFStringTableSection &operator=(const ELFStringTableSection &) = delete; public: using ELFSection::ELFSection; /// Add a string to the table, in preparation for final layout. void add(const std::string &Str); void add(GlobalString Str) { if (Str.hasStdString()) add(Str.toString()); } /// Finalizes the layout of the string table and fills in the section Data. void doLayout(); /// The first byte of the string table should be \0, so it is an invalid /// index. Indices start out as unknown until layout is complete. enum { UnknownIndex = 0 }; /// Grabs the final index of a string after layout. Returns UnknownIndex if /// the string's index is not found. size_t getIndex(const std::string &Str) const; llvm::StringRef getSectionData() const { assert(isLaidOut()); return llvm::StringRef(reinterpret_cast<const char *>(StringData.data()), StringData.size()); } size_t getSectionDataSize() const { return getSectionData().size(); } private: bool isLaidOut() const { return !StringData.empty(); } /// Strings can share a string table entry if they share the same suffix. /// E.g., "pop" and "lollipop" can both use the characters in "lollipop", but /// "pops" cannot, and "unpop" cannot either. Though, "pop", "lollipop", and /// "unpop" share "pop" as the suffix, "pop" can only share the characters /// with one of them. struct SuffixComparator { bool operator()(const std::string &StrA, const std::string &StrB) const; }; using StringToIndexType = std::map<std::string, size_t, SuffixComparator>; /// Track strings to their index. Index will be UnknownIndex if not yet laid /// out. StringToIndexType StringToIndexMap; using RawDataType = std::vector<uint8_t>; RawDataType StringData; }; template <bool IsELF64> void ELFSection::writeHeader(ELFStreamer &Str) { Str.writeELFWord<IsELF64>(Header.sh_name); Str.writeELFWord<IsELF64>(Header.sh_type); Str.writeELFXword<IsELF64>(Header.sh_flags); Str.writeAddrOrOffset<IsELF64>(Header.sh_addr); Str.writeAddrOrOffset<IsELF64>(Header.sh_offset); Str.writeELFXword<IsELF64>(Header.sh_size); Str.writeELFWord<IsELF64>(Header.sh_link); Str.writeELFWord<IsELF64>(Header.sh_info); Str.writeELFXword<IsELF64>(Header.sh_addralign); Str.writeELFXword<IsELF64>(Header.sh_entsize); } template <bool IsELF64> void ELFSymbolTableSection::writeSymbolMap(ELFStreamer &Str, const SymMap &Map) { // The order of the fields is different, so branch on IsELF64. if (IsELF64) { for (auto &KeyValue : Map) { const Elf64_Sym &SymInfo = KeyValue.second.Sym; Str.writeELFWord<IsELF64>(SymInfo.st_name); Str.write8(SymInfo.st_info); Str.write8(SymInfo.st_other); Str.writeLE16(SymInfo.st_shndx); Str.writeAddrOrOffset<IsELF64>(SymInfo.st_value); Str.writeELFXword<IsELF64>(SymInfo.st_size); } } else { for (auto &KeyValue : Map) { const Elf64_Sym &SymInfo = KeyValue.second.Sym; Str.writeELFWord<IsELF64>(SymInfo.st_name); Str.writeAddrOrOffset<IsELF64>(SymInfo.st_value); Str.writeELFWord<IsELF64>(SymInfo.st_size); Str.write8(SymInfo.st_info); Str.write8(SymInfo.st_other); Str.writeLE16(SymInfo.st_shndx); } } } template <bool IsELF64> void ELFRelocationSection::writeData(ELFStreamer &Str, const ELFSymbolTableSection *SymTab) { for (const AssemblerFixup &Fixup : Fixups) { const ELFSym *Symbol; if (Fixup.isNullSymbol()) { Symbol = SymTab->getNullSymbol(); } else if (Fixup.valueIsSymbol()) { Symbol = Fixup.getSymbolValue(); } else { GlobalString Name = Fixup.symbol(); Symbol = SymTab->findSymbol(Name); if (!Symbol) llvm::report_fatal_error(Name + ": Missing symbol mentioned in reloc"); } if (IsELF64) { Elf64_Rela Rela; Rela.r_offset = Fixup.position(); Rela.setSymbolAndType(Symbol->getNumber(), Fixup.kind()); Rela.r_addend = Fixup.offset(); Str.writeAddrOrOffset<IsELF64>(Rela.r_offset); Str.writeELFXword<IsELF64>(Rela.r_info); Str.writeELFXword<IsELF64>(Rela.r_addend); } else { Elf32_Rel Rel; Rel.r_offset = Fixup.position(); Rel.setSymbolAndType(Symbol->getNumber(), Fixup.kind()); Str.writeAddrOrOffset<IsELF64>(Rel.r_offset); Str.writeELFWord<IsELF64>(Rel.r_info); } } } } // end of namespace Ice #endif // SUBZERO_SRC_ICEELFSECTION_H