[PATCH v2] elf: Support multiple PT_GNU_RELRO segments

Adhemerval Zanella <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
When binaries become extremely large, PC-relative references to a
single GOT can exceed the +/- 2GB limit. To resolve this, we'd like
to generate multiple GOTs, which would require multiple PT_GNU_RELRO
segments.

This change modifies RELRO protection by removing cached fields
(l_relro_addr and l_relro_size) and instead iterating over all program
headers to protect every PT_GNU_RELRO segment discovered. The same
applies to _dl_readonly_area, where a queried range may now span
multiple adjacent segments.

Since mprotect operates on whole pages while the static linker only
aligns the segment end (and possibly only to a link-time page size
smaller than the run-time one), the protected range is computed by the
new _dl_relro_range helper. The end is rounded down so following
writable data never loses write access, and the start is rounded down
only for a segment that starts a PT_LOAD segment, where the leading
page slack is unused (BFD ld does not page-align the start, so
rounding it up could drop the protection entirely).  A RELRO segment
in the middle of a PT_LOAD segment is preceded by live writable data,
so its start is rounded up instead.

The new tests tst-relro-multi and tst-relro-multi-static carry two
placeholder writable PT_NOTE segments, placed by a linker script and
converted to PT_GNU_RELRO by the new --note-to-relro option of
scripts/tst-elf-edit.py.

Co-authored-by: Justin Rivera <[email protected]>
---
 elf/Makefile                 | 32 ++++++++++++++++++++
 elf/dl-load.c                |  5 ----
 elf/dl-readonly-area.c       | 40 +++++++++++++++++--------
 elf/dl-reloc.c               | 35 ++++++++++++----------
 elf/dl-support.c             |  5 ----
 elf/rtld.c                   | 15 ----------
 elf/tst-relro-multi-notes.S  | 58 ++++++++++++++++++++++++++++++++++++
 elf/tst-relro-multi-static.c | 19 ++++++++++++
 elf/tst-relro-multi.c        | 43 ++++++++++++++++++++++++++
 elf/tst-relro-multi.lds      | 24 +++++++++++++++
 elf/tst-relro-symbols.py     | 45 +++++++++++++++-------------
 include/link.h               |  4 ---
 scripts/tst-elf-edit.py      | 46 ++++++++++++++++++++++++++++
 sysdeps/generic/ldsodefs.h   | 36 ++++++++++++++++++++++
 14 files changed, 330 insertions(+), 77 deletions(-)
 create mode 100644 elf/tst-relro-multi-notes.S
 create mode 100644 elf/tst-relro-multi-static.c
 create mode 100644 elf/tst-relro-multi.c
 create mode 100644 elf/tst-relro-multi.lds

diff --git a/elf/Makefile b/elf/Makefile
index 8b063e1bbae..a6c4afc6265 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1530,6 +1530,38 @@ tests-special += $(objpfx)tst-tls-debug-recursive.out
 tests-special += $(objpfx)tst-dl-debug-exclude.out
 endif
 
