//===- 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(NullSectionName, SHT_NULL, 0, 0, 0); constexpr char ShStrTabName[] = ".shstrtab"; ShStrTab = new (Ctx.allocate()) 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(SymTabName, SHT_SYMTAB, 0, SymTabAlign, SymTabEntSize); SymTab->createNullSymbol(NullSection, &Ctx); constexpr char StrTabName[] = ".strtab"; StrTab = createSection(StrTabName, SHT_STRTAB, 0, 1, 0); } template 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(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( RelSectionName, ShType, ShFlags, ShAlign, ShEntSize); RelSection->setRelatedSection(RelatedSection); return RelSection; } template 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(CurSectionNumber, TextSections, RelTextSections, AllSections); assignRelSectionNumInPairs(CurSectionNumber, DataSections, RelDataSections, AllSections); for (ELFSection *BSSSection : BSSSections) { BSSSection->setNumber(CurSectionNumber++); BSSSection->setNameStrIndex(ShStrTab->getIndex(BSSSection->getName())); AllSections.push_back(BSSSection); } assignRelSectionNumInPairs(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(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( 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(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(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(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(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(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(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(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(DummySHOffset, DummySHStrIndex, DummyNumSections); } else { writeELFHeaderInternal(DummySHOffset, DummySHStrIndex, DummyNumSections); } } template 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(1); // e_version // Since this is for a relocatable object, there is no entry point, and no // program headers. Str.writeAddrOrOffset(0); // e_entry Str.writeAddrOrOffset(0); // e_phoff Str.writeAddrOrOffset(SectionHeaderOffset); // e_shoff Str.writeELFWord(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(NumSections)); // e_shnum Str.writeLE16(static_cast(SectHeaderStrIndex)); // e_shstrndx } template 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( 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(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(Type Ty); template void ELFObjectWriter::writeConstantPool(Type Ty); template void ELFObjectWriter::writeConstantPool(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(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(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(Str, SymTab); } else { RelSec->writeData(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(Str); else S->writeHeader(Str); } // Finally write the updated ELF header w/ the correct number of sections. Str.seek(0); if (ELF64) { writeELFHeaderInternal(ShOffset, ShStrTab->getNumber(), AllSections.size()); } else { writeELFHeaderInternal(ShOffset, ShStrTab->getNumber(), AllSections.size()); } } } // end of namespace Ice