diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -987,11 +987,6 @@ if (sec.name == "/DISCARD/") return true; - // We do not remove empty sections that are explicitly - // assigned to any segment. - if (!sec.phdrs.empty()) - return false; - // We do not want to remove OutputSections with expressions that reference // symbols even if the OutputSection is empty. We want to ensure that the // expressions can be evaluated and report an error if they cannot. @@ -1017,6 +1012,18 @@ return true; } +static void maybePropagatePhdrs(OutputSection &sec, + std::vector &phdrs) { + if (sec.phdrs.empty()) { + // To match the bfd linker script behaviour, only propagate program + // headers to sections that are allocated. + if (sec.flags & SHF_ALLOC) + sec.phdrs = phdrs; + } else { + phdrs = sec.phdrs; + } +} + void LinkerScript::adjustSectionsBeforeSorting() { // If the output section contains only symbol assignments, create a // corresponding output section. The issue is what to do with linker script @@ -1040,6 +1047,7 @@ // the previous sections. Only a few flags are needed to keep the impact low. uint64_t flags = SHF_ALLOC; + std::vector defPhdrs; for (BaseCommand *&cmd : sectionCommands) { auto *sec = dyn_cast(cmd); if (!sec) @@ -1062,6 +1070,18 @@ sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) | SHF_WRITE | SHF_EXECINSTR); + // The code below may remove empty output sections. We should save the + // specified program headers (if exist) and propagate them to subsequent + // sections which do not specify program headers. + // An example of such a linker script is: + // SECTIONS { .empty : { *(.empty) } :rw + // .foo : { *(.foo) } } + // Note: at this point the order of output sections has not been finalized, + // because orphans have not been inserted into their expected positions. We + // will handle them in adjustSectionsAfterSorting(). + if (sec->sectionIndex != UINT32_MAX) + maybePropagatePhdrs(*sec, defPhdrs); + if (isEmpty && isDiscardable(*sec)) { sec->markDead(); cmd = nullptr; @@ -1106,20 +1126,9 @@ // Walk the commands and propagate the program headers to commands that don't // explicitly specify them. - for (BaseCommand *base : sectionCommands) { - auto *sec = dyn_cast(base); - if (!sec) - continue; - - if (sec->phdrs.empty()) { - // To match the bfd linker script behaviour, only propagate program - // headers to sections that are allocated. - if (sec->flags & SHF_ALLOC) - sec->phdrs = defPhdrs; - } else { - defPhdrs = sec->phdrs; - } - } + for (BaseCommand *base : sectionCommands) + if (auto *sec = dyn_cast(base)) + maybePropagatePhdrs(*sec, defPhdrs); } static uint64_t computeBase(uint64_t min, bool allocateHeaders) { diff --git a/lld/test/ELF/linkerscript/empty-relaplt-dyntags.test b/lld/test/ELF/linkerscript/empty-relaplt-dyntags.test --- a/lld/test/ELF/linkerscript/empty-relaplt-dyntags.test +++ b/lld/test/ELF/linkerscript/empty-relaplt-dyntags.test @@ -3,31 +3,11 @@ # RUN: ld.lld -shared %t.o -T %s -o %t # RUN: llvm-readobj --dynamic-table --sections %t | FileCheck %s -## In spite of .rela.plt is empty, it might have been preserved because it is -## mentioned in the linker script. However, even in that case, we should not -## produce DT_JMPREL and DT_PLTGOT tags because this can cause a dynamic loader -## to write into slots in .got.plt it considers reserved to support lazy symbol -## resolution. In fact, as .got.plt is also empty, that memory might be -## allocated for something else. +## Check that we remove the empty .rela.plt section even when it +## is explicitly assigned to a program header. +## Check that no related dynamic tags are produced. -# CHECK: Sections [ -# CHECK: Name: .rela.plt -# CHECK-NEXT: Type: SHT_RELA -# CHECK-NEXT: Flags [ -# CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: ] -# CHECK-NEXT: Address: -# CHECK-NEXT: Offset: -# CHECK-NEXT: Size: 0 -# CHECK: Name: .got.plt -# CHECK-NEXT: Type: SHT_PROGBITS -# CHECK-NEXT: Flags [ -# CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: SHF_WRITE -# CHECK-NEXT: ] -# CHECK-NEXT: Address: -# CHECK-NEXT: Offset: -# CHECK-NEXT: Size: 0 +# CHECK-NOT: Name: .rela.plt # CHECK: DynamicSection [ # CHECK-NOT: JMPREL diff --git a/lld/test/ELF/linkerscript/implicit-program-header.test b/lld/test/ELF/linkerscript/implicit-program-header.test --- a/lld/test/ELF/linkerscript/implicit-program-header.test +++ b/lld/test/ELF/linkerscript/implicit-program-header.test @@ -7,16 +7,18 @@ # RUN: llvm-readelf -l %t1 | FileCheck %s # CHECK: Segment Sections... -# CHECK-NEXT: 00 .dynsym .hash .dynstr .bar .foo .text .dynamic -# CHECK-NEXT: 01 .bar .foo +# CHECK-NEXT: 00 .dynsym .hash .dynstr .foo .text .dynamic +# CHECK-NEXT: 01 .foo +# CHECK-NEXT: 02 .foo PHDRS { ph_write PT_LOAD FLAGS(2); ph_exec PT_LOAD FLAGS(1); + ph_note PT_NOTE; } SECTIONS { - .bar : { *(.bar) } : ph_exec + .bar : { *(.bar) } : ph_exec : ph_note .foo : { *(.foo) } .text : { *(.text) } : ph_write } diff --git a/lld/test/ELF/linkerscript/orphan-phdrs.s b/lld/test/ELF/linkerscript/orphan-phdrs.s --- a/lld/test/ELF/linkerscript/orphan-phdrs.s +++ b/lld/test/ELF/linkerscript/orphan-phdrs.s @@ -18,12 +18,11 @@ # CHECK: Section Headers # CHECK: .text # CHECK-NEXT: .orphan -# CHECK-NEXT: .empty # CHECK-NEXT: .rw # CHECK: Segment Sections # CHECK-NEXT: .text .orphan -# CHECK-NEXT: .empty .rw +# CHECK-NEXT: .rw .section .text,"ax" ret