blob: 0ee3f03c99eb72c3a99b78cbb1cbb5f589ac90a7 [file] [log] [blame]
//===- 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
/// 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"
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 IceString &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 IceString &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 IceString 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 IceString &Name, Elf64_Word ShType,
Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
Elf64_Xword ShEntsize)
: ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize),
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(const IceString &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(const IceString &Name, ELFSection *NullSection);
const ELFSym *findSymbol(const IceString &Name) const;
void createNullSymbol(ELFSection *NullSection);
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.
typedef IceString SymtabKey;
typedef std::map<SymtabKey, ELFSym> SymMap;
template <bool IsELF64>
void writeSymbolMap(ELFStreamer &Str, const SymMap &Map);
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 IceString &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);
/// Track a single additional relocation.
void addRelocation(const AssemblerFixup &Fixup) { Fixups.push_back(Fixup); }
size_t getSectionDataSize() const;
template <bool IsELF64>
void writeData(const GlobalContext &Ctx, 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 IceString &Str);
/// 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 IceString &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 IceString &StrA, const IceString &StrB) const;
};
typedef std::map<IceString, size_t, SuffixComparator> StringToIndexType;
/// Track strings to their index. Index will be UnknownIndex if not
/// yet laid out.
StringToIndexType StringToIndexMap;
typedef std::vector<uint8_t> RawDataType;
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(const GlobalContext &Ctx, ELFStreamer &Str,
const ELFSymbolTableSection *SymTab) {
for (const AssemblerFixup &Fixup : Fixups) {
const ELFSym *Symbol;
if (Fixup.isNullSymbol())
Symbol = SymTab->getNullSymbol();
else
Symbol = SymTab->findSymbol(Fixup.symbol(&Ctx));
if (!Symbol)
llvm::report_fatal_error("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