+test-srcs += tst-relro-multi
+tests-static += tst-relro-multi-static
+extra-test-objs += tst-relro-multi-notes.o
+LDFLAGS-tst-relro-multi = -Wl,-z,now -Wl,-T,$(..)elf/tst-relro-multi.lds
+LDFLAGS-tst-relro-multi-static = -Wl,-T,$(..)elf/tst-relro-multi.lds
+$(objpfx)tst-relro-multi: +nolink-deps += tst-relro-multi.lds
+$(objpfx)tst-relro-multi: $(objpfx)tst-relro-multi-notes.o tst-relro-multi.lds
+$(objpfx)tst-relro-multi-static: +nolink-deps += tst-relro-multi.lds
+$(objpfx)tst-relro-multi-static: $(objpfx)tst-relro-multi-notes.o \
+  tst-relro-multi.lds
+
+$(objpfx)tst-relro-multi-patched $(objpfx)tst-relro-multi-static-patched: \
+  %-patched: % $(..)scripts/tst-elf-edit.py
+	cp $< [email protected]
+	$(PYTHON) $(..)scripts/tst-elf-edit.py --note-to-relro 2 [email protected]
+	mv [email protected] $@
+
+$(objpfx)tst-relro-multi-patched.out: $(objpfx)tst-relro-multi-patched \
+  $(objpfx)ld.so
+	$(run-program-prefix) $< > $@ 2>&1; \
+	$(evaluate-test)
+
+$(objpfx)tst-relro-multi-static-patched.out: \
+  $(objpfx)tst-relro-multi-static-patched
+	$(test-wrapper) $< > $@ 2>&1; \
+	$(evaluate-test)
+
+ifeq ($(run-built-tests),yes)
+tests-special += $(objpfx)tst-relro-multi-patched.out
+tests-special += $(objpfx)tst-relro-multi-static-patched.out
+endif
+
 # The test requires shared _and_ PIE because the executable
 # unit test driver must be able to link with the shared object
 # that is going to eventually go into an installed DSO.
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 95404adae94..e76d149f1f6 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1091,11 +1091,6 @@ _dl_map_object_scan_phdrs (struct dl_pt_load_iterator *it,
 	case PT_GNU_STACK:
 	  *stack_flagsp = pf_to_prot (ph->p_flags);
 	  break;
-
-	case PT_GNU_RELRO:
-	  l->l_relro_addr = ph->p_vaddr;
-	  l->l_relro_size = ph->p_memsz;
-	  break;
 	}
     }
 
diff --git a/elf/dl-readonly-area.c b/elf/dl-readonly-area.c
index 833f4559049..0b7a5c0d319 100644
--- a/elf/dl-readonly-area.c
+++ b/elf/dl-readonly-area.c
@@ -17,23 +17,37 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <ldsodefs.h>
+#include <sys/param.h>
 
