Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -289,7 +289,13 @@ InX::Bss = make(".bss", 0, 1); Add(InX::Bss); - InX::BssRelRo = make(".bss.rel.ro", 0, 1); + // If there is a SECTIONS command and a .data.rel.ro section name use name + // .data.rel.ro.bss so that we match in the .data.rel.ro output section. + // This makes sure our relro is contiguous. + bool IsSectionsDataRelRoOS = + Script->HasSectionsCommand && findSection(".data.rel.ro"); + InX::BssRelRo = make( + IsSectionsDataRelRoOS ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1); Add(InX::BssRelRo); // Add MIPS-specific sections. @@ -718,7 +724,6 @@ if (Sec->Name != ".got") Rank |= RF_MIPS_NOT_GOT; } - return Rank; } @@ -1511,10 +1516,26 @@ // PT_GNU_RELRO includes all sections that should be marked as // read-only by dynamic linker after proccessing relocations. + // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give + // an error message if more than one PT_GNU_RELRO PHDR is required. PhdrEntry *RelRo = make(PT_GNU_RELRO, PF_R); - for (OutputSection *Sec : OutputSections) - if (needsPtLoad(Sec) && isRelroSection(Sec)) - RelRo->add(Sec); + bool InRelroPhdr = false; + bool IsRelroFinished = false; + for (OutputSection *Sec : OutputSections) { + if (!needsPtLoad(Sec)) + continue; + if (isRelroSection(Sec)) { + InRelroPhdr = true; + if (!IsRelroFinished) + RelRo->add(Sec); + else if (Sec->Size) + error("section: " + Sec->Name + " is not contiguous with other relro" + + " sections"); + } else if (InRelroPhdr && Sec->Size) { + InRelroPhdr = false; + IsRelroFinished = true; + } + } if (RelRo->FirstSec) Ret.push_back(RelRo); Index: test/ELF/relro-non-contiguous-zerosize.s =================================================================== --- /dev/null +++ test/ELF/relro-non-contiguous-zerosize.s @@ -0,0 +1,54 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/copy-in-shared.s -o %t2.o +// RUN: ld.lld -shared %t.o %t2.o -o %t.so + +// Check that we ignore zero sized non-relro sections that are covered by the +// range of addresses covered by the PT_GNU_RELRO header. +// Check that we ignore zero sized relro sections that are disjoint from the +// range of addresses covered by the PT_GNU_RELRO header. +// REQUIRES: x86 + +// RUN: echo "SECTIONS { \ +// RUN: .dynamic : { *(.dynamic) } \ +// RUN: .zero_size : { *(.zero_size) } \ +// RUN: .jcr : { *(.jcr) } \ +// RUN: .got.plt : { *(.got.plt) } \ +// RUN: .large : { *(.large) } \ +// RUN: .data.rel.ro : { *(.data.rel.ro.*) ; . = ALIGN(1); } \ +// RUN: } " > %t.script +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t3.o +// RUN: ld.lld %t3.o %t.so -o %t --script=%t.script +// RUN: llvm-readobj -program-headers %t | FileCheck %s + +// CHECK: Type: PT_GNU_RELRO +// CHECK-NEXT: Offset: +// CHECK-NEXT: VirtualAddress: +// CHECK-NEXT: PhysicalAddress: +// CHECK-NEXT: FileSize: +// CHECK-NEXT: MemSize: 4096 + + .section .text, "ax", @progbits + .global _start + .global bar + .global foo +_start: + callq bar + + // page size non-relro section that would alter PT_GNU_RELRO header + // MemSize if counted as part of relro. + .section .large, "aw", @progbits + .space 4 * 1024 + + // non-empty relro section + .section .jcr, "aw", @progbits + .quad 0 + + // empty non-relro section + .section .zero_size, "aw", @progbits + .global sym +sym: + + // empty relro section + .section .data.rel.ro, "aw", @progbits + .global sym2 +sym2: Index: test/ELF/relro-non-contiguous.s =================================================================== --- /dev/null +++ test/ELF/relro-non-contiguous.s @@ -0,0 +1,28 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/copy-in-shared.s -o %t2.o +// RUN: ld.lld -shared %t.o %t2.o -o %t.so + +// Place the .got.plt (non relro) immediately after .dynamic. This is the +// reverse order of the non-linker script case. The linker created .bss.rel.ro +// section will be placed after .got.plt causing the relro to be non-contiguous. +// RUN: echo "SECTIONS { \ +// RUN: .dynamic : { *(.dynamic) } \ +// RUN: .got.plt : { *(.got.plt) } \ +// RUN: } " > %t.script +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t3.o + +// Expect error for non-contiguous relro +// RUN: not ld.lld %t3.o %t.so -z relro -o %t --script=%t.script 2>&1 | FileCheck %s +// No error when we do not request relro. +// RUN: ld.lld %t3.o %t.so -z norelro -o %t --script=%t.script +// REQUIRES: x86 + +// CHECK: error: section: .bss.rel.ro is not contiguous with other relro sections + .section .text, "ax", @progbits + .global _start + .global bar + .global foo +_start: + .quad bar + .quad foo + Index: test/ELF/relro-script.s =================================================================== --- /dev/null +++ test/ELF/relro-script.s @@ -0,0 +1,29 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/copy-in-shared.s -o %t2.o +// RUN: ld.lld -shared %t.o %t2.o -o %t.so + +// ld.bfd and gold use .data.rel.ro rather than .bss.rel.ro. When a linker +// script, such as ld.bfd's internal linker script has a .data.rel.ro +// OutputSection we rename .bss.rel.ro to .data.rel.ro.bss in order to match in +// .data.rel.ro. This keeps the relro sections contiguous. + +// Use the same sections and ordering as the ld.bfd internal linker script. +// RUN: echo "SECTIONS { \ +// RUN: .data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) } \ +// RUN: .dynamic : { *(.dynamic) } \ +// RUN: .got : { *(.got) } \ +// RUN: .got.plt : { *(.got.plt) } \ +// RUN: } " > %t.script +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t3.o +// RUN: ld.lld %t3.o %t.so -o %t --script=%t.script --print-map | FileCheck %s +// REQUIRES: x86 + +// CHECK: .data.rel.ro +// CHECK-NEXT: :(.bss.rel.ro) + .section .text, "ax", @progbits + .global _start + .global bar + .global foo +_start: + .quad bar + .quad foo