Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -48,6 +48,8 @@ : SectionKind(SectionKind), Live(Live), Compressed(Compressed), Name(Name), Data(Data) {} + virtual ~InputSectionData() = default; + private: unsigned SectionKind : 3; @@ -233,7 +235,7 @@ // Write this section to a mmap'ed file, assuming Buf is pointing to // beginning of the output section. - void writeTo(uint8_t *Buf); + virtual void writeTo(uint8_t *Buf); // Relocation sections that refer to this one. llvm::TinyPtrVector RelocSections; @@ -335,6 +337,22 @@ static typename ELFT::Shdr Hdr; }; +// Virtual input section object, which allows using any output section +// as regular input section for OutputSection. +template class ProxyInputSection : public InputSection { +public: + ProxyInputSection(OutputSectionBase *OutSec); + + void writeTo(uint8_t *Buf) override; + void finalize(); + void assignTargetAddress(); + + OutputSectionBase *TargetSec; + +private: + typename ELFT::Shdr Hdr; +}; + template CommonInputSection *CommonInputSection::X; template typename ELFT::Shdr CommonInputSection::Hdr; Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -803,6 +803,36 @@ } } +template static T *zero(T *V) { + return static_cast(memset(V, 0, sizeof(*V))); +} + +template +ProxyInputSection::ProxyInputSection(OutputSectionBase *OutSec) + : InputSection(nullptr, zero(&Hdr), OutSec->getName()), + TargetSec(OutSec) { + + Hdr.sh_addralign = this->Alignment = TargetSec->getAlignment(); + Hdr.sh_type = TargetSec->getType(); + Hdr.sh_flags = TargetSec->getFlags(); +} + +template void ProxyInputSection::finalize() { + if (this->OutSec) { + TargetSec->finalize(); + Hdr.sh_size = TargetSec->getSize(); + } +} + +template void ProxyInputSection::writeTo(uint8_t *Buf) { + TargetSec->writeTo(Buf + this->OutSecOff); +} + +template void ProxyInputSection::assignTargetAddress() { + if (this->OutSec) + TargetSec->setVA(this->OutSec->getVA() + this->OutSecOff); +} + template class elf::InputSectionBase; template class elf::InputSectionBase; template class elf::InputSectionBase; @@ -842,3 +872,8 @@ template class elf::CommonInputSection; template class elf::CommonInputSection; template class elf::CommonInputSection; + +template class elf::ProxyInputSection; +template class elf::ProxyInputSection; +template class elf::ProxyInputSection; +template class elf::ProxyInputSection; Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -29,6 +29,7 @@ class SymbolBody; template class InputSectionBase; template class InputSection; +template class ProxyInputSection; template class OutputSectionBase; template class OutputSectionFactory; class InputSectionData; @@ -225,8 +226,10 @@ uint64_t getHeaderSize() override; uint64_t getSymbolValue(StringRef S) override; bool isDefined(StringRef S) override; + bool convertedToInputSection(OutputSectionBase *OutSec); std::vector *> *OutputSections; + std::vector *> ProxyInputs; int getSectionIndex(StringRef Name); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -187,6 +187,10 @@ I->Sections.push_back(CommonInputSection::X); } + for (InputSectionBase *S : ProxyInputs) + if (I->FileRe.match(Config->OutputFile) && Pat.SectionRe.match(S->Name)) + I->Sections.push_back(S); + // Sort sections as instructed by SORT-family commands and --sort-section // option. Because SORT-family commands can be nested at most two depth // (e.g. SORT_BY_NAME(SORT_BY_ALIGNMENT(.text.*))) and because the command @@ -613,6 +617,10 @@ continue; } + // Assign size to proxy input sections. + for (ProxyInputSection *PS : ProxyInputs) + PS->finalize(); + // Assign addresses as instructed by linker script SECTIONS sub-commands. Dot = 0; @@ -639,6 +647,11 @@ assignOffsets(Cmd); } + // Assign proxy target VA to allow DT_PLTGOT be + // correctly set. + for (ProxyInputSection *PS : ProxyInputs) + PS->assignTargetAddress(); + uintX_t MinVA = std::numeric_limits::max(); for (OutputSectionBase *Sec : *OutputSections) { if (Sec->getFlags() & SHF_ALLOC) @@ -857,6 +870,16 @@ return Symtab::X->find(S) != nullptr; } +template +bool LinkerScript::convertedToInputSection( + OutputSectionBase *OutSec) { + for (ProxyInputSection *PS : ProxyInputs) + if (PS->TargetSec == OutSec && PS->OutSec) + return true; + + return false; +} + // Returns indices of ELF headers containing specific section, identified // by Name. Each index is a zero based number of ELF header listed within // PHDRS {} script block. Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -261,6 +261,10 @@ CommonInputSection Common(getCommonSymbols()); CommonInputSection::X = &Common; + ProxyInputSection GotInput(Out::Got); + ProxyInputSection GotPltInput(Out::GotPlt); + + Script::X->ProxyInputs = {&GotInput, &GotPltInput}; Script::X->OutputSections = &OutputSections; if (ScriptConfig->HasSections) { Script::X->createSections(Factory); @@ -887,7 +891,7 @@ // This function add Out::* sections to OutputSections. template void Writer::addPredefinedSections() { auto Add = [&](OutputSectionBase *OS) { - if (OS) + if (OS && !Script::X->convertedToInputSection(OS)) OutputSections.push_back(OS); }; Index: test/ELF/linkerscript/output-to-input.s =================================================================== --- test/ELF/linkerscript/output-to-input.s +++ test/ELF/linkerscript/output-to-input.s @@ -0,0 +1,50 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +## First script just sets fixed section order +# RUN: echo "SECTIONS { \ +# RUN: .dynsym : {} \ +# RUN: .hash : {} \ +# RUN: .dynsttr : {} \ +# RUN: .rela.dyn : {} \ +# RUN: .rela.plt : {} \ +# RUN: .text : { *(.text) } \ +# RUN: .plt : {} \ +# RUN: .dynamic : {} \ +# RUN: .got : {} \ +# RUN: .got.plt : {} \ +# RUN: }" > %t1.script + +## Second script just sets the same section order, except +## .got and .got.plt are now merged to a single section +# RUN: echo "SECTIONS { \ +# RUN: .dynsym : {} \ +# RUN: .hash : {} \ +# RUN: .dynsttr : {} \ +# RUN: .rela.dyn : {} \ +# RUN: .rela.plt : {} \ +# RUN: .text : { *(.text) } \ +# RUN: .plt : {} \ +# RUN: .dynamic : {} \ +# RUN: .got : { *(.got) *(.got.plt) } \ +# RUN: }" > %t2.script + +# RUN: ld.lld -o %t1 --script %t1.script -shared %t +# RUN: llvm-objdump -s -j .got.plt %t1 | FileCheck --check-prefix=GOTPLT %s + +# GOTPLT: Contents of section .got.plt: +# GOTPLT-NEXT: 01b8 e0000000 00000000 00000000 00000000 +# GOTPLT-NEXT: 01c8 00000000 00000000 c6000000 00000000 + +# RUN: ld.lld -o %t2 --script %t2.script -shared %t +# RUN: llvm-objdump -s -j .got %t2 | FileCheck --check-prefix=GOT %s + +## Last 32 bytes of .got section should be equal to contents +## of .got.plt section from previous test. +# GOT: Contents of section .got: +# GOT-NEXT: 01b0 00000000 00000000 e0000000 00000000 +# GOT-NEXT: 01c0 00000000 00000000 00000000 00000000 +# GOT-NEXT: 01d0 c6000000 00000000 + +mov bar@gotpcrel(%rip), %rax +call foo@plt