-static bool
+static enum dl_readonly_area_error_type
 check_relro (const struct link_map *l, uintptr_t start, uintptr_t end)
 {
-  if (l->l_relro_addr != 0)
-    {
-      uintptr_t relro_start = ALIGN_DOWN (l->l_addr + l->l_relro_addr,
-					  GLRO(dl_pagesize));
-      uintptr_t relro_end = ALIGN_DOWN (l->l_addr + l->l_relro_addr
-					+ l->l_relro_size,
-					GLRO(dl_pagesize));
-      /* RELRO is caved out from a RW segment, so the next range is either
-	 RW or nonexistent.  */
-      return relro_start <= start && end <= relro_end
-	? dl_readonly_area_rdonly : dl_readonly_area_writable;
+  /* The range may span multiple PT_GNU_RELRO segments whose ranges are
+     adjacent, so accumulate the covered bytes.  */
+  size_t size = end - start;
+  for (const ElfW(Phdr) *ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+    if (ph->p_type == PT_GNU_RELRO)
+      {
+	bool at_load_start = false;
+	for (const ElfW(Phdr) *lph = l->l_phdr;
+	     lph < &l->l_phdr[l->l_phnum]; ++lph)
+	  if (lph->p_type == PT_LOAD && lph->p_vaddr == ph->p_vaddr)
+	    {
+	      at_load_start = true;
+	      break;
+	    }
 
-    }
+	struct dl_relro_range relro = _dl_relro_range (l, ph, at_load_start);
+	uintptr_t from = MAX (relro.start, start);
+	uintptr_t to = MIN (relro.end, end);
+	if (from < to)
+	  size -= to - from;
+	if (size == 0)
+	  return dl_readonly_area_rdonly;
+      }
+
+  /* RELRO is caved out from a RW segment, so any range outside of
+     a RELRO segment is either RW or nonexistent.  */
   return dl_readonly_area_writable;
 }
 
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 191d39cbbd9..5154937897e 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -406,23 +406,28 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
 void
 _dl_protect_relro (struct link_map *l)
 {
-  if (l->l_relro_size == 0)
-    return;
+  for (const ElfW(Phdr) *ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+    if (ph->p_type == PT_GNU_RELRO)
+      {
+	bool at_load_start = false;
+	for (const ElfW(Phdr) *lph = l->l_phdr;
+	     lph < &l->l_phdr[l->l_phnum]; ++lph)
+	  if (lph->p_type == PT_LOAD && lph->p_vaddr == ph->p_vaddr)
+	    {
+	      at_load_start = true;
+	      break;
+	    }
 
-  ElfW(Addr) start = ALIGN_DOWN((l->l_addr
-				 + l->l_relro_addr),
-				GLRO(dl_pagesize));
-  ElfW(Addr) end = ALIGN_DOWN((l->l_addr
-			       + l->l_relro_addr
-			       + l->l_relro_size),
-			      GLRO(dl_pagesize));
-  if (start != end
-      && __mprotect ((void *) start, end - start, PROT_READ) < 0)
-    {
-      static const char errstring[] = N_("\
+	struct dl_relro_range range = _dl_relro_range (l, ph, at_load_start);
+	if (range.start < range.end
+	    && __mprotect ((void *) range.start, range.end - range.start,
+			   PROT_READ) < 0)
+	  {
+	    static const char errstring[] = N_("\
 cannot apply additional memory protection after relocation");
-      _dl_signal_error (errno, l->l_name, NULL, errstring);
-    }
+	    _dl_signal_error (errno, l->l_name, NULL, errstring);
+	  }
+      }
 }
 
 void
diff --git a/elf/dl-support.c b/elf/dl-support.c
index b57fd74670b..041b89da797 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -327,11 +327,6 @@ _dl_non_dynamic_init (void)
       case PT_GNU_STACK:
 	_dl_stack_prot_flags = pf_to_prot (ph->p_flags);
 	break;
-
-      case PT_GNU_RELRO:
-	_dl_main_map.l_relro_addr = ph->p_vaddr;
-	_dl_main_map.l_relro_size = ph->p_memsz;
-	break;
       }
 
   _dl_handle_execstack_tunable ();
diff --git a/elf/rtld.c b/elf/rtld.c
index fc053df8586..b9d0047a686 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1198,11 +1198,6 @@ rtld_setup_main_map (struct link_map *main_map)
       case PT_GNU_STACK:
 	GL(dl_stack_prot_flags) = pf_to_prot (ph->p_flags);
 	break;
-
-      case PT_GNU_RELRO:
-	main_map->l_relro_addr = ph->p_vaddr;
-	main_map->l_relro_size = ph->p_memsz;
-	break;
       }
 
   _dl_executable_postprocess (main_map, phdr, phnum);
@@ -1270,16 +1265,6 @@ rtld_setup_phdr (void)
 				   & ~(GLRO(dl_pagesize) - 1));
 	}
   }
