Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -15,6 +15,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/MemoryBuffer.h" +#include "Writer.h" namespace lld { namespace elf { @@ -46,6 +47,14 @@ SectionsCommandKind Kind; std::vector Expr; StringRef SectionName; + std::vector Headers; +}; + +struct PhdrsCommand { + StringRef HeaderName; + unsigned HeaderType; + bool HasFilehdr; + bool HasPhdrs; }; // ScriptConfiguration holds linker script parse results. @@ -59,6 +68,9 @@ // Used to assign addresses to sections. std::vector Commands; + // Used to assign sections to headers. + std::vector PhdrsCommands; + bool DoLayout = false; llvm::BumpPtrAllocator Alloc; @@ -75,12 +87,18 @@ typedef typename ELFT::uint uintX_t; public: + typedef Phdr Phdr; + StringRef getOutputSection(InputSectionBase *S); ArrayRef getFiller(StringRef Name); bool isDiscarded(InputSectionBase *S); bool shouldKeep(InputSectionBase *S); void assignAddresses(ArrayRef *> S); + void createScriptedPhdrs(std::vector &Phdrs, + ArrayRef *> S); int compareSections(StringRef A, StringRef B); + ArrayRef getHeaderSpec(); + std::vector getHeaderIndicesForSection(StringRef Name); private: // "ScriptConfig" is a bit too long, so define a short name for it. Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -22,6 +22,7 @@ #include "Strings.h" #include "SymbolTable.h" #include "Target.h" +#include "Writer.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ELF.h" #include "llvm/Support/FileSystem.h" @@ -217,7 +218,7 @@ for (OutputSectionBase *Sec : Sections) { StringRef Name = Sec->getName(); if (getSectionIndex(Name) == INT_MAX) - Opt.Commands.push_back({SectionKind, {}, Name}); + Opt.Commands.push_back({SectionKind, {}, Name, {}}); } // Assign addresses as instructed by linker script SECTIONS sub-commands. @@ -267,6 +268,89 @@ } template +void LinkerScript::createScriptedPhdrs( + std::vector &Phdrs, ArrayRef *> Sections) { + int TlsNum = -1; + int NoteNum = -1; + int RelroNum = -1; + Phdr *Load = nullptr; + uintX_t Flags = PF_R; + + for (const PhdrsCommand &Hdr : getHeaderSpec()) { + auto ItAdded = Phdrs.emplace(Phdrs.end(), Hdr.HeaderType, PF_R); + if (Hdr.HasFilehdr) + ItAdded->AddSec(Out::ElfHeader); + if (Hdr.HasPhdrs) + ItAdded->AddSec(Out::ProgramHeaders); + + switch (Hdr.HeaderType) { + case PT_INTERP: + if (needsInterpSection()) + ItAdded->AddSec(Out::Interp); + break; + case PT_DYNAMIC: + if (isOutputDynamic()) { + ItAdded->H.p_flags = toPhdrFlags(Out::Dynamic->getFlags()); + ItAdded->AddSec(Out::Dynamic); + } + break; + case PT_TLS: + TlsNum = std::distance(Phdrs.begin(), ItAdded); + break; + case PT_NOTE: + NoteNum = std::distance(Phdrs.begin(), ItAdded); + break; + case PT_GNU_RELRO: + RelroNum = std::distance(Phdrs.begin(), ItAdded); + break; + case PT_GNU_EH_FRAME: + if (!Out::EhFrame->empty() && Out::EhFrameHdr) { + ItAdded->H.p_flags = toPhdrFlags(Out::EhFrameHdr->getFlags()); + ItAdded->AddSec(Out::EhFrameHdr); + } + break; + } + } + + for (OutputSectionBase *Sec : Sections) { + if (!(Sec->getFlags() & SHF_ALLOC)) + break; + + if (TlsNum != -1 && (Sec->getFlags() & SHF_TLS)) + Phdrs[TlsNum].AddSec(Sec); + + if (!needsPtLoad(Sec)) + continue; + + std::vector HeaderIds = + Script::X->getHeaderIndicesForSection(Sec->getName()); + + if (!HeaderIds.empty()) { + // Assign headers specified by linker script + for (size_t HdrId : HeaderIds) { + Phdrs[HdrId].AddSec(Sec); + Phdrs[HdrId].H.p_flags |= toPhdrFlags(Sec->getFlags()); + } + } + else { + // If we have no load segment or flags've changed then we want new load + // segment. + uintX_t NewFlags = toPhdrFlags(Sec->getFlags()); + if (Load == nullptr || Flags != NewFlags) { + Load = &*Phdrs.emplace(Phdrs.end(), PT_LOAD, NewFlags); + Flags = NewFlags; + } + Load->AddSec(Sec); + } + + if (RelroNum != -1 && isRelroSection(Sec)) + Phdrs[RelroNum].AddSec(Sec); + if (NoteNum != -1 && Sec->getType() == SHT_NOTE) + Phdrs[NoteNum].AddSec(Sec); + } +} + +template ArrayRef LinkerScript::getFiller(StringRef Name) { auto I = Opt.Filler.find(Name); if (I == Opt.Filler.end()) @@ -299,6 +383,33 @@ return I < J ? -1 : 1; } +template +ArrayRef LinkerScript::getHeaderSpec() { + return Opt.PhdrsCommands; +} + +template +std::vector +LinkerScript::getHeaderIndicesForSection(StringRef Name) { + std::vector Indices; + auto ItSect = std::find_if( + Opt.Commands.begin(), Opt.Commands.end(), + [Name](const SectionsCommand &Cmd) { return Cmd.SectionName == Name; }); + if (ItSect != Opt.Commands.end()) { + SectionsCommand &SecCmd = (*ItSect); + for (StringRef HdrName : SecCmd.Headers) { + auto ItHdr = std::find_if( + Opt.PhdrsCommands.rbegin(), Opt.PhdrsCommands.rend(), + [HdrName](PhdrsCommand &Cmd) { return Cmd.HeaderName == HdrName; }); + if (ItHdr == Opt.PhdrsCommands.rend()) + error("section header '" + HdrName + "' is not listed in PHDRS"); + else + Indices.push_back(std::distance(ItHdr, Opt.PhdrsCommands.rend()) - 1); + } + } + return Indices; +} + class elf::ScriptParser : public ScriptParserBase { typedef void (ScriptParser::*Handler)(); @@ -321,9 +432,12 @@ void readOutputFormat(); void readSearchDir(); void readSections(); + void readPhdrs(); void readLocationCounterValue(); void readOutputSectionDescription(); + void readOutputSectionHeaders(); + void readPhdrsType(); const static StringMap Cmd; ScriptConfiguration &Opt = *ScriptConfig; @@ -342,6 +456,7 @@ {"OUTPUT_FORMAT", &ScriptParser::readOutputFormat}, {"SEARCH_DIR", &ScriptParser::readSearchDir}, {"SECTIONS", &ScriptParser::readSections}, + {"PHDRS", &ScriptParser::readPhdrs}, {";", &ScriptParser::readNothing}}; void ScriptParser::run() { @@ -494,10 +609,32 @@ } } +void ScriptParser::readPhdrs() { + expect("{"); + while (!Error && !skip("}")) { + StringRef Tok = next(); + Opt.PhdrsCommands.push_back({Tok, PT_NULL, false, false}); + PhdrsCommand &PhdrCmd = Opt.PhdrsCommands.back(); + + readPhdrsType(); + do { + Tok = next(); + if (Tok == "FILEHDR") + PhdrCmd.HasFilehdr = true; + else if (Tok == "PHDRS") + PhdrCmd.HasPhdrs = true; + else if (Tok == ";") + break; + else + setError("unexpected header attribute: " + Tok); + } while (!Error); + } +} + void ScriptParser::readLocationCounterValue() { expect("."); expect("="); - Opt.Commands.push_back({ExprKind, {}, ""}); + Opt.Commands.push_back({ExprKind, {}, "", {}}); SectionsCommand &Cmd = Opt.Commands.back(); while (!Error) { StringRef Tok = next(); @@ -511,7 +648,7 @@ void ScriptParser::readOutputSectionDescription() { StringRef OutSec = next(); - Opt.Commands.push_back({SectionKind, {}, OutSec}); + Opt.Commands.push_back({SectionKind, {}, OutSec, {}}); expect(":"); expect("{"); @@ -535,6 +672,7 @@ setError("unknown command " + Tok); } } + readOutputSectionHeaders(); StringRef Tok = peek(); if (Tok.startswith("=")) { @@ -548,6 +686,35 @@ } } +void ScriptParser::readOutputSectionHeaders() { + while (!Error && peek().startswith(":")) { + StringRef Tok = next(); + Tok = (Tok.size() == 1) ? next() : Tok.substr(1); + if (Tok.empty()) + setError("section header name is empty"); + else + Opt.Commands.back().Headers.push_back(Tok); + } +} + +void ScriptParser::readPhdrsType() { + static const char *typeNames[] = { + "PT_NULL", "PT_LOAD", "PT_DYNAMIC", "PT_INTERP", + "PT_NOTE", "PT_SHLIB", "PT_PHDR", "PT_TLS", + "PT_GNU_EH_FRAME", "PT_GNU_STACK", "PT_GNU_RELRO"}; + static unsigned typeCodes[] = { + PT_NULL, PT_LOAD, PT_DYNAMIC, PT_INTERP, PT_NOTE, PT_SHLIB, + PT_PHDR, PT_TLS, PT_GNU_EH_FRAME, PT_GNU_STACK, PT_GNU_RELRO}; + + PhdrsCommand &Cmd = Opt.PhdrsCommands.back(); + StringRef Tok = next(); + auto It = std::find(std::begin(typeNames), std::end(typeNames), Tok); + if (It != std::end(typeNames)) + Cmd.HeaderType = typeCodes[std::distance(std::begin(typeNames), It)]; + else + setError("invalid header type"); +} + static bool isUnderSysroot(StringRef Path) { if (Config->Sysroot == "") return false; Index: ELF/Writer.h =================================================================== --- ELF/Writer.h +++ ELF/Writer.h @@ -10,14 +10,42 @@ #ifndef LLD_ELF_WRITER_H #define LLD_ELF_WRITER_H +#include + namespace lld { namespace elf { +template class OutputSectionBase; + template class SymbolTable; template void writeResult(SymbolTable *Symtab); template void markLive(); + +template bool needsInterpSection(); + +template bool isOutputDynamic(); + +template bool isRelroSection(OutputSectionBase *Sec); + +template bool needsPtLoad(OutputSectionBase *Sec); + +uint32_t toPhdrFlags(uint64_t Flags); + +// This describes a program header entry. +// Each contains type, access flags and range of output sections that will be +// placed in it. +// Program header entry implementation +template +struct Phdr { + Phdr(unsigned Type, unsigned Flags); + void AddSec(OutputSectionBase *Sec); + + typename ELFT::Phdr H = {}; + OutputSectionBase *First = nullptr; + OutputSectionBase *Last = nullptr; +}; } } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -44,18 +44,7 @@ void run(); private: - // This describes a program header entry. - // Each contains type, access flags and range of output sections that will be - // placed in it. - struct Phdr { - Phdr(unsigned Type, unsigned Flags) { - H.p_type = Type; - H.p_flags = Flags; - } - Elf_Phdr H = {}; - OutputSectionBase *First = nullptr; - OutputSectionBase *Last = nullptr; - }; + typedef Phdr Phdr; void copyLocalSymbols(); void addReservedSymbols(); @@ -64,6 +53,7 @@ bool needsGot(); void createPhdrs(); + void createDefaultPhdrs(); void assignAddresses(); void assignFileOffsets(); void setPhdrs(); @@ -76,12 +66,6 @@ void writeBuildId(); bool isDiscarded(InputSectionBase *IS) const; StringRef getOutputSectionName(InputSectionBase *S) const; - bool needsInterpSection() const { - return !Symtab.getSharedFiles().empty() && !Config->DynamicLinker.empty(); - } - bool isOutputDynamic() const { - return !Symtab.getSharedFiles().empty() || Config->Pic; - } void addCommonSymbols(std::vector &Syms); @@ -384,7 +368,7 @@ .Default(1); } -template static bool isRelroSection(OutputSectionBase *Sec) { +template bool elf::isRelroSection(OutputSectionBase *Sec) { if (!Config->ZRelro) return false; typename ELFT::uint Flags = Sec->getFlags(); @@ -480,6 +464,45 @@ return false; } +uint32_t elf::toPhdrFlags(uint64_t Flags) { + uint32_t Ret = PF_R; + if (Flags & SHF_WRITE) + Ret |= PF_W; + if (Flags & SHF_EXECINSTR) + Ret |= PF_X; + return Ret; +} + +// Various helper functions +template bool elf::needsInterpSection() { + return !Symtab::X->getSharedFiles().empty() && + !Config->DynamicLinker.empty(); +} + +template bool elf::isOutputDynamic() { + return !Symtab::X->getSharedFiles().empty() || Config->Pic; +} + +// Program header entry +template +Phdr::Phdr(unsigned Type, unsigned Flags) { + H.p_type = Type; + H.p_flags = Flags; +} + +template +void Phdr::AddSec(OutputSectionBase *Sec) { + Last = Sec; + if (!First) + First = Sec; + H.p_align = std::max(H.p_align, Sec->getAlignment()); +} + +template struct elf::Phdr; +template struct elf::Phdr; +template struct elf::Phdr; +template struct elf::Phdr; + // Until this function is called, common symbols do not belong to any section. // This function adds them to end of BSS section. template @@ -553,7 +576,7 @@ // need these symbols, since IRELATIVE relocs are resolved through GOT // and PLT. For details, see http://www.airs.com/blog/archives/403. template void Writer::addRelIpltSymbols() { - if (isOutputDynamic() || !Out::RelaPlt) + if (isOutputDynamic() || !Out::RelaPlt) return; StringRef S = Config->Rela ? "__rela_iplt_start" : "__rel_iplt_start"; addOptionalSynthetic(Symtab, S, Out::RelaPlt, 0); @@ -680,7 +703,7 @@ // static linking the linker is required to optimize away any references to // __tls_get_addr, so it's not defined anywhere. Create a hidden definition // to avoid the undefined symbol error. - if (!isOutputDynamic()) + if (!isOutputDynamic()) Symtab.addIgnored("__tls_get_addr"); auto Define = [this](StringRef S, DefinedRegular *&Sym1, @@ -718,7 +741,7 @@ template void Writer::createSections() { // Add .interp first because some loaders want to see that section // on the first page of the executable file when loaded into memory. - if (needsInterpSection()) + if (needsInterpSection()) OutputSections.push_back(Out::Interp); // A core file does not usually contain unmodified segments except @@ -780,7 +803,7 @@ // It should be okay as no one seems to care about the type. // Even the author of gold doesn't remember why gold behaves that way. // https://sourceware.org/ml/binutils/2002-03/msg00360.html - if (isOutputDynamic()) + if (isOutputDynamic()) Symtab.addSynthetic("_DYNAMIC", Out::Dynamic, 0); // Define __rel[a]_iplt_{start,end} symbols if needed. @@ -830,7 +853,7 @@ if (Out::SymTab) Out::SymTab->addSymbol(Body); - if (isOutputDynamic() && S->includeInDynsym()) { + if (isOutputDynamic() && S->includeInDynsym()) { Out::DynSymTab->addSymbol(Body); if (auto *SS = dyn_cast>(Body)) if (SS->File->isNeeded()) @@ -859,7 +882,7 @@ // Finalizers fix each section's size. // .dynsym is finalized early since that may fill up .gnu.hash. - if (isOutputDynamic()) + if (isOutputDynamic()) Out::DynSymTab->finalize(); // Fill other section headers. The dynamic table is finalized @@ -871,7 +894,7 @@ if (Sec != Out::DynStrTab && Sec != Out::Dynamic) Sec->finalize(); - if (isOutputDynamic()) + if (isOutputDynamic()) Out::Dynamic->finalize(); // Now that all output offsets are fixed. Finalize mergeable sections @@ -906,7 +929,7 @@ Add(Out::SymTab); Add(Out::ShStrTab); Add(Out::StrTab); - if (isOutputDynamic()) { + if (isOutputDynamic()) { Add(Out::DynSymTab); bool HasVerNeed = Out::VerNeed->getNeedNum() != 0; @@ -929,7 +952,7 @@ // Even during static linking it can contain R_[*]_IRELATIVE relocations. if (Out::RelaPlt && Out::RelaPlt->hasRelocs()) { Add(Out::RelaPlt); - Out::RelaPlt->Static = !isOutputDynamic(); + Out::RelaPlt->Static = !isOutputDynamic(); } if (needsGot()) @@ -989,7 +1012,7 @@ Symtab.addSynthetic(Stop, Sec, DefinedSynthetic::SectionEnd); } -template static bool needsPtLoad(OutputSectionBase *Sec) { +template bool elf::needsPtLoad(OutputSectionBase *Sec) { if (!(Sec->getFlags() & SHF_ALLOC)) return false; @@ -1001,44 +1024,35 @@ return true; } -static uint32_t toPhdrFlags(uint64_t Flags) { - uint32_t Ret = PF_R; - if (Flags & SHF_WRITE) - Ret |= PF_W; - if (Flags & SHF_EXECINSTR) - Ret |= PF_X; - return Ret; -} - // Decide which program headers to create and which sections to include in each // one. template void Writer::createPhdrs() { + Script::X->getHeaderSpec().size() + ? Script::X->createScriptedPhdrs(Phdrs, OutputSections) + : createDefaultPhdrs(); + Out::ProgramHeaders->setSize(sizeof(Elf_Phdr) * Phdrs.size()); +} + +template void Writer::createDefaultPhdrs() { auto AddHdr = [this](unsigned Type, unsigned Flags) { return &*Phdrs.emplace(Phdrs.end(), Type, Flags); }; - auto AddSec = [](Phdr &Hdr, OutputSectionBase *Sec) { - Hdr.Last = Sec; - if (!Hdr.First) - Hdr.First = Sec; - Hdr.H.p_align = std::max(Hdr.H.p_align, Sec->getAlignment()); - }; - // The first phdr entry is PT_PHDR which describes the program header itself. Phdr &Hdr = *AddHdr(PT_PHDR, PF_R); - AddSec(Hdr, Out::ProgramHeaders); + Hdr.AddSec(Out::ProgramHeaders); // PT_INTERP must be the second entry if exists. - if (needsInterpSection()) { + if (needsInterpSection()) { Phdr &Hdr = *AddHdr(PT_INTERP, toPhdrFlags(Out::Interp->getFlags())); - AddSec(Hdr, Out::Interp); + Hdr.AddSec(Out::Interp); } // Add the first PT_LOAD segment for regular output sections. uintX_t Flags = PF_R; Phdr *Load = AddHdr(PT_LOAD, Flags); - AddSec(*Load, Out::ElfHeader); - AddSec(*Load, Out::ProgramHeaders); + Load->AddSec(Out::ElfHeader); + Load->AddSec(Out::ProgramHeaders); Phdr TlsHdr(PT_TLS, PF_R); Phdr RelRo(PT_GNU_RELRO, PF_R); @@ -1051,7 +1065,7 @@ // and put all TLS sections inside for futher use when // assign addresses. if (Sec->getFlags() & SHF_TLS) - AddSec(TlsHdr, Sec); + TlsHdr.AddSec(Sec); if (!needsPtLoad(Sec)) continue; @@ -1063,12 +1077,12 @@ Flags = NewFlags; } - AddSec(*Load, Sec); + Load->AddSec(Sec); if (isRelroSection(Sec)) - AddSec(RelRo, Sec); + RelRo.AddSec(Sec); if (Sec->getType() == SHT_NOTE) - AddSec(Note, Sec); + Note.AddSec(Sec); } // Add the TLS segment unless it's empty. @@ -1076,9 +1090,9 @@ Phdrs.push_back(std::move(TlsHdr)); // Add an entry for .dynamic. - if (isOutputDynamic()) { + if (isOutputDynamic()) { Phdr &H = *AddHdr(PT_DYNAMIC, toPhdrFlags(Out::Dynamic->getFlags())); - AddSec(H, Out::Dynamic); + H.AddSec(Out::Dynamic); } // PT_GNU_RELRO includes all sections that should be marked as @@ -1090,7 +1104,7 @@ if (!Out::EhFrame->empty() && Out::EhFrameHdr) { Phdr &Hdr = *AddHdr(PT_GNU_EH_FRAME, toPhdrFlags(Out::EhFrameHdr->getFlags())); - AddSec(Hdr, Out::EhFrameHdr); + Hdr.AddSec(Out::EhFrameHdr); } // PT_GNU_STACK is a special section to tell the loader to make the @@ -1100,8 +1114,6 @@ if (Note.First) Phdrs.push_back(std::move(Note)); - - Out::ProgramHeaders->setSize(sizeof(Elf_Phdr) * Phdrs.size()); } // The first section of each PT_LOAD and the first section after PT_GNU_RELRO Index: test/ELF/linkerscript-phdrs.s =================================================================== --- test/ELF/linkerscript-phdrs.s +++ test/ELF/linkerscript-phdrs.s @@ -0,0 +1,32 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS;} SECTIONS {. = 0x10000200; .text : {*(.text.*)} :all .foo : {*(.foo.*)} :all .data : {*(.data.*)} :all}" > %t.script + +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-readobj -program-headers %t1 | FileCheck %s +# CHECK: ProgramHeaders [ +# CHECK-NEXT: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD (0x1) +# CHECK-NEXT: Offset: 0x0 +# CHECK-NEXT: VirtualAddress: 0x10000000 +# CHECK-NEXT: PhysicalAddress: 0x10000000 +# CHECK-NEXT: FileSize: 521 +# CHECK-NEXT: MemSize: 521 +# CHECK-NEXT: Flags [ (0x7) +# CHECK-NEXT: PF_R (0x4) +# CHECK-NEXT: PF_W (0x2) +# CHECK-NEXT: PF_X (0x1) +# CHECK-NEXT: ] + + +.global _start +_start: + nop + +.section .foo.1,"a" +foo1: + .long 0 + +.section .foo.2,"aw" +foo2: + .long 0