Signed-off-by: hmz007 <hmz007@gmail.com> Change-Id: Ib87fdbe7a0be7dc961af3500fe9ea4589a127f9f
686 lines
27 KiB
C++
686 lines
27 KiB
C++
//===- 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;
|
|
}
|
|
|
|
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));
|
|
|
|
// 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());
|
|
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
|