-
-  /* PT_GNU_RELRO is usually the last phdr.  */
-  size_t cnt = rtld_ehdr->e_phnum;
-  while (cnt-- > 0)
-    if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
-      {
-	_dl_rtld_map.l_relro_addr = rtld_phdr[cnt].p_vaddr;
-	_dl_rtld_map.l_relro_size = rtld_phdr[cnt].p_memsz;
-	break;
-      }
 }
 
 /* Adjusts the contents of the stack and related globals for the user
diff --git a/elf/tst-relro-multi-notes.S b/elf/tst-relro-multi-notes.S
new file mode 100644
index 00000000000..94cd05f28c5
--- /dev/null
+++ b/elf/tst-relro-multi-notes.S
@@ -0,0 +1,58 @@
+/* Data definitions for the multiple PT_GNU_RELRO test.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Assembly is used because compilers emit SHT_PROGBITS for
+   __attribute__ ((section)) definitions and gas might warn when its
+   name-based heuristic retypes .note.* sections; and  note sections
+   must not have the SHF_WRITE flag for the same reason.  */
+
+	.section .note.a, "a", %note
+	.balign 4
+	.long 2f-1f		/* n_namesz */
+	.long 4f-3f		/* n_descsz */
+	.long 1			/* n_type */
+1:	.asciz "GLIBC-TST-RELRO"
+2:	.balign 4
+3:	.globl relro_a
+	.type relro_a, %object
+relro_a:
+	.dc.a 0xAAAA
+	.size relro_a, .-relro_a
+4:
+
+	.section .gap, "aw", %progbits
+	.balign 8
+	.globl gap_d
+	.type gap_d, %object
+gap_d:
+	.dc.a 0xDDDD
+	.size gap_d, .-gap_d
+
+	.section .note.b, "a", %note
+	.balign 4
+	.long 2f-1f		/* n_namesz */
+	.long 4f-3f		/* n_descsz */
+	.long 1			/* n_type */
+1:	.asciz "GLIBC-TST-RELRO"
+2:	.balign 4
+3:	.globl relro_b
+	.type relro_b, %object
+relro_b:
+	.dc.a 0xBBBB
+	.size relro_b, .-relro_b
+4:
diff --git a/elf/tst-relro-multi-static.c b/elf/tst-relro-multi-static.c
new file mode 100644
index 00000000000..8f795c84bbc
--- /dev/null
+++ b/elf/tst-relro-multi-static.c
@@ -0,0 +1,19 @@
+/* Multiple PT_GNU_RELRO test, static version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-relro-multi.c"
diff --git a/elf/tst-relro-multi.c b/elf/tst-relro-multi.c
new file mode 100644
index 00000000000..be8feb3252a
--- /dev/null
+++ b/elf/tst-relro-multi.c
@@ -0,0 +1,43 @@
+/* Multiple PT_GNU_RELRO test.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/check_mem_access.h>
+
+/* The tst-elf-edit.py --note-to-relro convert relro_a and relro_b
+   to page-padded PT_GNU_RELRO seguments.  */
+extern unsigned long int relro_a;
+extern unsigned long int gap_d;
+extern unsigned long int relro_b;
+
+static int
+do_test (void)
+{
+  /* Both RELRO regions must be readable but not writable.  */
+  TEST_COMPARE (check_mem_access (&relro_a, false), true);
+  TEST_COMPARE (check_mem_access (&relro_a, true), false);
+  TEST_COMPARE (check_mem_access (&relro_b, false), true);
+  TEST_COMPARE (check_mem_access (&relro_b, true), false);
+
+  /* The gap between the two RELRO regions must remain writable.  */
+  TEST_COMPARE (check_mem_access (&gap_d, true), true);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-relro-multi.lds b/elf/tst-relro-multi.lds
new file mode 100644
index 00000000000..eeb6af15d3c
--- /dev/null
+++ b/elf/tst-relro-multi.lds
@@ -0,0 +1,24 @@
+/* Multiple PT_GNU_RELRO test.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+SECTIONS {
+  . = ALIGN(CONSTANT(MAXPAGESIZE));
+  .note.a : { *(.note.a) . = ALIGN(CONSTANT(MAXPAGESIZE)); }
+  .gap    : { *(.gap) .    = ALIGN(CONSTANT(MAXPAGESIZE)); }
+  .note.b : { *(.note.b) . = ALIGN(CONSTANT(MAXPAGESIZE)); }
+}
+INSERT AFTER .data;
diff --git a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py
index ffbe9958fed..28d2bbf80e6 100644
--- a/elf/tst-relro-symbols.py
+++ b/elf/tst-relro-symbols.py
@@ -32,25 +32,30 @@ sys.path.append(os.path.join(
 
 import glibcelf
 
-def find_relro(path: str, img: glibcelf.Image) -> (int, int):
-    """Discover the address range of the PT_GNU_RELRO segment."""
-    for phdr in img.phdrs():
-        if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO:
-            # The computation is not entirely accurate because
-            # _dl_protect_relro in elf/dl-reloc.c rounds both the
-            # start end and downwards using the run-time page size.
-            return phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz
-    sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path))
-    sys.exit(1)
+def find_relro(path: str, img: glibcelf.Image) -> list:
+    """Discover the address ranges of the PT_GNU_RELRO segments."""
+    # The computation is not entirely accurate because
+    # _dl_protect_relro in elf/dl-reloc.c rounds both the
+    # start end and downwards using the run-time page size.
+    regions = [(phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz)
+               for phdr in img.phdrs()
+               if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO]
+    if not regions:
+        sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path))
+        sys.exit(1)
+    return regions
 
-def check_in_relro(kind, relro_begin, relro_end, name, start, size, error):
-    """Check if a section or symbol falls within in the RELRO segment."""
+def check_in_relro(kind, relro_regions, name, start, size, error):
+    """Check if a section or symbol falls within in any RELRO segment."""
     end = start + size - 1
-    if not (relro_begin <= start < end < relro_end):
-        error(
-            '{} {!r} of size {} at 0x{:x} is not in RELRO range [0x{:x}, 0x{:x})'.format(
-                kind, name.decode('UTF-8'), start, size,
-                relro_begin, relro_end))
+    for relro_begin, relro_end in relro_regions:
+        if relro_begin <= start <= end < relro_end:
+            return
+    error(
+        '{} {!r} of size {} at 0x{:x} is not in any RELRO range: {}'.format(
+            kind, name.decode('UTF-8'), size, start,
+            ', '.join('[0x{:x}, 0x{:x})'.format(*region)
+                      for region in relro_regions)))
 
 def get_parser():
     """Return an argument parser for this script."""
@@ -78,7 +83,7 @@ def main(argv):
     symbols_found = set()
 
     # Discover the extent of the RELRO segment.
-    relro_begin, relro_end = find_relro(opts.object, img)
+    relro_regions = find_relro(opts.object, img)
     symbol_table_found = False
 
     errors = False
@@ -109,13 +114,13 @@ def main(argv):
                             sym.st_name.decode('UTF-8')))
                         continue
 
-                    check_in_relro('symbol', relro_begin, relro_end,
+                    check_in_relro('symbol', relro_regions,
                                    sym.st_name, sym.st_value, sym.st_size,
                                    error)
             continue # SHT_SYMTAB
         if shdr.sh_name == b'.data.rel.ro' \
            or shdr.sh_name.startswith(b'.data.rel.ro.'):
-            check_in_relro('section', relro_begin, relro_end,
+            check_in_relro('section', relro_regions,
                            shdr.sh_name, shdr.sh_addr, shdr.sh_size,
                            error)
             continue
diff --git a/include/link.h b/include/link.h
index 8f851d2212d..04274b490ea 100644
--- a/include/link.h
+++ b/include/link.h
@@ -340,10 +340,6 @@ struct link_map
        lock.  See also: CONCURRENCY NOTES in cxa_thread_atexit_impl.c.  */
     size_t l_tls_dtor_count;
 
-    /* Information used to change permission after the relocations are
-       done.  */
-    ElfW(Addr) l_relro_addr;
-    size_t l_relro_size;
 
     unsigned long long int l_serial;
   };
