[lld] [ELF] Align the end of PT_GNU_RELRO associated PT_LOAD to a common-page-size boundary (PR #66042)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 13 19:39:54 PDT 2023


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/66042:

>From 198620f6275c2379904b13e70f01421907b8edbb Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 10 Sep 2023 21:07:20 -0700
Subject: [PATCH 1/3] [ELF] Align the end of PT_GNU_RELRO associated PT_LOAD to
 a common-page-size boundary

Close #57618: currently we align the end of PT_GNU_RELRO to a common-page-size
boundary, but do not align the end of the associated PT_LOAD. This is benign
when runtime_page_size >= common-page-size.

However, when runtime_page_size < common-page-size, it is possible that
`alignUp(end(PT_LOAD), page_size) < alignDown(end(PT_GNU_RELRO), page_size)`.
In this case, rtld's mprotect call for PT_GNU_RELRO will apply to unmapped
regions and lead to an error, e.g.

```
error while loading shared libraries: cannot apply additional memory protection after relocation: Cannot allocate memory
```

To fix the issue, add a padding section .relro_padding like mold, which
is contained in the PT_GNU_RELRO segment and the associated PT_LOAD
segment. The section also prevents strip from corrupting PT_LOAD program
headers.

.relro_padding has the largest `sortRank` among RELRO sections.
Therefore, it is naturally placed at the end of `PT_GNU_RELRO` segment
in the absence of `PHDRS`/`SECTIONS` commands.

In the presence of `SECTIONS` commands, we place .relro_padding
immediately before a symbol assignment using DATA_SEGMENT_RELRO_END (see
also https://reviews.llvm.org/D124656), if present.
DATA_SEGMENT_RELRO_END is changed to align to max-page-size instead of common-page-size.

Some edge cases worth mentioning:

* ppc64-toc-addis-nop.s: when PHDRS is present, do not append .relro_padding
* avoid-empty-program-headers.s: when the only RELRO section is .tbss,
  it is not part of PT_LOAD segment, therefore we do not append .relro_padding.

---

Close #65002: GNU ld from 2.39 onwards aligns the end of PT_GNU_RELRO to a
max-page-size boundary (https://sourceware.org/PR28824) so that the last page is
protected even if system-page-size > common-page-size.

In my opinion, losing protection for the last page when the system page size is
larger than common-page-size is not really an issue. Double mapping a page of up
to max-common-page for the protection could cause undesired VM waste. Internally
we had users complaining about 2MiB max-page-size applying to shared objects.

Therefore, the end of .relro_padding is padded to a common-page-size
boundary. Users who are really anxious can set common-page-size to match
their runtime page-size.

---

17 tests need updating as there are lots of change detectors.
---
 lld/ELF/Driver.cpp                            |  4 +--
 lld/ELF/LinkerScript.cpp                      | 14 ++++++++++
 lld/ELF/ScriptParser.cpp                      |  3 +--
 lld/ELF/SyntheticSections.cpp                 |  5 ++++
 lld/ELF/SyntheticSections.h                   | 11 ++++++++
 lld/ELF/Writer.cpp                            | 26 ++++++++++---------
 lld/docs/ReleaseNotes.rst                     |  3 ++-
 lld/test/ELF/arm-execute-only.s               |  4 +--
 lld/test/ELF/end-dso-defined.s                |  6 ++---
 .../ELF/linkerscript/data-segment-relro.test  | 23 ++++++++--------
 lld/test/ELF/linkerscript/insert-before.test  |  2 +-
 lld/test/ELF/map-file-copy.s                  |  2 +-
 lld/test/ELF/map-file.s                       |  6 +++--
 lld/test/ELF/partition-notes.s                |  2 +-
 lld/test/ELF/partition-synthetic-sections.s   |  1 +
 lld/test/ELF/relocation-copy-relro.s          | 19 ++++++++++++++
 lld/test/ELF/relro-bss.s                      |  3 ++-
 lld/test/ELF/relro-copyrel-bss-script.s       |  2 +-
 lld/test/ELF/relro.s                          |  4 +--
 lld/test/ELF/riscv-section-layout.s           |  2 ++
 lld/test/ELF/section-name.s                   |  3 ++-
 lld/test/ELF/separate-segments.s              |  6 ++---
 lld/test/ELF/shuffle-sections-init-fini.s     |  4 +--
 lld/test/ELF/shuffle-sections.s               |  4 +--
 lld/test/ELF/sort-norosegment.s               |  2 +-
 lld/test/ELF/x86-64-section-layout.s          |  3 ++-
 26 files changed, 112 insertions(+), 52 deletions(-)

diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 9219314111610d1..9d167293574fa64 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1586,8 +1586,8 @@ static void readConfigs(opt::InputArgList &args) {
 
   // Page alignment can be disabled by the -n (--nmagic) and -N (--omagic).
   // As PT_GNU_RELRO relies on Paging, do not create it when we have disabled
-  // it.
-  if (config->nmagic || config->omagic)
+  // it. Also disable RELRO for -r.
+  if (config->nmagic || config->omagic || config->relocatable)
     config->zRelro = false;
 
   std::tie(config->buildId, config->buildIdVector) = getBuildId(args);
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 2b9f28ce68d685d..1c74c0c1eda4ff0 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -1079,6 +1079,11 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
     }
   }
 
+  // If .relro_padding is present, round up the end to a common-page-size
+  // boundary to protect the last page.
+  if (in.relroPadding && sec == in.relroPadding->getParent())
+    expandOutputSection(alignToPowerOf2(dot, config->commonPageSize) - dot);
+
   // Non-SHF_ALLOC sections do not affect the addresses of other OutputSections
   // as they are not part of the process image.
   if (!(sec->flags & SHF_ALLOC)) {
@@ -1160,6 +1165,7 @@ void LinkerScript::adjustOutputSections() {
   uint64_t flags = SHF_ALLOC;
 
   SmallVector<StringRef, 0> defPhdrs;
+  bool seenRelro = false;
   for (SectionCommand *&cmd : sectionCommands) {
     if (!isa<OutputDesc>(cmd))
       continue;
@@ -1196,9 +1202,17 @@ void LinkerScript::adjustOutputSections() {
     if (sec->sectionIndex != UINT32_MAX)
       maybePropagatePhdrs(*sec, defPhdrs);
 
+    // Discard .relro_padding if we have not seen one RELRO section. Note: when
+    // .tbss is the only RELRO section, there is no associated PT_LOAD segment
+    // (needsPtLoad), so we don't append .relro_padding in the case.
+    if (in.relroPadding && in.relroPadding->getParent() == sec && !seenRelro)
+      discardable = true;
     if (discardable) {
       sec->markDead();
       cmd = nullptr;
+    } else {
+      seenRelro |=
+          sec->relro && !(sec->type == SHT_NOBITS && (sec->flags & SHF_TLS));
     }
   }
 
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 1f8592fbb95deda..7c5f15011f3953d 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -1461,8 +1461,7 @@ Expr ScriptParser::readPrimary() {
     readExpr();
     expect(")");
     seenRelroEnd = true;
-    Expr e = getPageSize();
-    return [=] { return alignToPowerOf2(script->getDot(), e().getValue()); };
+    return [=] { return alignToPowerOf2(script->getDot(), config->maxPageSize); };
   }
   if (tok == "DEFINED") {
     StringRef name = unquote(readParenLiteral());
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 09090835af4a621..f412efa36480284 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -2688,6 +2688,10 @@ size_t IBTPltSection::getSize() const {
 
 bool IBTPltSection::isNeeded() const { return in.plt->getNumEntries() > 0; }
 
+RelroPaddingSection::RelroPaddingSection()
+    : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, 1, ".relro_padding") {
+}
+
 // The string hash function for .gdb_index.
 static uint32_t computeGdbHash(StringRef s) {
   uint32_t h = 0;
@@ -3839,6 +3843,7 @@ void InStruct::reset() {
   got.reset();
   gotPlt.reset();
   igotPlt.reset();
+  relroPadding.reset();
   armCmseSGSection.reset();
   ppc64LongBranchTarget.reset();
   mipsAbiFlags.reset();
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 21a3f768ffcf8e1..3a9f4ba886f6bbb 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -778,6 +778,16 @@ class IBTPltSection : public SyntheticSection {
   size_t getSize() const override;
 };
 
+// Used to align the end of the PT_GNU_RELRO segment and the associated PT_LOAD
+// segment to a common-page-size boundary. This padding section ensures that all
+// pages in the PT_LOAD segment is covered by at least one section.
+class RelroPaddingSection final : public SyntheticSection {
+public:
+  RelroPaddingSection();
+  size_t getSize() const override { return 0; }
+  void writeTo(uint8_t *buf) override {}
+};
+
 class GdbIndexSection final : public SyntheticSection {
 public:
   struct AddressEntry {
@@ -1333,6 +1343,7 @@ struct InStruct {
   std::unique_ptr<GotSection> got;
   std::unique_ptr<GotPltSection> gotPlt;
   std::unique_ptr<IgotPltSection> igotPlt;
+  std::unique_ptr<RelroPaddingSection> relroPadding;
   std::unique_ptr<SyntheticSection> armCmseSGSection;
   std::unique_ptr<PPC64LongBranchTargetSection> ppc64LongBranchTarget;
   std::unique_ptr<SyntheticSection> mipsAbiFlags;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index d4c667c91ab751a..7b27482cc0175fd 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -458,6 +458,12 @@ template <class ELFT> void elf::createSyntheticSections() {
   add(*in.gotPlt);
   in.igotPlt = std::make_unique<IgotPltSection>();
   add(*in.igotPlt);
+  // In the absence of PHDRS/SECTIONS commands. add .relro_padding.
+  if (config->zRelro && script->phdrsCommands.empty() &&
+      !script->hasSectionsCommand) {
+    in.relroPadding = std::make_unique<RelroPaddingSection>();
+    add(*in.relroPadding);
+  }
 
   if (config->emachine == EM_ARM) {
     in.armCmseSGSection = std::make_unique<ArmCmseSGSection>();
@@ -818,6 +824,9 @@ static bool isRelroSection(const OutputSection *sec) {
   if (sec == in.gotPlt->getParent())
     return config->zNow;
 
+  if (in.relroPadding && sec == in.relroPadding->getParent())
+    return true;
+
   // .dynamic section contains data for the dynamic linker, and
   // there's no need to write to it at runtime, so it's better to put
   // it into RELRO.
@@ -857,7 +866,7 @@ enum RankFlags {
   RF_BSS = 1 << 7,
 };
 
-static unsigned getSectionRank(const OutputSection &osec) {
+static unsigned getSectionRank(OutputSection &osec) {
   unsigned rank = osec.partition * RF_PARTITION;
 
   // We want to put section specified by -T option first, so we
@@ -920,7 +929,9 @@ static unsigned getSectionRank(const OutputSection &osec) {
     // TLS sections directly before the other RELRO sections.
     if (!(osec.flags & SHF_TLS))
       rank |= RF_NOT_TLS;
-    if (!isRelroSection(&osec))
+    if (isRelroSection(&osec))
+      osec.relro = true;
+    else
       rank |= RF_NOT_RELRO;
     // Place .ldata and .lbss after .bss. Making .bss closer to .text alleviates
     // relocation overflow pressure.
@@ -2334,6 +2345,7 @@ SmallVector<PhdrEntry *, 0> Writer<ELFT>::createPhdrs(Partition &part) {
       relroEnd = sec;
     }
   }
+  relRo->p_align = 1;
 
   for (OutputSection *sec : outputSections) {
     if (!needsPtLoad(sec))
@@ -2677,16 +2689,6 @@ template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
       if (!p->hasLMA)
         p->p_paddr = first->getLMA();
     }
-
-    if (p->p_type == PT_GNU_RELRO) {
-      p->p_align = 1;
-      // musl/glibc ld.so rounds the size down, so we need to round up
-      // to protect the last page. This is a no-op on FreeBSD which always
-      // rounds up.
-      p->p_memsz =
-          alignToPowerOf2(p->p_offset + p->p_memsz, config->commonPageSize) -
-          p->p_offset;
-    }
   }
 }
 
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 1590879416853e0..a5e66d98800cc6e 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -29,7 +29,8 @@ ELF Improvements
 * ``--fat-lto-objects`` option is added to support LLVM FatLTO.
   Without ``--fat-lto-objects``, LLD will link LLVM FatLTO objects using the
   relocatable object file. (`D146778 <https://reviews.llvm.org/D146778>`_)
-
+* common-page-size can now be larger than the system page-size.
+ (`#57618 <https://github.com/llvm/llvm-project/issues/57618>`_)
 
 Breaking changes
 ----------------
diff --git a/lld/test/ELF/arm-execute-only.s b/lld/test/ELF/arm-execute-only.s
index 483d33d19fcd4a6..e938be5e64a4b65 100644
--- a/lld/test/ELF/arm-execute-only.s
+++ b/lld/test/ELF/arm-execute-only.s
@@ -13,7 +13,7 @@
 // CHECK:      LOAD           0x000000 0x00000000 0x00000000 0x0016d 0x0016d  R 0x10000
 // CHECK:      LOAD           0x000170 0x00010170 0x00010170 0x{{.*}} 0x{{.*}} R E 0x10000
 // CHECK:      LOAD           0x000174 0x00020174 0x00020174 0x{{.*}} 0x{{.*}}   E 0x10000
-// CHECK:      LOAD           0x000178 0x00030178 0x00030178 0x00038  0x00038  RW  0x10000
+// CHECK:      LOAD           0x000178 0x00030178 0x00030178 0x00038  0x00e88  RW  0x10000
 
 // CHECK: 01     .dynsym .gnu.hash .hash .dynstr
 // CHECK: 02     .text
@@ -22,7 +22,7 @@
 
 // DIFF:      LOAD           0x000000 0x00000000 0x00000000 0x0014d 0x0014d R   0x10000
 // DIFF:      LOAD           0x000150 0x00010150 0x00010150 0x0000c 0x0000c R E 0x10000
-// DIFF:      LOAD           0x00015c 0x0002015c 0x0002015c 0x00038 0x00038 RW  0x10000
+// DIFF:      LOAD           0x00015c 0x0002015c 0x0002015c 0x00038 0x00ea4 RW  0x10000
 
 // DIFF: 01     .dynsym .gnu.hash .hash .dynstr
 // DIFF: 02     .text .foo
diff --git a/lld/test/ELF/end-dso-defined.s b/lld/test/ELF/end-dso-defined.s
index 0b5e977296d1042..d0c143c25148e25 100644
--- a/lld/test/ELF/end-dso-defined.s
+++ b/lld/test/ELF/end-dso-defined.s
@@ -21,16 +21,16 @@
 # CHECK-NEXT: AddressAlignment:
 # CHECK-NEXT: EntrySize:
 # CHECK-NEXT: SectionData (
-# CHECK-NEXT:   0000: 08232000 00000000 08232000 00000000
+# CHECK-NEXT:   0000: 00302000 00000000 00302000 00000000
 # CHECK-NEXT: )
 
 # CHECK:      Symbol {
 # CHECK:        Name: _end
-# CHECK-NEXT:   Value: 0x202308
+# CHECK-NEXT:   Value: 0x203000
 
 # CHECK:      Symbol {
 # CHECK:        Name: end
-# CHECK-NEXT:   Value: 0x202308
+# CHECK-NEXT:   Value: 0x203000
 
 .global _start
 _start:
diff --git a/lld/test/ELF/linkerscript/data-segment-relro.test b/lld/test/ELF/linkerscript/data-segment-relro.test
index 8aba8acb466f6eb..1043c69120d4679 100644
--- a/lld/test/ELF/linkerscript/data-segment-relro.test
+++ b/lld/test/ELF/linkerscript/data-segment-relro.test
@@ -7,10 +7,10 @@
 ## With relro or without DATA_SEGMENT_RELRO_END just aligns to
 ## page boundary.
 
-# RUN: ld.lld --hash-style=sysv -z norelro %t/a.o %t/b.so -T %t/1.t -o %t/a1
+# RUN: ld.lld --hash-style=sysv -z max-page-size=65536 -z norelro %t/a.o %t/b.so -T %t/1.t -o %t/a1
 # RUN: llvm-readelf -S -l %t/a1 | FileCheck %s --check-prefixes=CHECK,CHECK1
 
-# RUN: ld.lld --hash-style=sysv -z relro %t/a.o %t/b.so -T %t/1.t -o %t/a2
+# RUN: ld.lld --hash-style=sysv -z max-page-size=65536 -z relro %t/a.o %t/b.so -T %t/1.t -o %t/a2
 # RUN: llvm-readelf -S -l %t/a2 | FileCheck %s --check-prefixes=CHECK,CHECK2
 
 # CHECK:      Name           Type     Address          Off      Size ES Flg
@@ -26,15 +26,16 @@
 # CHECK1-NOT:  GNU_RELRO
 
 # CHECK2:      Program Headers:
-# CHECK2-NEXT:   Type
-# CHECK2-NEXT:   PHDR
-# CHECK2-NEXT:   LOAD
-# CHECK2-NEXT:   LOAD
-# CHECK2-NEXT:   LOAD
-# CHECK2-NEXT:   LOAD
-# CHECK2-NEXT:   DYNAMIC
-# CHECK2-NEXT:   GNU_RELRO
-# CHECK2-NEXT:   GNU_STACK
+# CHECK2-NEXT:   Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# CHECK2-NEXT:   PHDR           0x000040
+# CHECK2-NEXT:   LOAD           0x000000
+# CHECK2-NEXT:   LOAD           0x0002b0
+# CHECK2-NEXT:   LOAD           0x010000 0x0000000000010000 0x0000000000010000 0x000100 0x000100 RW  0x10000
+# CHECK2-NEXT:   LOAD           0x020000 0x0000000000020000 0x0000000000020000 0x000034 0x000034 RW  0x10000
+# CHECK2-NEXT:   DYNAMIC        0x010000 0x0000000000010000 0x0000000000010000 0x0000f0 0x0000f0 RW  0x8
+# CHECK2-NEXT:   GNU_RELRO      0x010000 0x0000000000010000 0x0000000000010000 0x000100 0x000100 R   0x1
+# CHECK2-NEXT:   GNU_STACK      0x000000
+
 
 # CHECK2:      Section to Segment mapping:
 # CHECK2:        06     .dynamic __libc_atexit .got {{$}}
diff --git a/lld/test/ELF/linkerscript/insert-before.test b/lld/test/ELF/linkerscript/insert-before.test
index 922e0859106ff1e..f9611538c013b2b 100644
--- a/lld/test/ELF/linkerscript/insert-before.test
+++ b/lld/test/ELF/linkerscript/insert-before.test
@@ -29,7 +29,7 @@
 # CHECK2-NEXT:           NULL
 # CHECK2-NEXT: .foo.text PROGBITS 000000000020{{.*}} [[#%x,]] 000008 00  AX
 # CHECK2-NEXT: .text     PROGBITS [[#%x,]]           [[#%x,]] 000008 00  AX
-# CHECK2-NEXT: .byte     PROGBITS [[#%x,]]           [[#%x,]] 000001 00  AX
+# CHECK2-NEXT: .byte     PROGBITS [[#%x,]]           [[#%x,]] 000001 00  WA
 # CHECK2-NEXT: .foo.data PROGBITS [[#%x,]]           [[#%x,]] 000008 00  WA
 # CHECK2-NEXT: .data     PROGBITS [[#%x,]]           [[#%x,]] 000008 00  WA
 # CHECK2:      Type      {{.*}} Flg Align
diff --git a/lld/test/ELF/map-file-copy.s b/lld/test/ELF/map-file-copy.s
index 58c0ce981048093..029724418450889 100644
--- a/lld/test/ELF/map-file-copy.s
+++ b/lld/test/ELF/map-file-copy.s
@@ -19,7 +19,7 @@
 # CHECK-NEXT:         <internal>:(.bss.rel.ro)
 ## Ideally this is displayed as copy at v2.
 # CHECK-NEXT:                 copy{{$}}
-# CHECK-NEXT: .got.plt
+# CHECK-NEXT: .relro_padding
 
 #--- 1.s
 .global _start
diff --git a/lld/test/ELF/map-file.s b/lld/test/ELF/map-file.s
index 59931409c7abdb3..0cb20d5a4164bcb 100644
--- a/lld/test/ELF/map-file.s
+++ b/lld/test/ELF/map-file.s
@@ -87,6 +87,8 @@ labs = 0x1AB5
 # CHECK-NEXT:          201420           201420        0     1                 sharedFunc2
 # CHECK-NEXT:          202430           202430      100     8 .dynamic
 # CHECK-NEXT:          202430           202430      100     8         <internal>:(.dynamic)
+# CHECK-NEXT:          202530           202530      ad0     1 .relro_padding
+# CHECK-NEXT:          202530           202530        0     1         <internal>:(.relro_padding)
 # CHECK-NEXT:          203530           203530       28     8 .got.plt
 # CHECK-NEXT:          203530           203530       28     8         <internal>:(.got.plt)
 # CHECK-NEXT:          203560           203560       10    16 .bss
@@ -100,8 +102,8 @@ labs = 0x1AB5
 # CHECK-NEXT:               0                0        8     1         <internal>:(.comment)
 # CHECK-NEXT:               0                0      1b0     8 .symtab
 # CHECK-NEXT:               0                0      1b0     8         <internal>:(.symtab)
-# CHECK-NEXT:               0                0       84     1 .shstrtab
-# CHECK-NEXT:               0                0       84     1         <internal>:(.shstrtab)
+# CHECK-NEXT:               0                0       93     1 .shstrtab
+# CHECK-NEXT:               0                0       93     1         <internal>:(.shstrtab)
 # CHECK-NEXT:               0                0       71     1 .strtab
 # CHECK-NEXT:               0                0       71     1         <internal>:(.strtab)
 
diff --git a/lld/test/ELF/partition-notes.s b/lld/test/ELF/partition-notes.s
index 9bc43f2fbf9ee4c..c5ade3a47e05282 100644
--- a/lld/test/ELF/partition-notes.s
+++ b/lld/test/ELF/partition-notes.s
@@ -37,7 +37,7 @@
 // CHECK-NEXT:       Owner: GNU
 // CHECK-NEXT:       Data size:
 // CHECK-NEXT:       Type: NT_GNU_BUILD_ID (unique build ID bitstring)
-// CHECK-NEXT:       Build ID: ab81108a3d85b729980356331fddc2bfc4c10177{{$}}
+// CHECK-NEXT:       Build ID: d5101cb9d03b7e836ba9b64f5768a0b31980920f{{$}}
 // CHECK-NEXT:     }
 // CHECK-NEXT:   }
 // CHECK-NEXT: ]
diff --git a/lld/test/ELF/partition-synthetic-sections.s b/lld/test/ELF/partition-synthetic-sections.s
index 2eec08392fe68d3..d38597856e165ef 100644
--- a/lld/test/ELF/partition-synthetic-sections.s
+++ b/lld/test/ELF/partition-synthetic-sections.s
@@ -41,6 +41,7 @@
 // PART0-NEXT: .plt              PROGBITS
 // PART0-NEXT: .init_array       INIT_ARRAY      {{0*}}[[INIT_ARRAY_ADDR:[^ ]*]]
 // CHECK-NEXT: .dynamic          DYNAMIC         {{0*}}[[DYNAMIC_ADDR:[^ ]*]]
+// PART0-NEXT: .relro_padding    NOBITS
 // PART0-NEXT: .data             PROGBITS        000000000000[[DATA_SEGMENT:.]]178
 // PART1-NEXT: .data             PROGBITS        000000000000[[DATA_SEGMENT:.]]130
 // PART0-NEXT: .got.plt          PROGBITS        {{0*}}[[GOT_PLT_ADDR:[^ ]*]]
diff --git a/lld/test/ELF/relocation-copy-relro.s b/lld/test/ELF/relocation-copy-relro.s
index 91994501a301dbd..58492509a38b2b7 100644
--- a/lld/test/ELF/relocation-copy-relro.s
+++ b/lld/test/ELF/relocation-copy-relro.s
@@ -8,6 +8,11 @@
 // RUN: ld.lld %t.o %t.so -o %t3
 // RUN: llvm-readobj -S -l -r %t3 | FileCheck %s
 
+/// Due to -z rodynamic, The only RELRO section is .bss.rel.ro. Test that we
+/// still append the .relro_padding section.
+// RUN: ld.lld -z rodynamic %t.o %t.so -o %t4
+// RUN: llvm-readelf -S -l %t4 | FileCheck %s --check-prefix=CHECK2
+
 // CHECK:        Name: .bss.rel.ro
 // CHECK-NEXT:   Type: SHT_NOBITS (0x8)
 // CHECK-NEXT:   Flags [ (0x3)
@@ -28,6 +33,20 @@
 // CHECK: 0x202368 R_X86_64_COPY a 0x0
 // CHECK: 0x20236C R_X86_64_COPY b 0x0
 
+// CHECK2:      LOAD           0x000356 0x0000000000202356 0x0000000000202356 0x000000 0x000caa RW  0x1000
+// CHECK2:      DYNAMIC        0x000258 0x0000000000200258 0x0000000000200258 0x0000b0 0x0000b0 R   0x8
+// CHECK2:      GNU_RELRO      0x000356 0x0000000000202356 0x0000000000202356 0x000000 0x000caa R   0x1
+
+// CHECK2:      Section to Segment mapping:
+// CHECK2-NEXT:  Segment Sections...
+// CHECK2-NEXT:   00
+// CHECK2-NEXT:   01     .dynsym .gnu.hash .hash .dynamic .dynstr .rela.dyn
+// CHECK2-NEXT:   02     .text
+// CHECK2-NEXT:   03     .bss.rel.ro .relro_padding
+// CHECK2-NEXT:   04     .dynamic
+// CHECK2-NEXT:   05     .bss.rel.ro .relro_padding
+// CHECK2-NEXT:   06
+
 .text
 .global _start
 _start:
diff --git a/lld/test/ELF/relro-bss.s b/lld/test/ELF/relro-bss.s
index 8fc8167bb51b7dc..f15aff36872df5c 100644
--- a/lld/test/ELF/relro-bss.s
+++ b/lld/test/ELF/relro-bss.s
@@ -10,7 +10,7 @@
 
 #           Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
 # SEG:      LOAD           0x0001c8 0x00000000002011c8 0x00000000002011c8 0x000001 0x000001 R E 0x1000
-# SEG-NEXT: LOAD           0x0001c9 0x00000000002021c9 0x00000000002021c9 0x000001 0x002001 RW  0x1000
+# SEG-NEXT: LOAD           0x0001c9 0x00000000002021c9 0x00000000002021c9 0x000001 0x002e37 RW  0x1000
 # SEG-NEXT: LOAD           0x0001ca 0x00000000002051ca 0x00000000002051ca 0x000001 0x000002 RW  0x1000
 # SEG-NEXT: GNU_RELRO      0x0001c9 0x00000000002021c9 0x00000000002021c9 0x000001 0x002e37 R   0x1
 # SEG-NEXT: GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x0
@@ -24,6 +24,7 @@
 #        [Nr] Name              Type            Address          Off    Size
 # CHECK:      .data.rel.ro      PROGBITS        00000000002021c9 0001c9 000001
 # CHECK-NEXT: .bss.rel.ro       NOBITS          00000000002021ca 0001ca 002000
+# CHECK-NEXT: .relro_padding    NOBITS          00000000002041ca 0001ca 000e36
 # CHECK-NEXT: .data             PROGBITS        00000000002051ca 0001ca 000001
 # CHECK-NEXT: .bss              NOBITS          00000000002051cb 0001cb 000001
 
diff --git a/lld/test/ELF/relro-copyrel-bss-script.s b/lld/test/ELF/relro-copyrel-bss-script.s
index 9a947d898a12a1d..4745caae8679039 100644
--- a/lld/test/ELF/relro-copyrel-bss-script.s
+++ b/lld/test/ELF/relro-copyrel-bss-script.s
@@ -28,4 +28,4 @@ _start:
         .space 0x2000
 
 // CHECK: Type      Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
-// CHECK: GNU_RELRO 0x002150 0x0000000000000150 0x0000000000000150 0x000100 0x000eb0 R   0x1
+// CHECK: GNU_RELRO 0x002150 0x0000000000000150 0x0000000000000150 0x000100 0x000100 R   0x1
diff --git a/lld/test/ELF/relro.s b/lld/test/ELF/relro.s
index 163e257e2956e0a..0ab9665443d1fb3 100644
--- a/lld/test/ELF/relro.s
+++ b/lld/test/ELF/relro.s
@@ -24,8 +24,8 @@
 // CHECK-NEXT: GNU_RELRO
 // CHECK: Section to Segment mapping:
 
-// FULLRELRO:  03     .openbsd.randomdata .dynamic .got .got.plt {{$}}
-// PARTRELRO:  03     .openbsd.randomdata .dynamic .got {{$}}
+// FULLRELRO:  03     .openbsd.randomdata .dynamic .got .got.plt .relro_padding {{$}}
+// PARTRELRO:  03     .openbsd.randomdata .dynamic .got .relro_padding {{$}}
 
 
 // NORELRO-NOT: GNU_RELRO
diff --git a/lld/test/ELF/riscv-section-layout.s b/lld/test/ELF/riscv-section-layout.s
index bb9adf5eef0545b..56ac95d01fdfd61 100644
--- a/lld/test/ELF/riscv-section-layout.s
+++ b/lld/test/ELF/riscv-section-layout.s
@@ -20,6 +20,7 @@
 # NOSDATA-NEXT: .tbss
 # NOSDATA-NEXT: .dynamic
 # NOSDATA-NEXT: .got
+# NOSDATA-NEXT: .relro_padding
 # NOSDATA-NEXT: .data    PROGBITS [[#%x,DATA:]]
 # NOSDATA-NEXT: .bss     NOBITS   [[#%x,BSS:]]
 
@@ -36,6 +37,7 @@
 # CHECK-NEXT: .tbss
 # CHECK-NEXT: .dynamic
 # CHECK-NEXT: .got
+# CHECK-NEXT: .relro_padding
 # CHECK-NEXT: .data
 # CHECK-NEXT: .sdata     PROGBITS [[#%x,SDATA:]]
 # CHECK-NEXT: .sbss      NOBITS   [[#%x,SBSS:]]
diff --git a/lld/test/ELF/section-name.s b/lld/test/ELF/section-name.s
index fff3fc14f30246f..819cd9c14d95001 100644
--- a/lld/test/ELF/section-name.s
+++ b/lld/test/ELF/section-name.s
@@ -48,11 +48,12 @@ _start:
 // CHECK-NEXT: .tdata            00000001
 // CHECK-NEXT: .tbss             00000001
 // CHECK-NEXT: .data.rel.ro      00000004
+// CHECK-NEXT: .relro_padding    00000df5
 // CHECK-NEXT: .data             00000002
 // CHECK-NEXT: .foo.a            00000001
 // CHECK-NEXT: .foo              00000001
 // CHECK-NEXT: .bss              00000002
 // CHECK-NEXT: .comment          00000008
 // CHECK-NEXT: .symtab           00000030
-// CHECK-NEXT: .shstrtab         00000075
+// CHECK-NEXT: .shstrtab         00000084
 // CHECK-NEXT: .strtab           00000008
diff --git a/lld/test/ELF/separate-segments.s b/lld/test/ELF/separate-segments.s
index d0e4afe7fb66858..20501e92b29c97e 100644
--- a/lld/test/ELF/separate-segments.s
+++ b/lld/test/ELF/separate-segments.s
@@ -7,7 +7,7 @@
 # RUN: llvm-readelf -l %t | FileCheck --check-prefix=NONE %s
 # NONE:      LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R   0x1000
 # NONE-NEXT: LOAD 0x000248 0x0000000000001248 0x0000000000001248 0x000001 0x000001 R E 0x1000
-# NONE-NEXT: LOAD 0x000250 0x0000000000002250 0x0000000000002250 0x000090 0x000090 RW  0x1000
+# NONE-NEXT: LOAD 0x000250 0x0000000000002250 0x0000000000002250 0x000090 0x000db0 RW  0x1000
 # NONE-NEXT: LOAD 0x0002e0 0x00000000000032e0 0x00000000000032e0 0x000001 0x000001 RW  0x1000
 
 ## -z separate-code makes text segment (RX) separate.
@@ -16,7 +16,7 @@
 # RUN: llvm-readelf -l %t | FileCheck --check-prefix=CODE %s
 # CODE:      LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R   0x1000
 # CODE-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
-# CODE-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000090 0x000090 RW  0x1000
+# CODE-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000090 0x001000 RW  0x1000
 # CODE-NEXT: LOAD 0x002090 0x0000000000003090 0x0000000000003090 0x000001 0x000001 RW  0x1000
 
 ## -z separate-loadable-segments makes all segments separate.
@@ -24,7 +24,7 @@
 # RUN: llvm-readelf -l %t | FileCheck --check-prefix=ALL %s
 # ALL:       LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000245 0x000245 R   0x1000
 # ALL-NEXT:  LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000001 0x000001 R E 0x1000
-# ALL-NEXT:  LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000090 0x000090 RW  0x1000
+# ALL-NEXT:  LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000090 0x001000 RW  0x1000
 # ALL-NEXT:  LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 RW  0x1000
 
 nop
diff --git a/lld/test/ELF/shuffle-sections-init-fini.s b/lld/test/ELF/shuffle-sections-init-fini.s
index 66e32be7c874add..c8e56d0860499d5 100644
--- a/lld/test/ELF/shuffle-sections-init-fini.s
+++ b/lld/test/ELF/shuffle-sections-init-fini.s
@@ -1,11 +1,11 @@
 # REQUIRES: x86
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 
-# RUN: ld.lld %t.o -o %t
+# RUN: ld.lld -z norelro %t.o -o %t
 # RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t | \
 # RUN:   FileCheck --check-prefixes=CHECK,ORDERED %s
 
-# RUN: ld.lld %t.o --shuffle-sections '*=1' -o %t1
+# RUN: ld.lld -z norelro %t.o --shuffle-sections '*=1' -o %t1
 # RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t1 | \
 # RUN:   FileCheck --check-prefixes=CHECK,SHUFFLED %s
 
diff --git a/lld/test/ELF/shuffle-sections.s b/lld/test/ELF/shuffle-sections.s
index 21c79eab950d671..56ed2c9ca9bdb09 100644
--- a/lld/test/ELF/shuffle-sections.s
+++ b/lld/test/ELF/shuffle-sections.s
@@ -10,7 +10,7 @@
 # RUN: ld.lld --shuffle-sections='*=1' %t.o -o %t1.out
 # RUN: llvm-readelf -x .text %t1.out | FileCheck %s --check-prefix=SHUFFLE1
 # SHUFFLE1: Hex dump of section '.text':
-# SHUFFLE1-NEXT: 01020403
+# SHUFFLE1-NEXT: 030402cc 01
 
 ## Test that --shuffle-sections= can be used with --symbol-ordering-file
 # RUN: echo "foo" > %t_order.txt
@@ -21,7 +21,7 @@
 # SHUFFLE2: Hex dump of section '.text':
 # SHUFFLE2-NEXT: 02cccccc 010304
 
-# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections='*=3' %t.o -o %t3.out
+# RUN: ld.lld -z norelro --symbol-ordering-file %t_order.txt --shuffle-sections='*=3' %t.o -o %t3.out
 # RUN: llvm-readelf -x .text %t3.out | FileCheck %s --check-prefix=SHUFFLE3
 # SHUFFLE3: Hex dump of section '.text':
 # SHUFFLE3-NEXT: 02cccccc 010403
diff --git a/lld/test/ELF/sort-norosegment.s b/lld/test/ELF/sort-norosegment.s
index 9bf38eaac236bc2..9a1836820ef1fa7 100644
--- a/lld/test/ELF/sort-norosegment.s
+++ b/lld/test/ELF/sort-norosegment.s
@@ -9,7 +9,7 @@
 # CHECK-NEXT: .dynstr  {{.*}}   A
 # CHECK-NEXT: .text    {{.*}}   AX
 # CHECK-NEXT: .dynamic {{.*}}  WA
-# CHECK-NEXT: foo      {{.*}}  WA
+# CHECK:      foo      {{.*}}  WA
 
 .section foo, "aw"
 .byte 0
diff --git a/lld/test/ELF/x86-64-section-layout.s b/lld/test/ELF/x86-64-section-layout.s
index e9ea69afab3b106..37201279fa0a5d0 100644
--- a/lld/test/ELF/x86-64-section-layout.s
+++ b/lld/test/ELF/x86-64-section-layout.s
@@ -23,6 +23,7 @@
 # CHECK-NEXT:  .text      PROGBITS        0000000000201304 000304 000001 00  AX  0   0  4
 # CHECK-NEXT:  .tdata     PROGBITS        0000000000202305 000305 000001 00 WAT  0   0  1
 # CHECK-NEXT:  .tbss      NOBITS          0000000000202306 000306 000002 00 WAT  0   0  1
+# CHECK-NEXT:  .relro_padding NOBITS      0000000000202306 000306 000cfa 00  WA  0   0  1
 # CHECK-NEXT:  .data      PROGBITS        0000000000203306 000306 000001 00  WA  0   0  1
 # CHECK-NEXT:  .bss       NOBITS          0000000000203307 000307 001800 00  WA  0   0  1
 ## We spend size(.bss) % MAXPAGESIZE bytes for .bss.
@@ -36,7 +37,7 @@
 # CHECK-NEXT:    PHDR  0x000040 0x0000000000200040 0x0000000000200040 {{.*}}   {{.*}}   R   0x8
 # CHECK-NEXT:    LOAD  0x000000 0x0000000000200000 0x0000000000200000 0x000304 0x000304 R   0x1000
 # CHECK-NEXT:    LOAD  0x000304 0x0000000000201304 0x0000000000201304 0x000001 0x000001 R E 0x1000
-# CHECK-NEXT:    LOAD  0x000305 0x0000000000202305 0x0000000000202305 0x000001 0x000001 RW  0x1000
+# CHECK-NEXT:    LOAD  0x000305 0x0000000000202305 0x0000000000202305 0x000001 0x000cfb RW  0x1000
 # CHECK-NEXT:    LOAD  0x000306 0x0000000000203306 0x0000000000203306 0x000001 0x001801 RW  0x1000
 # CHECK-NEXT:    LOAD  0x000b07 0x0000000000205b07 0x0000000000205b07 0x000003 0x000005 RW  0x1000
 

>From 8790f8f34841041828202a2dc7bec66119da23d4 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 13 Sep 2023 15:33:57 -0700
Subject: [PATCH 2/3] Append .relro_padding also for DATA_SEGMENT_RELRO_END

---
 lld/ELF/LinkerScript.cpp                      |  4 +
 lld/ELF/LinkerScript.h                        |  5 ++
 lld/ELF/ScriptParser.cpp                      | 13 ++-
 lld/ELF/Writer.cpp                            | 16 +++-
 lld/docs/ELF/linker_script.rst                | 15 ++++
 .../data-segment-relro-ppc64.test             | 79 +++++++++++++++++++
 .../ELF/linkerscript/data-segment-relro.test  | 14 ++--
 lld/test/ELF/ppc64-section-layout.s           |  2 +
 8 files changed, 134 insertions(+), 14 deletions(-)
 create mode 100644 lld/test/ELF/linkerscript/data-segment-relro-ppc64.test

diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 1c74c0c1eda4ff0..77d13e54de125ca 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -887,6 +887,10 @@ void LinkerScript::diagnoseOrphanHandling() const {
   if (config->orphanHandling == OrphanHandlingPolicy::Place)
     return;
   for (const InputSectionBase *sec : orphanSections) {
+    // .relro_padding is inserted before DATA_SEGMENT_RELRO_END, if present,
+    // automatically. The section is not supposed to be specified by scripts.
+    if (sec == in.relroPadding.get())
+      continue;
     // Input SHT_REL[A] retained by --emit-relocs are ignored by
     // computeInputSections(). Don't warn/error.
     if (isa<InputSection>(sec) &&
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index f0a914e65993e2c..89780bb60f48244 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -105,6 +105,9 @@ struct SymbolAssignment : SectionCommand {
   bool provide = false;
   bool hidden = false;
 
+  // This assignment references DATA_SEGMENT_RELRO_END.
+  bool dataSegmentRelroEnd = false;
+
   unsigned symOrder;
 
   // Holds file name and line number for error reporting.
@@ -352,6 +355,8 @@ class LinkerScript final {
   SmallVector<PhdrsCommand, 0> phdrsCommands;
 
   bool hasSectionsCommand = false;
+  bool seenDataAlign = false;
+  bool seenRelroEnd = false;
   bool errorOnMissingSection = false;
 
   // List of section patterns specified with KEEP commands. They will
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 7c5f15011f3953d..3fbb49d440c0227 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -136,9 +136,6 @@ class ScriptParser final : ScriptLexer {
   // True if a script being read is in the --sysroot directory.
   bool isUnderSysroot = false;
 
-  bool seenDataAlign = false;
-  bool seenRelroEnd = false;
-
   // A set to detect an INCLUDE() cycle.
   StringSet<> seen;
 };
@@ -600,7 +597,7 @@ void ScriptParser::readSections() {
 
   // If DATA_SEGMENT_RELRO_END is absent, for sections after DATA_SEGMENT_ALIGN,
   // the relro fields should be cleared.
-  if (!seenRelroEnd)
+  if (!script->seenRelroEnd)
     for (SectionCommand *cmd : v)
       if (auto *osd = dyn_cast<OutputDesc>(cmd))
         osd->osec.relro = false;
@@ -916,7 +913,7 @@ OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) {
       script->createOutputSection(unquote(outSec), getCurrentLocation());
   OutputSection *osec = &cmd->osec;
   // Maybe relro. Will reset to false if DATA_SEGMENT_RELRO_END is absent.
-  osec->relro = seenDataAlign && !seenRelroEnd;
+  osec->relro = script->seenDataAlign && !script->seenRelroEnd;
 
   size_t symbolsReferenced = script->referencedSymbols.size();
 
@@ -1051,6 +1048,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
 
   size_t oldPos = pos;
   SymbolAssignment *cmd = nullptr;
+  bool savedSeenRelroEnd = script->seenRelroEnd;
   const StringRef op = peek();
   if (op.starts_with("=")) {
     // Support = followed by an expression without whitespace.
@@ -1071,6 +1069,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
   }
 
   if (cmd) {
+    cmd->dataSegmentRelroEnd = !savedSeenRelroEnd && script->seenRelroEnd;
     cmd->commandString =
         tok.str() + " " +
         llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " ");
@@ -1439,7 +1438,7 @@ Expr ScriptParser::readPrimary() {
     expect(",");
     readExpr();
     expect(")");
-    seenDataAlign = true;
+    script->seenDataAlign = true;
     return [=] {
       uint64_t align = std::max(uint64_t(1), e().getValue());
       return (script->getDot() + align - 1) & -align;
@@ -1460,7 +1459,7 @@ Expr ScriptParser::readPrimary() {
     expect(",");
     readExpr();
     expect(")");
-    seenRelroEnd = true;
+    script->seenRelroEnd = true;
     return [=] { return alignToPowerOf2(script->getDot(), config->maxPageSize); };
   }
   if (tok == "DEFINED") {
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 7b27482cc0175fd..5692a9ac59e0aee 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -459,8 +459,8 @@ template <class ELFT> void elf::createSyntheticSections() {
   in.igotPlt = std::make_unique<IgotPltSection>();
   add(*in.igotPlt);
   // In the absence of PHDRS/SECTIONS commands. add .relro_padding.
-  if (config->zRelro && script->phdrsCommands.empty() &&
-      !script->hasSectionsCommand) {
+  if (config->zRelro && ((script->phdrsCommands.empty() &&
+        !script->hasSectionsCommand) || script->seenRelroEnd)) {
     in.relroPadding = std::make_unique<RelroPaddingSection>();
     add(*in.relroPadding);
   }
@@ -1151,6 +1151,18 @@ findOrphanPos(SmallVectorImpl<SectionCommand *>::iterator b,
               SmallVectorImpl<SectionCommand *>::iterator e) {
   OutputSection *sec = &cast<OutputDesc>(*e)->osec;
 
+  // As a special case, place .relro_padding before the SymbolAssignment using
+  // DATA_SEGMENT_RELRO_END, if present.
+  if (in.relroPadding && sec == in.relroPadding->getParent()) {
+    auto i = std::find_if(b, e, [=](SectionCommand *a) {
+      if (auto *assign = dyn_cast<SymbolAssignment>(a))
+        return assign->dataSegmentRelroEnd;
+      return false;
+    });
+    if (i != e)
+      return i;
+  }
+
   // Find the first element that has as close a rank as possible.
   auto i = std::max_element(b, e, [=](SectionCommand *a, SectionCommand *b) {
     return getRankProximity(sec, a) < getRankProximity(sec, b);
diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst
index bc2037595e5f5a6..fbd96abcb44c5b1 100644
--- a/lld/docs/ELF/linker_script.rst
+++ b/lld/docs/ELF/linker_script.rst
@@ -172,3 +172,18 @@ description in the ``OVERWRITE_SECTIONS`` command while the insert command
 still applies (possibly after orphan section placement). It is recommended to
 leave the brace empty (i.e. ``section : {}``) for the insert command, because
 its description will be ignored anyway.
+
+Built-in functions
+~~~~~~~~~~~~~~~~~~
+
+``DATA_SEGMENT_RELRO_END(offset, exp)`` defines the end of the ``PT_GNU_RELRO``
+segment when ``-z relro`` (default) is in effect. Sections between
+``DATA_SEGMENT_ALIGN`` and ``DATA_SEGMENT_RELRO_END`` are considered RELRO.
+
+The typical use case is ``. = DATA_SEGMENT_RELRO_END(0, .);`` followed by
+writable but non-RELRO sections. LLD ignores ``offset`` and ``exp`` and aligns
+the current location to a max-page-size boundary, ensuring that the next
+``PT_LOAD`` segment will not overlap with the ``PT_GNU_RELRO`` segment.
+
+LLD will insert ``.relro_padding`` immediately before the symbol assignment
+using ``DATA_SEGMENT_RELRO_END``.
diff --git a/lld/test/ELF/linkerscript/data-segment-relro-ppc64.test b/lld/test/ELF/linkerscript/data-segment-relro-ppc64.test
new file mode 100644
index 000000000000000..502cdd382a55fa1
--- /dev/null
+++ b/lld/test/ELF/linkerscript/data-segment-relro-ppc64.test
@@ -0,0 +1,79 @@
+# REQUIRES: x86
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le %t/a.s -o %t/a.o
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le %p/Inputs/shared.s -o %t/b.o
+# RUN: ld.lld -shared -soname=b %t/b.o -o %t/b.so
+
+# RUN: ld.lld -z max-page-size=65536 -z norelro %t/a.o %t/b.so -T %t/1.t -o %t/a1
+# RN: llvm-readelf -S -l %t/a1 | FileCheck %s --check-prefixes=CHECK1
+
+# RUN: ld.lld -z max-page-size=65536 -z relro %t/a.o %t/b.so -T %t/1.t -o %t/a2
+# RUN: llvm-readelf -S -l %t/a2 | FileCheck %s --check-prefixes=CHECK2
+
+## -z norelro suppresses the .relro_padding section.
+# CHECK1:      Name           Type     Address          Off      Size ES Flg
+# CHECK1:      .foo           PROGBITS
+# CHECK1-NEXT: .orphan.rw     PROGBITS
+# CHECK1-NEXT: .branch_lt     PROGBITS
+# CHECK1-NEXT: .got           PROGBITS
+# CHECK1-NEXT: .data          PROGBITS
+# CHECK1-NEXT: .bss           NOBITS
+
+# CHECK2:      Name           Type     Address          Off      Size ES Flg
+# CHECK2-NEXT:                NULL     {{.*}}
+# CHECK2:      .orphan.ro     PROGBITS {{.*}}                              A
+# CHECK2:      .dynamic       DYNAMIC  {{.*}}                             WA
+# CHECK2-NEXT: .branch_lt     PROGBITS {{.*}}                             WA
+# CHECK2-NEXT: .got           PROGBITS {{.*}}                             WA
+# CHECK2-NEXT: .relro_padding NOBITS   00000000000100f0 0100f0 000f10 00  WA
+# CHECK2-NEXT: .data          PROGBITS {{.*}}                             WA
+# CHECK2-NEXT: .foo           PROGBITS {{.*}}                             WA
+# CHECK2-NEXT: .orphan.rw     PROGBITS {{.*}}                             WA
+# CHECK2-NEXT: .bss           NOBITS   {{.*}}                             WA
+
+#--- a.s
+.global _start
+_start:
+  addis 3, 2, bar2 at toc@ha
+  ld 12, bar2 at toc@l(3)
+  mtctr 12
+  bctrl
+  b bar
+  nop
+
+.section .data,"aw"
+.quad 0
+
+.zero 4
+.section .foo,"aw"
+.section .bss,"", at nobits
+
+.section .orphan.ro,"a", at progbits
+.dc.a 0
+
+.section .orphan.rw,"aw", at progbits
+.dc.a .orphan.rw
+
+#--- 1.t
+SECTIONS {
+  . = SIZEOF_HEADERS;
+
+  .plt  : { *(.plt) }
+  .text : { *(.text) }
+
+  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+
+  .dynamic : { *(.dynamic) }
+  .branch_lt : { *(.branch_lt) }
+  .got : { *(.got) }
+
+  . = DATA_SEGMENT_RELRO_END (0, .);
+
+  .plt : { *(.plt) }
+  .data : { *(.data) }
+  .bss : { *(.bss) }
+
+  . = DATA_SEGMENT_END (.);
+
+  .comment 0 : { *(.comment) }
+}
diff --git a/lld/test/ELF/linkerscript/data-segment-relro.test b/lld/test/ELF/linkerscript/data-segment-relro.test
index 1043c69120d4679..79bc9156e803ff8 100644
--- a/lld/test/ELF/linkerscript/data-segment-relro.test
+++ b/lld/test/ELF/linkerscript/data-segment-relro.test
@@ -10,15 +10,20 @@
 # RUN: ld.lld --hash-style=sysv -z max-page-size=65536 -z norelro %t/a.o %t/b.so -T %t/1.t -o %t/a1
 # RUN: llvm-readelf -S -l %t/a1 | FileCheck %s --check-prefixes=CHECK,CHECK1
 
-# RUN: ld.lld --hash-style=sysv -z max-page-size=65536 -z relro %t/a.o %t/b.so -T %t/1.t -o %t/a2
+# RUN: ld.lld --hash-style=sysv -z max-page-size=65536 -z relro --orphan-handling=warn \
+# RUN:   %t/a.o %t/b.so -T %t/1.t -o %t/a2 2>&1 | FileCheck %s --check-prefix=WARN
 # RUN: llvm-readelf -S -l %t/a2 | FileCheck %s --check-prefixes=CHECK,CHECK2
 
+# WARN:     warning: <internal>:(.dynsym) is being placed in '.dynsym'
+# WARN-NOT:                     (.relro_padding)
+
 # CHECK:      Name           Type     Address          Off      Size ES Flg
 # CHECK-NEXT:                NULL     {{.*}}
 # CHECK:      .orphan.ro     PROGBITS {{.*}}                              A
 # CHECK:      .dynamic       DYNAMIC  {{.*}}                             WA
 # CHECK-NEXT: __libc_atexit  PROGBITS {{.*}}                             WA
 # CHECK-NEXT: .got           PROGBITS {{.*}}                             WA
+# CHECK2-NEXT:.relro_padding NOBITS   0000000000010100 010100 000f00 00  WA
 # CHECK-NEXT: .got.plt       PROGBITS {{.*}}                             WA
 # CHECK:      .orphan.rw     PROGBITS {{.*}}                             WA
 
@@ -30,15 +35,14 @@
 # CHECK2-NEXT:   PHDR           0x000040
 # CHECK2-NEXT:   LOAD           0x000000
 # CHECK2-NEXT:   LOAD           0x0002b0
-# CHECK2-NEXT:   LOAD           0x010000 0x0000000000010000 0x0000000000010000 0x000100 0x000100 RW  0x10000
+# CHECK2-NEXT:   LOAD           0x010000 0x0000000000010000 0x0000000000010000 0x000100 0x001000 RW  0x10000
 # CHECK2-NEXT:   LOAD           0x020000 0x0000000000020000 0x0000000000020000 0x000034 0x000034 RW  0x10000
 # CHECK2-NEXT:   DYNAMIC        0x010000 0x0000000000010000 0x0000000000010000 0x0000f0 0x0000f0 RW  0x8
-# CHECK2-NEXT:   GNU_RELRO      0x010000 0x0000000000010000 0x0000000000010000 0x000100 0x000100 R   0x1
+# CHECK2-NEXT:   GNU_RELRO      0x010000 0x0000000000010000 0x0000000000010000 0x000100 0x001000 R   0x1
 # CHECK2-NEXT:   GNU_STACK      0x000000
 
-
 # CHECK2:      Section to Segment mapping:
-# CHECK2:        06     .dynamic __libc_atexit .got {{$}}
+# CHECK2:        06     .dynamic __libc_atexit .got .relro_padding {{$}}
 
 # RUN: sed '/DATA_SEGMENT_RELRO_END/d' %t/1.t > %t/2.t
 # RUN: not ld.lld %t/a.o %t/b.so -T %t/2.t -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
diff --git a/lld/test/ELF/ppc64-section-layout.s b/lld/test/ELF/ppc64-section-layout.s
index ae5843e1608a1f4..490f69b82868c10 100644
--- a/lld/test/ELF/ppc64-section-layout.s
+++ b/lld/test/ELF/ppc64-section-layout.s
@@ -13,6 +13,8 @@
 # CHECK-NEXT: .dynamic
 # CHECK-NEXT: .got
 # CHECK-NEXT: .toc
+## The end of .relro_padding is aligned to a common-page-size boundary.
+# CHECK-NEXT: .relro_padding NOBITS 0000000010020400 000400 000c00 00 WA 0 0 1
 # CHECK-NEXT: .data
 # CHECK-NEXT: .branch_lt
 

>From 25ab9511207961f81910f754cdf4d31af7332838 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Wed, 13 Sep 2023 19:39:31 -0700
Subject: [PATCH 3/3] Clarify when .relro_padding is added

---
 lld/ELF/Writer.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 5692a9ac59e0aee..40f7d7981d9d441 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -458,7 +458,8 @@ template <class ELFT> void elf::createSyntheticSections() {
   add(*in.gotPlt);
   in.igotPlt = std::make_unique<IgotPltSection>();
   add(*in.igotPlt);
-  // In the absence of PHDRS/SECTIONS commands. add .relro_padding.
+  // Add .relro_padding if DATA_SEGMENT_RELRO_END is used; otherwise, add the
+  // section in the absence of PHDRS/SECTIONS commands.
   if (config->zRelro && ((script->phdrsCommands.empty() &&
         !script->hasSectionsCommand) || script->seenRelroEnd)) {
     in.relroPadding = std::make_unique<RelroPaddingSection>();



More information about the llvm-commits mailing list