//===- subzero/src/IceELFObjectWriter.cpp - ELF object file writer --------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines the writer for ELF relocatable object files.
///
//===----------------------------------------------------------------------===//
#include "IceELFObjectWriter.h"
#include "IceAssembler.h"
#include "IceDefs.h"
#include "IceELFSection.h"
#include "IceELFStreamer.h"
#include "IceGlobalContext.h"
#include "IceGlobalInits.h"
#include "IceInst.h"
#include "IceOperand.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/MathExtras.h"
namespace Ice {
namespace {
constexpr struct {
bool IsELF64;
uint16_t ELFMachine;
uint32_t ELFFlags;
} ELFTargetInfo[TargetArch_NUM] = {
#define X(tag, str, is_elf64, e_machine, e_flags) \
{ is_elf64, e_machine, e_flags } \
,
TARGETARCH_TABLE
#undef X
};
bool isELF64(const ClFlags &Flags) {
const TargetArch Arch = Flags.getTargetArch();
if (Arch >= TargetArch_NUM) {
llvm_unreachable("Invalid target arch for isELF64");
return false;
}
if (Flags.getApplicationBinaryInterface() == ABI_PNaCl &&
!Flags.getUseSandboxing()) {
// Unsandboxed PNaCl code is always ELF32 (pexes are ILP32.)
return false;
}
return ELFTargetInfo[Arch].IsELF64;
}
uint16_t getELFMachine(TargetArch Arch) {
if (Arch < TargetArch_NUM)
return ELFTargetInfo[Arch].ELFMachine;
llvm_unreachable("Invalid target arch for getELFMachine");
return EM_NONE;
}
uint32_t getELFFlags(TargetArch Arch) {
if (Arch < TargetArch_NUM)
return ELFTargetInfo[Arch].ELFFlags;
llvm_unreachable("Invalid target arch for getELFFlags");
return 0;
}
} // end of anonymous namespace
ELFObjectWriter::ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out)
: Ctx(Ctx), Str(Out), ELF64(isELF64(getFlags())) {
// Create the special bookkeeping sections now.
constexpr char NullSectionName[] = "";
NullSection = new (Ctx.allocate<ELFSection>())
ELFSection(NullSectionName, SHT_NULL, 0, 0, 0);
constexpr char ShStrTabName[] = ".shstrtab";
ShStrTab = new (Ctx.allocate<ELFStringTableSection>())
ELFStringTableSection(ShStrTabName, SHT_STRTAB, 0, 1, 0);
ShStrTab->add(ShStrTabName);
constexpr char SymTabName[] = ".symtab";
const Elf64_Xword SymTabAlign = ELF64 ? 8 : 4;
const Elf64_Xword SymTabEntSize =
ELF64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
static_assert(sizeof(Elf64_Sym) == 24 && sizeof(Elf32_Sym) == 16,
"Elf_Sym sizes cannot be derived from sizeof");
SymTab = createSection<ELFSymbolTableSection>(SymTabName, SHT_SYMTAB, 0,
SymTabAlign, SymTabEntSize);
SymTab->createNullSymbol(NullSection, &Ctx);
constexpr char StrTabName[] = ".strtab";
StrTab =
createSection<ELFStringTableSection>(StrTabName, SHT_STRTAB, 0, 1, 0);
}
template <typename T>
T *ELFObjectWriter::createSection(const std::string &Name, Elf64_Word ShType,
Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
Elf64_Xword ShEntsize) {
assert(!SectionNumbersAssigned);
T *NewSection =
new (Ctx.allocate<T>()) T(Name, ShType, ShFlags, ShAddralign, ShEntsize);
ShStrTab->add(Name);
return NewSection;
}
ELFRelocationSection *
ELFObjectWriter::createRelocationSection(const ELFSection *RelatedSection) {
// Choice of RELA vs REL is actually separate from elf64 vs elf32, but in
// practice we've only had .rela for elf64 (x86-64). In the future, the two
// properties may need to be decoupled and the ShEntSize can vary more.
const Elf64_Word ShType = ELF64 ? SHT_RELA : SHT_REL;
const std::string RelPrefix = ELF64 ? ".rela" : ".rel";
const std::string RelSectionName = RelPrefix + RelatedSection->getName();
const Elf64_Xword ShAlign = ELF64 ? 8 : 4;
const Elf64_Xword ShEntSize = ELF64 ? sizeof(Elf64_Rela) : sizeof(Elf32_Rel);
static_assert(sizeof(Elf64_Rela) == 24 && sizeof(Elf32_Rel) == 8,
"Elf_Rel/Rela sizes cannot be derived from sizeof");
constexpr Elf64_Xword ShFlags = 0;
ELFRelocationSection *RelSection = createSection<ELFRelocationSection>(
RelSectionName, ShType, ShFlags, ShAlign, ShEntSize);
RelSection->setRelatedSection(RelatedSection);
return RelSection;
}
template <typename UserSectionList>
void ELFObjectWriter::assignRelSectionNumInPairs(SizeT &CurSectionNumber,
UserSectionList &UserSections,
RelSectionList &RelSections,
SectionList &AllSections) {
RelSectionList::iterator RelIt = RelSections.begin();
RelSectionList::iterator RelE = RelSections.end();
for (ELFSection *UserSection : UserSections) {
UserSection->setNumber(CurSectionNumber++);
UserSection->setNameStrIndex(ShStrTab->getIndex(UserSection->getName()));
AllSections.push_back(UserSection);
if (RelIt != RelE) {
ELFRelocationSection *RelSection = *RelIt;
if (RelSection->getRelatedSection() == UserSection) {
RelSection->setInfoNum(UserSection->getNumber());
RelSection->setNumber(CurSectionNumber++);
RelSection->setNameStrIndex(ShStrTab->getIndex(RelSection->getName()));
AllSections.push_back(RelSection);
++RelIt;
}
}
}
// Should finish with UserIt at the same time as RelIt.
assert(RelIt == RelE);
return;
}
void ELFObjectWriter::assignRelLinkNum(SizeT SymTabNumber,
RelSectionList &RelSections) {
for (ELFRelocationSection *S : RelSections) {
S->setLinkNum(SymTabNumber);
}
}
void ELFObjectWriter::assignSectionNumbersInfo(SectionList &AllSections) {
// Go through each section, assigning them section numbers and and fill in
// the size for sections that aren't incrementally updated.
assert(!SectionNumbersAssigned);
SizeT CurSectionNumber = 0;
NullSection->setNumber(CurSectionNumber++);
// The rest of the fields are initialized to 0, and stay that way.
AllSections.push_back(NullSection);
assignRelSectionNumInPairs<TextSectionList>(CurSectionNumber, TextSections,
RelTextSections, AllSections);
assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, DataSections,
RelDataSections, AllSections);
for (ELFSection *BSSSection : BSSSections) {
BSSSection->setNumber(CurSectionNumber++);
BSSSection->setNameStrIndex(ShStrTab->getIndex(BSSSection->getName()));
AllSections.push_back(BSSSection);
}
assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, RODataSections,
RelRODataSections, AllSections);
ShStrTab->setNumber(CurSectionNumber++);
ShStrTab->setNameStrIndex(ShStrTab->getIndex(ShStrTab->getName()));
AllSections.push_back(ShStrTab);
SymTab->setNumber(CurSectionNumber++);
SymTab->setNameStrIndex(ShStrTab->getIndex(SymTab->getName()));
AllSections.push_back(SymTab);
StrTab->setNumber(CurSectionNumber++);
StrTab->setNameStrIndex(ShStrTab->getIndex(StrTab->getName()));
AllSections.push_back(StrTab);
SymTab->setLinkNum(StrTab->getNumber());
SymTab->setInfoNum(SymTab->getNumLocals());
assignRelLinkNum(SymTab->getNumber(), RelTextSections);
assignRelLinkNum(SymTab->getNumber(), RelDataSections);
assignRelLinkNum(SymTab->getNumber(), RelRODataSections);
SectionNumbersAssigned = true;
}
Elf64_Off ELFObjectWriter::alignFileOffset(Elf64_Xword Align) {
Elf64_Off OffsetInFile = Str.tell();
Elf64_Xword AlignDiff = Utils::OffsetToAlignment(OffsetInFile, Align);
if (AlignDiff == 0)
return OffsetInFile;
Str.writeZeroPadding(AlignDiff);
OffsetInFile += AlignDiff;
return OffsetInFile;
}
void ELFObjectWriter::writeFunctionCode(GlobalString FuncName, bool IsInternal,
Assembler *Asm) {
assert(!SectionNumbersAssigned);
TimerMarker T_func(&Ctx, FuncName.toStringOrEmpty());
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
ELFTextSection *Section = nullptr;
ELFRelocationSection *RelSection = nullptr;
const bool FunctionSections = getFlags().getFunctionSections();
if (TextSections.empty() || FunctionSections) {
std::string SectionName = ".text";
if (FunctionSections)
SectionName += "." + FuncName;
constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_EXECINSTR;
const Elf64_Xword ShAlign = 1 << Asm->getBundleAlignLog2Bytes();
Section = createSection<ELFTextSection>(SectionName, SHT_PROGBITS, ShFlags,
ShAlign, 0);
Elf64_Off OffsetInFile = alignFileOffset(Section->getSectionAlign());
Section->setFileOffset(OffsetInFile);
TextSections.push_back(Section);
RelSection = createRelocationSection(Section);
RelTextSections.push_back(RelSection);
} else {
Section = TextSections[0];
RelSection = RelTextSections[0];
}
const RelocOffsetT OffsetInSection = Section->getCurrentSize();
// Function symbols are set to 0 size in the symbol table, in contrast to
// data symbols which have a proper size.
constexpr SizeT SymbolSize = 0;
uint8_t SymbolType;
uint8_t SymbolBinding;
if (IsInternal && !getFlags().getDisableInternal()) {
SymbolType = STT_NOTYPE;
SymbolBinding = STB_LOCAL;
} else {
SymbolType = STT_FUNC;
SymbolBinding = STB_GLOBAL;
}
SymTab->createDefinedSym(FuncName, SymbolType, SymbolBinding, Section,
OffsetInSection, SymbolSize);
StrTab->add(FuncName);
// Copy the fixup information from per-function Assembler memory to the
// object writer's memory, for writing later.
const auto &Fixups = Asm->fixups();
if (!Fixups.empty()) {
if (!RelSection->isRela()) {
// This is a non-rela section, so we need to update the instruction stream
// with the relocation addends.
for (const auto *Fixup : Fixups) {
Fixup->emitOffset(Asm);
}
}
RelSection->addRelocations(OffsetInSection, Asm->fixups(), SymTab);
}
Section->appendData(Str, Asm->getBufferView());
}
namespace {
ELFObjectWriter::SectionType
classifyGlobalSection(const VariableDeclaration *Var) {
if (Var->getIsConstant())
return ELFObjectWriter::ROData;
if (Var->hasNonzeroInitializer())
return ELFObjectWriter::Data;
return ELFObjectWriter::BSS;
}
// Partition the Vars list by SectionType into VarsBySection. If TranslateOnly
// is non-empty, then only the TranslateOnly variable is kept for emission.
void partitionGlobalsBySection(const VariableDeclarationList &Vars,
VariableDeclarationPartition VarsBySection[]) {
for (VariableDeclaration *Var : Vars) {
if (getFlags().matchTranslateOnly(Var->getName(), 0)) {
size_t Section = classifyGlobalSection(Var);
assert(Section < ELFObjectWriter::NumSectionTypes);
VarsBySection[Section].push_back(Var);
}
}
}
} // end of anonymous namespace
void ELFObjectWriter::writeTargetRODataSection(const std::string &Name,
Elf64_Word ShType,
Elf64_Xword ShFlags,
Elf64_Xword ShAddralign,
Elf64_Xword ShEntsize,
const llvm::StringRef &SecData) {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
assert(!SectionNumbersAssigned);
ELFDataSection *Section = createSection<ELFDataSection>(
Name, ShType, ShFlags, ShAddralign, ShEntsize);
Section->setFileOffset(alignFileOffset(ShAddralign));
Section->appendData(Str, llvm::StringRef(SecData.data(), SecData.size()));
RODataSections.push_back(Section);
}
void ELFObjectWriter::writeDataSection(const VariableDeclarationList &Vars,
FixupKind RelocationKind,
const std::string &SectionSuffix,
bool IsPIC) {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
assert(!SectionNumbersAssigned);
VariableDeclarationPartition VarsBySection[ELFObjectWriter::NumSectionTypes];
for (auto &SectionList : VarsBySection)
SectionList.reserve(Vars.size());
partitionGlobalsBySection(Vars, VarsBySection);
size_t I = 0;
for (auto &SectionList : VarsBySection) {
writeDataOfType(static_cast<SectionType>(I++), SectionList, RelocationKind,
SectionSuffix, IsPIC);
}
}
namespace {
std::string MangleSectionName(const char Base[], const std::string &Suffix) {
if (Suffix.empty())
return Base;
return Base + ("." + Suffix);
}
} // end of anonymous namespace
// TODO(jvoung): Handle fdata-sections.
void ELFObjectWriter::writeDataOfType(SectionType ST,
const VariableDeclarationPartition &Vars,
FixupKind RelocationKind,
const std::string &SectionSuffix,
bool IsPIC) {
if (Vars.empty())
return;
ELFDataSection *Section;
ELFRelocationSection *RelSection;
Elf64_Xword ShAddralign = 1;
for (VariableDeclaration *Var : Vars) {
Elf64_Xword Align = Var->getAlignment();
ShAddralign = std::max(ShAddralign, Align);
}
constexpr Elf64_Xword ShEntsize = 0; // non-uniform data element size.
// Lift this out, so it can be re-used if we do fdata-sections?
switch (ST) {
case ROData: {
const std::string SectionName =
MangleSectionName(IsPIC ? ".data.rel.ro" : ".rodata", SectionSuffix);
const Elf64_Xword ShFlags = IsPIC ? (SHF_ALLOC | SHF_WRITE) : SHF_ALLOC;
Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
ShAddralign, ShEntsize);
Section->setFileOffset(alignFileOffset(ShAddralign));
RODataSections.push_back(Section);
RelSection = createRelocationSection(Section);
RelRODataSections.push_back(RelSection);
break;
}
case Data: {
const std::string SectionName = MangleSectionName(".data", SectionSuffix);
constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
ShAddralign, ShEntsize);
Section->setFileOffset(alignFileOffset(ShAddralign));
DataSections.push_back(Section);
RelSection = createRelocationSection(Section);
RelDataSections.push_back(RelSection);
break;
}
case BSS: {
const std::string SectionName = MangleSectionName(".bss", SectionSuffix);
constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
Section = createSection<ELFDataSection>(SectionName, SHT_NOBITS, ShFlags,
ShAddralign, ShEntsize);
Section->setFileOffset(alignFileOffset(ShAddralign));
BSSSections.push_back(Section);
break;
}
case NumSectionTypes:
llvm::report_fatal_error("Unknown SectionType");
break;
}
constexpr uint8_t SymbolType = STT_OBJECT;
for (VariableDeclaration *Var : Vars) {
// If the variable declaration does not have an initializer, its symtab
// entry will be created separately.
if (!Var->hasInitializer())
continue;
constexpr Elf64_Xword MinAlign = 1;
const auto Align = std::max<Elf64_Xword>(MinAlign, Var->getAlignment());
Section->padToAlignment(Str, Align);
SizeT SymbolSize = Var->getNumBytes();
bool IsExternal = Var->isExternal() || getFlags().getDisableInternal();
const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
GlobalString Name = Var->getName();
SymTab->createDefinedSym(Name, SymbolType, SymbolBinding, Section,
Section->getCurrentSize(), SymbolSize);
StrTab->add(Name);
if (!Var->hasNonzeroInitializer()) {
assert(ST == BSS || ST == ROData);
if (ST == ROData)
Section->appendZeros(Str, SymbolSize);
else
Section->setSize(Section->getCurrentSize() + SymbolSize);
} else {
assert(ST != BSS);
for (const auto *Init : Var->getInitializers()) {
switch (Init->getKind()) {
case VariableDeclaration::Initializer::DataInitializerKind: {
const auto &Data =
llvm::cast<VariableDeclaration::DataInitializer>(Init)
->getContents();
Section->appendData(Str, llvm::StringRef(Data.data(), Data.size()));
break;
}
case VariableDeclaration::Initializer::ZeroInitializerKind:
Section->appendZeros(Str, Init->getNumBytes());
break;
case VariableDeclaration::Initializer::RelocInitializerKind: {
const auto *Reloc =
llvm::cast<VariableDeclaration::RelocInitializer>(Init);
AssemblerFixup NewFixup;
NewFixup.set_position(Section->getCurrentSize());
NewFixup.set_kind(Reloc->hasFixup() ? Reloc->getFixup()
: RelocationKind);
assert(NewFixup.kind() != llvm::ELF::R_ARM_NONE);
NewFixup.set_value(Ctx.getConstantSym(
Reloc->getOffset(), Reloc->getDeclaration()->getName()));
RelSection->addRelocation(NewFixup);
Section->appendRelocationOffset(Str, RelSection->isRela(),
Reloc->getOffset());
break;
}
}
}
}
}
}
void ELFObjectWriter::writeInitialELFHeader() {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
assert(!SectionNumbersAssigned);
constexpr Elf64_Off DummySHOffset = 0;
constexpr SizeT DummySHStrIndex = 0;
constexpr SizeT DummyNumSections = 0;
if (ELF64) {
writeELFHeaderInternal<true>(DummySHOffset, DummySHStrIndex,
DummyNumSections);
} else {
writeELFHeaderInternal<false>(DummySHOffset, DummySHStrIndex,
DummyNumSections);
}
}
template <bool IsELF64>
void ELFObjectWriter::writeELFHeaderInternal(Elf64_Off SectionHeaderOffset,
SizeT SectHeaderStrIndex,
SizeT NumSections) {
// Write the e_ident: magic number, class, etc. The e_ident is byte order and
// ELF class independent.
Str.writeBytes(llvm::StringRef(ElfMagic, strlen(ElfMagic)));
Str.write8(IsELF64 ? ELFCLASS64 : ELFCLASS32);
Str.write8(ELFDATA2LSB);
Str.write8(EV_CURRENT);
Str.write8(ELFOSABI_NONE);
constexpr uint8_t ELF_ABIVersion = 0;
Str.write8(ELF_ABIVersion);
Str.writeZeroPadding(EI_NIDENT - EI_PAD);
// TODO(jvoung): Handle and test > 64K sections. See the generic ABI doc:
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.eheader.html e_shnum should
// be 0 and then actual number of sections is stored in the sh_size member of
// the 0th section.
assert(NumSections < SHN_LORESERVE);
assert(SectHeaderStrIndex < SHN_LORESERVE);
const TargetArch Arch = getFlags().getTargetArch();
// Write the rest of the file header, which does depend on byte order and ELF
// class.
Str.writeLE16(ET_REL); // e_type
Str.writeLE16(getELFMachine(getFlags().getTargetArch())); // e_machine
Str.writeELFWord<IsELF64>(1); // e_version
// Since this is for a relocatable object, there is no entry point, and no
// program headers.
Str.writeAddrOrOffset<IsELF64>(0); // e_entry
Str.writeAddrOrOffset<IsELF64>(0); // e_phoff
Str.writeAddrOrOffset<IsELF64>(SectionHeaderOffset); // e_shoff
Str.writeELFWord<IsELF64>(getELFFlags(Arch)); // e_flags
Str.writeLE16(IsELF64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); // e_ehsize
static_assert(sizeof(Elf64_Ehdr) == 64 && sizeof(Elf32_Ehdr) == 52,
"Elf_Ehdr sizes cannot be derived from sizeof");
Str.writeLE16(0); // e_phentsize
Str.writeLE16(0); // e_phnum
Str.writeLE16(IsELF64 ? sizeof(Elf64_Shdr)
: sizeof(Elf32_Shdr)); // e_shentsize
static_assert(sizeof(Elf64_Shdr) == 64 && sizeof(Elf32_Shdr) == 40,
"Elf_Shdr sizes cannot be derived from sizeof");
Str.writeLE16(static_cast<Elf64_Half>(NumSections)); // e_shnum
Str.writeLE16(static_cast<Elf64_Half>(SectHeaderStrIndex)); // e_shstrndx
}
template <typename ConstType> void ELFObjectWriter::writeConstantPool(Type Ty) {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
ConstantList Pool = Ctx.getConstantPool(Ty);
if (Pool.empty()) {
return;
}
SizeT Align = typeAlignInBytes(Ty);
size_t EntSize = typeWidthInBytes(Ty);
char Buf[20];
SizeT WriteAmt = std::min(EntSize, llvm::array_lengthof(Buf));
// Check that we write the full PrimType.
assert(WriteAmt == EntSize);
// Assume that writing WriteAmt bytes at a time allows us to avoid aligning
// between entries.
assert(WriteAmt % Align == 0);
constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_MERGE;
std::string SecBuffer;
llvm::raw_string_ostream SecStrBuf(SecBuffer);
SecStrBuf << ".rodata.cst" << WriteAmt;
ELFDataSection *Section = createSection<ELFDataSection>(
SecStrBuf.str(), SHT_PROGBITS, ShFlags, Align, WriteAmt);
RODataSections.push_back(Section);
SizeT OffsetInSection = 0;
// The symbol table entry doesn't need to know the defined symbol's size
// since this is in a section with a fixed Entry Size.
constexpr SizeT SymbolSize = 0;
Section->setFileOffset(alignFileOffset(Align));
// If the -reorder-pooled-constant option is set to true, we should shuffle
// the constants before we emit them.
if (getFlags().getReorderPooledConstants() && !Pool.empty()) {
// Use the constant's kind value as the salt for creating random number
// generator.
Operand::OperandKind K = (*Pool.begin())->getKind();
RandomNumberGenerator RNG(getFlags().getRandomSeed(),
RPE_PooledConstantReordering, K);
RandomShuffle(Pool.begin(), Pool.end(),
[&RNG](uint64_t N) { return (uint32_t)RNG.next(N); });
}
// Write the data.
for (Constant *C : Pool) {
if (!C->getShouldBePooled())
continue;
auto *Const = llvm::cast<ConstType>(C);
GlobalString SymName = Const->getLabelName();
SymTab->createDefinedSym(SymName, STT_NOTYPE, STB_LOCAL, Section,
OffsetInSection, SymbolSize);
StrTab->add(SymName);
typename ConstType::PrimType Value = Const->getValue();
memcpy(Buf, &Value, WriteAmt);
Str.writeBytes(llvm::StringRef(Buf, WriteAmt));
OffsetInSection += WriteAmt;
}
Section->setSize(OffsetInSection);
}
// Instantiate known needed versions of the template, since we are defining the
// function in the .cpp file instead of the .h file. We may need to instantiate
// constant pools for integers as well if we do constant-pooling of large
// integers to remove them from the instruction stream (fewer bytes controlled
// by an attacker).
template void ELFObjectWriter::writeConstantPool<ConstantFloat>(Type Ty);
template void ELFObjectWriter::writeConstantPool<ConstantDouble>(Type Ty);
template void ELFObjectWriter::writeConstantPool<ConstantInteger32>(Type Ty);
void ELFObjectWriter::writeAllRelocationSections() {
writeRelocationSections(RelTextSections);
writeRelocationSections(RelDataSections);
writeRelocationSections(RelRODataSections);
}
void ELFObjectWriter::writeJumpTable(const JumpTableData &JT,
FixupKind RelocationKind, bool IsPIC) {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
ELFDataSection *Section;
ELFRelocationSection *RelSection;
const Elf64_Xword PointerSize = typeWidthInBytes(getPointerType());
const Elf64_Xword ShAddralign = PointerSize;
const Elf64_Xword ShEntsize = PointerSize;
const std::string SectionName = MangleSectionName(
IsPIC ? ".data.rel.ro" : ".rodata", JT.getSectionName());
Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, SHF_ALLOC,
ShAddralign, ShEntsize);
Section->setFileOffset(alignFileOffset(ShAddralign));
RODataSections.push_back(Section);
RelSection = createRelocationSection(Section);
RelRODataSections.push_back(RelSection);
constexpr uint8_t SymbolType = STT_OBJECT;
Section->padToAlignment(Str, PointerSize);
const bool IsExternal = getFlags().getDisableInternal();
const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
const auto JumpTableName = JT.getName();
SymTab->createDefinedSym(JumpTableName, SymbolType, SymbolBinding, Section,
Section->getCurrentSize(), PointerSize);
StrTab->add(JumpTableName);
for (intptr_t TargetOffset : JT.getTargetOffsets()) {
AssemblerFixup NewFixup;
NewFixup.set_position(Section->getCurrentSize());
NewFixup.set_kind(RelocationKind);
NewFixup.set_value(Ctx.getConstantSym(TargetOffset, JT.getFunctionName()));
RelSection->addRelocation(NewFixup);
Section->appendRelocationOffset(Str, RelSection->isRela(), TargetOffset);
}
}
void ELFObjectWriter::setUndefinedSyms(const ConstantList &UndefSyms) {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
for (const Constant *S : UndefSyms) {
const auto *Sym = llvm::cast<ConstantRelocatable>(S);
GlobalString Name = Sym->getName();
assert(Name.hasStdString());
bool BadIntrinsic;
const Intrinsics::FullIntrinsicInfo *Info =
Ctx.getIntrinsicsInfo().find(Name, BadIntrinsic);
if (Info)
continue;
// Ignore BadIntrinsic, which is set if the name begins with "llvm." but
// doesn't match a known intrinsic. If we want this to turn into an error,
// we should catch it early on.
assert(Sym->getOffset() == 0);
SymTab->noteUndefinedSym(Name, NullSection);
StrTab->add(Name);
}
}
void ELFObjectWriter::writeRelocationSections(RelSectionList &RelSections) {
for (ELFRelocationSection *RelSec : RelSections) {
Elf64_Off Offset = alignFileOffset(RelSec->getSectionAlign());
RelSec->setFileOffset(Offset);
RelSec->setSize(RelSec->getSectionDataSize());
if (ELF64) {
RelSec->writeData<true>(Str, SymTab);
} else {
RelSec->writeData<false>(Str, SymTab);
}
}
}
void ELFObjectWriter::writeNonUserSections() {
TimerMarker Timer(TimerStack::TT_writeELF, &Ctx);
// Write out the shstrtab now that all sections are known.
ShStrTab->doLayout();
ShStrTab->setSize(ShStrTab->getSectionDataSize());
Elf64_Off ShStrTabOffset = alignFileOffset(ShStrTab->getSectionAlign());
ShStrTab->setFileOffset(ShStrTabOffset);
Str.writeBytes(ShStrTab->getSectionData());
SectionList AllSections;
assignSectionNumbersInfo(AllSections);
// Finalize the regular StrTab and fix up references in the SymTab.
StrTab->doLayout();
StrTab->setSize(StrTab->getSectionDataSize());
SymTab->updateIndices(StrTab);
Elf64_Off SymTabOffset = alignFileOffset(SymTab->getSectionAlign());
SymTab->setFileOffset(SymTabOffset);
SymTab->setSize(SymTab->getSectionDataSize());
SymTab->writeData(Str, ELF64);
Elf64_Off StrTabOffset = alignFileOffset(StrTab->getSectionAlign());
StrTab->setFileOffset(StrTabOffset);
Str.writeBytes(StrTab->getSectionData());
writeAllRelocationSections();
// Write out the section headers.
const size_t ShdrAlign = ELF64 ? 8 : 4;
Elf64_Off ShOffset = alignFileOffset(ShdrAlign);
for (const auto S : AllSections) {
if (ELF64)
S->writeHeader<true>(Str);
else
S->writeHeader<false>(Str);
}
// Finally write the updated ELF header w/ the correct number of sections.
Str.seek(0);
if (ELF64) {
writeELFHeaderInternal<true>(ShOffset, ShStrTab->getNumber(),
AllSections.size());
} else {
writeELFHeaderInternal<false>(ShOffset, ShStrTab->getNumber(),
AllSections.size());
}
}
} // end of namespace Ice