diff --git a/scripts/tst-elf-edit.py b/scripts/tst-elf-edit.py
index 07fa7e90f55..1d036e925cc 100644
--- a/scripts/tst-elf-edit.py
+++ b/scripts/tst-elf-edit.py
@@ -47,7 +47,12 @@ ET_EXEC=2
 ET_DYN=3
 
 PT_LOAD=1
+PT_NOTE=4
 PT_TLS=7
+PT_GNU_RELRO=0x6474e552
+
+# PT_NOTE constant mark used by --note-to-relro.
+NN_GLIBC_TST_RELRO=b'GLIBC-TST-RELRO\x00'
 
 def elf_types_fmts(e_ident):
     endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>'
@@ -156,6 +161,40 @@ def elf_edit_maximize_tls_size(phdr, elfclass):
     else:
         phdr.p_memsz = 1 << 63
 
+def elf_note_name(f, e_ident, phdr):
+    """Return the owner name of the first note in a PT_NOTE segment."""
+    endian, _, _ = elf_types_fmts(e_ident)
+    fmt = '{}III'.format(endian)
+    nhdr_len = struct.calcsize(fmt)
+    if phdr.p_filesz < nhdr_len:
+        return None
+    f.seek(phdr.p_offset)
+    namesz, descsz, n_type = struct.unpack(fmt, f.read(nhdr_len))
+    if namesz == 0 or namesz > phdr.p_filesz - nhdr_len:
+        return None
+    return f.read(namesz)
+
+def elf_edit_note_to_relro(f, e_ident, ehdr, expected):
+    converted = 0
+    for i in range(0, ehdr.e_phnum):
+        phdr = Elf_Phdr(e_ident)
+        f.seek(ehdr.e_phoff + i * phdr.len)
+        phdr.read(f)
+        if phdr.p_type != PT_NOTE:
+            continue
+        if elf_note_name(f, e_ident, phdr) != NN_GLIBC_TST_RELRO:
+            continue
+        phdr.p_type = PT_GNU_RELRO
+        # Match the alignment the linker uses for PT_GNU_RELRO.
+        phdr.p_align = 1
+        f.seek(ehdr.e_phoff + i * phdr.len)
+        phdr.write(f)
+        converted += 1
+
+    if converted != expected:
+        error('{}: converted {} PT_NOTE segment(s), expected {}'.format(
+            f.name, converted, expected))
+
 def elf_edit(f, opts):
     ei_nident_fmt = 'c' * EI_NIDENT
     ei_nident_len = struct.calcsize(ei_nident_fmt)
