Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -46,6 +46,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 +67,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; @@ -81,6 +92,8 @@ bool shouldKeep(InputSectionBase *S); void assignAddresses(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 @@ -217,7 +217,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. @@ -298,6 +298,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)(); @@ -320,9 +347,12 @@ void readOutputFormat(); void readSearchDir(); void readSections(); + void readPHDRS(); void readLocationCounterValue(); void readOutputSectionDescription(); + void readOutputSectionHeaders(); + void readPhdrsType(); const static StringMap Cmd; ScriptConfiguration &Opt = *ScriptConfig; @@ -341,6 +371,7 @@ {"OUTPUT_FORMAT", &ScriptParser::readOutputFormat}, {"SEARCH_DIR", &ScriptParser::readSearchDir}, {"SECTIONS", &ScriptParser::readSections}, + {"PHDRS", &ScriptParser::readPHDRS}, {";", &ScriptParser::readNothing}}; void ScriptParser::run() { @@ -493,10 +524,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(); @@ -510,7 +563,7 @@ void ScriptParser::readOutputSectionDescription() { StringRef OutSec = next(); - Opt.Commands.push_back({SectionKind, {}, OutSec}); + Opt.Commands.push_back({SectionKind, {}, OutSec, {}}); expect(":"); expect("{"); @@ -534,6 +587,7 @@ setError("unknown command " + Tok); } } + readOutputSectionHeaders(); StringRef Tok = peek(); if (Tok.startswith("=")) { @@ -547,6 +601,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.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -52,6 +52,12 @@ H.p_type = Type; H.p_flags = Flags; } + void AddSec(OutputSectionBase *Sec) { + Last = Sec; + if (!First) + First = Sec; + H.p_align = std::max(H.p_align, Sec->getAlignment()); + } Elf_Phdr H = {}; OutputSectionBase *First = nullptr; OutputSectionBase *Last = nullptr; @@ -64,6 +70,8 @@ bool needsGot(); void createPhdrs(); + void createDefaultPhdrs(); + void createPhdrsUsingSpec(); void assignAddresses(); void assignFileOffsets(); void setPhdrs(); @@ -1013,32 +1021,31 @@ // Decide which program headers to create and which sections to include in each // one. template void Writer::createPhdrs() { + Script::X->getHeaderSpec().size() ? createPhdrsUsingSpec() + : 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()) { 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 +1058,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 +1070,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. @@ -1078,7 +1085,7 @@ // Add an entry for .dynamic. 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 +1097,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 +1107,89 @@ if (Note.First) Phdrs.push_back(std::move(Note)); +} - Out::ProgramHeaders->setSize(sizeof(Elf_Phdr) * Phdrs.size()); +template void Writer::createPhdrsUsingSpec() { + + int TlsNum = -1; + int NoteNum = -1; + int RelroNum = -1; + Phdr *Load = nullptr; + uintX_t Flags = PF_R; + + ArrayRef Headers = Script::X->getHeaderSpec(); + for (const PhdrsCommand &Hdr : Headers) { + 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 : OutputSections) { + 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); + } } // 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