@@ -184,6 +223,10 @@ def elf_edit(f, opts):
     if ehdr.e_type not in (ET_EXEC, ET_DYN):
        error('{}: not an executable or shared library'.format(f.name))
 
+    if opts.note_to_relro is not None:
+        elf_edit_note_to_relro(f, e_ident, ehdr, opts.note_to_relro)
+        return
+
     phdr = Elf_Phdr(e_ident)
     maximize_tls_size_done = False
     for i in range(0, ehdr.e_phnum):
@@ -210,6 +253,9 @@ def get_parser():
                         help='How to set the LOAD alignment')
     parser.add_argument('--maximize-tls-size', action='store_true',
                         help='Set maximum PT_TLS size')
+    parser.add_argument('--note-to-relro', type=int, metavar='COUNT',
+                        help='Convert COUNT PT_NOTE segments whose first '
+                        'note has the GLIBC-TST-RELRO name to PT_GNU_RELRO')
     parser.add_argument('output',
                         help='ELF file to edit')
     return parser
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 305ca6e0df2..9f17e9d34c5 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -32,6 +32,7 @@
 #include <dlfcn.h>
 #include <fpu_control.h>
 #include <sys/mman.h>
+#include <libc-pointer-arith.h>
 #include <link.h>
 #include <dl-lookupcfg.h>
 #include <dl-sysdep.h>
@@ -1037,6 +1038,41 @@ void _dl_relocate_object_no_relro (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+struct dl_relro_range
+{
+  ElfW(Addr) start;
+  ElfW(Addr) end;
+};
+
+/* Compute the address range to protect for the PT_GNU_RELRO segment
+   PH of map L.  AT_LOAD_START tells whether the segment starts a
+   PT_LOAD segment.  mprotect requires page-aligned boundaries, but
+   the static linker only aligns the segment end, possibly only to a
+   link-time page size smaller than the run-time one.  The end is
+   rounded down so the writable data after the segment never loses
+   write access.  A segment that starts a PT_LOAD segment (the
+   traditional single-RELRO layout) has only unused slack below it in
+   its first page, so its start is rounded down; rounding up instead
+   could drop the protection entirely (BFD ld does not align the
+   start at all).  Any other segment is preceded by live writable
+   data, so its start is rounded up.  The result may be empty with
+   START above END; callers must check START < END.  */
+static inline struct dl_relro_range
+_dl_relro_range (const struct link_map *l, const ElfW(Phdr) *ph,
+		 bool at_load_start)
+{
+  ElfW(Addr) start = l->l_addr + ph->p_vaddr;
+  ElfW(Addr) end = start + ph->p_memsz;
+
+  return (struct dl_relro_range)
+    {
+      .start = at_load_start
+	       ? ALIGN_DOWN (start, GLRO(dl_pagesize))
+	       : ALIGN_UP (start, GLRO(dl_pagesize)),
+      .end = ALIGN_DOWN (end, GLRO(dl_pagesize)),
+    };
+}
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.