Subject: [PATCH] readelf: Update dynamic tag processing and validate section info

"H.J. Lu" <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <CAMe9rOrB-Y5nRfgKAqjEjNZ3rG7Jxq-fnu7QgFUnzY-TpxxgEA@mail.gmail.com>
Count DT_RELR relocations using dynamic tags when section header isn't
used.  Ignore section sh_offset and sh_size if they are larger than file
size.

One side effect is that we now display

readelf: Warning: Ignore the out of range sh_offset value of
2199023261148 for section 17
readelf: Warning: Ignore the out of range sh_size value of 13 for
section 17 with sh_offset value of 2199023261148
...
  [17] .fini             PROGBITS        00000000000015dc 000000
000000 00  AX  0   0  4
...

instead of

...
  [17] .fini             PROGBITS        00000000000015dc 200000015dc
00000d 00  AX  0   0  4
...

and segfault.

binutils/

PR binutils/34326
PR binutils/34339
* readelf.c (update_all_relocations): Skip if nentries == 0.
(validate_section_info): New.
(get_32bit_section_headers): Call validate_section_info.
(get_64bit_section_headers): Likewise.
(process_relocs): Call count_relr_relocations if needed.  Check
invalid invalid DT_RELENT and DT_RELAENT dynamic tags.
(process_got_section_contents): Skip if all_relocations_count
== 0.

ld/
PR binutils/34339
* testsuite/ld-x86-64/binutils.exp: Run PR binutils/34339 tests.
* testsuite/ld-x86-64/got-2.s: New file.
* testsuite/ld-x86-64/libgot-2.rd: Likewise.


-- 
H.J.
0001-readelf-Update-dynamic-tag-processing-and-validate-s.patch (text/x-patch, 11.3 KB)
From a09869cd143ffbd9c0a33207da9c3322e31458f2 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Wed, 1 Jul 2026 10:15:30 +0800
Subject: [PATCH] readelf: Update dynamic tag processing and validate section
 info

Count DT_RELR relocations using dynamic tags when section header isn't
used.  Ignore section sh_offset and sh_size if they are larger than file
size.

One side effect is that we now display

readelf: Warning: Ignore the out of range sh_offset value of 2199023261148 for section 17
readelf: Warning: Ignore the out of range sh_size value of 13 for section 17 with sh_offset value of 2199023261148
...
  [17] .fini             PROGBITS        00000000000015dc 000000 000000 00  AX  0   0  4
...

instead of

...
  [17] .fini             PROGBITS        00000000000015dc 200000015dc 00000d 00  AX  0   0  4
...

and segfault.

binutils/

	PR binutils/34326
	PR binutils/34339
	* readelf.c (update_all_relocations): Skip if nentries == 0.
	(validate_section_info): New.
	(get_32bit_section_headers): Call validate_section_info.
	(get_64bit_section_headers): Likewise.
	(process_relocs): Call count_relr_relocations if needed.  Check
	invalid invalid DT_RELENT and DT_RELAENT dynamic tags.
	(process_got_section_contents): Skip if all_relocations_count
	== 0.

ld/
	PR binutils/34339
	* testsuite/ld-x86-64/binutils.exp: Run PR binutils/34339 tests.
	* testsuite/ld-x86-64/got-2.s: New file.
	* testsuite/ld-x86-64/libgot-2.rd: Likewise.

Signed-off-by: H.J. Lu <[email protected]>
---
 binutils/readelf.c                  | 152 +++++++++++++++++++++++-----
 ld/testsuite/ld-x86-64/binutils.exp |  48 +++++++++
 ld/testsuite/ld-x86-64/got-2.s      |  10 ++
 ld/testsuite/ld-x86-64/libgot-2.rd  |   9 ++
 4 files changed, 192 insertions(+), 27 deletions(-)
 create mode 100644 ld/testsuite/ld-x86-64/got-2.s
 create mode 100644 ld/testsuite/ld-x86-64/libgot-2.rd

diff --git a/binutils/readelf.c b/binutils/readelf.c
index 33b95da8b7e..582d67cf2ab 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -1831,7 +1831,7 @@ update_all_relocations (size_t nentries)
 {
   size_t sz;
 
-  if (!do_got_section_contents)
+  if (nentries == 0 || !do_got_section_contents)
     return;
 
   if (!all_relocations_root)
@@ -7848,6 +7848,92 @@ offset_from_vma (Filedata * filedata, uint64_t vma, uint64_t size)
   return vma;
 }
 
+/* Valid section info and clear the invalid fields.  */
+
+static void
+validate_section_info (Elf_Internal_Shdr *internal, unsigned int i,
+		       Filedata *filedata, bool dynamic, bool probe)
+{
+  const char *dynamic_tag = NULL;
+  const char *dynamicsz_tag = NULL;
+  const char *dynamicent_tag = NULL;
+  if (probe)
+    return;
+
+  if (dynamic)
+    switch (i)
+      {
+      case DT_RELR:
+	dynamic_tag = "DT_RELR";
+	dynamicsz_tag = "DT_RELRSZ";
+	dynamicent_tag = "DT_RELRENT";
+	break;
+
+      default:
+	abort ();
+      }
+  else
+    {
+      if (internal->sh_link >= filedata->file_header.e_shnum
+	  && !special_defined_section_index (filedata,
+					     internal->sh_link))
+	{
+	  warn (_("Ignore the out of range sh_link value of %u for "
+		  "section %u\n"), internal->sh_link, i);
+	  internal->sh_link = 0;
+	}
+
+      if ((internal->sh_flags & SHF_INFO_LINK) != 0
+	  && internal->sh_info > filedata->file_header.e_shnum)
+	{
+	  warn (_("Ignore the out of range sh_info value of %u for "
+		  "section %u\n"), internal->sh_info, i);
+	  internal->sh_info = 0;
+	}
+    }
+
+  if (internal->sh_entsize > filedata->file_size)
+    {
+      if (dynamic)
+	warn (_("Ignore the out of range value of %" PRId64 " for "
+		"dynamic tag %s\n"), (uint64_t) internal->sh_entsize,
+	      dynamicent_tag);
+      else
+	warn (_("Ignore the out of range sh_entsize value of %"
+		PRId64 " for section %u\n"),
+	      (uint64_t) internal->sh_entsize, i);
+      internal->sh_entsize = 0;
+    }
+
+  if (internal->sh_type != SHT_NOBITS)
+    {
+      int64_t sh_offset = internal->sh_offset;
+      if (sh_offset < 0 || (uint64_t) sh_offset > filedata->file_size)
+	{
+	  if (dynamic)
+	    warn (_("Ignore the out of range value of %" PRId64 " for "
+		    "dynamic tag %s\n"), sh_offset, dynamic_tag);
+	  else
+	    warn (_("Ignore the out of range sh_offset value of %"
+		    PRId64 " for section %u\n"), sh_offset, i);
+	  internal->sh_offset = 0;
+	}
+
+      if (sh_offset + internal->sh_size > filedata->file_size)
+	{
+	  if (dynamic)
+	    warn (_("Ignore the out of range value of %" PRId64 " for "
+		    "dynamic tag %s\n"), (uint64_t) internal->sh_size,
+		  dynamicsz_tag);
+	  else
+	    warn (_("Ignore the out of range sh_size value of %"
+		    PRIu64 " for section %u with sh_offset value of %"
+		    PRId64 "\n"), (uint64_t) internal->sh_size, i,
+		  sh_offset);
+	  internal->sh_size = 0;
+	}
+    }
+}
 
 /* Allocate memory and load the sections headers into FILEDATA->filedata->section_headers.
    If PROBE is true, this is just a probe and we do not generate any error
@@ -7911,13 +7997,7 @@ get_32bit_section_headers (Filedata * filedata, bool probe)
       internal->sh_info      = BYTE_GET (shdrs[i].sh_info);
       internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign);
       internal->sh_entsize   = BYTE_GET (shdrs[i].sh_entsize);
-      if (!probe
-	  && internal->sh_link >= num
-	  && !special_defined_section_index (filedata,
-					     internal->sh_link))
-	warn (_("Section %u has an out of range sh_link value of %u\n"), i, internal->sh_link);
-      if (!probe && internal->sh_flags & SHF_INFO_LINK && internal->sh_info > num)
-	warn (_("Section %u has an out of range sh_info value of %u\n"), i, internal->sh_info);
+      validate_section_info (internal, i, filedata, false, probe);
     }
 
   free (shdrs);
@@ -7986,13 +8066,7 @@ get_64bit_section_headers (Filedata * filedata, bool probe)
       internal->sh_info      = BYTE_GET (shdrs[i].sh_info);
       internal->sh_offset    = BYTE_GET (shdrs[i].sh_offset);
       internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign);
-      if (!probe
-	  && internal->sh_link >= num
-	  && !special_defined_section_index (filedata,
-					     internal->sh_link))
-	warn (_("Section %u has an out of range sh_link value of %u\n"), i, internal->sh_link);
-      if (!probe && internal->sh_flags & SHF_INFO_LINK && internal->sh_info > num)
-	warn (_("Section %u has an out of range sh_info value of %u\n"), i, internal->sh_info);
+      validate_section_info (internal, i, filedata, false, probe);
     }
 
   free (shdrs);
@@ -10112,19 +10186,43 @@ process_relocs (Filedata * filedata)
 	    }
 
 	  if (rel_type == reltype_relr)
-	    dump_relr_relocations (filedata,
-				   filedata->dynamic_info[DT_RELRSZ],
-				   filedata->dynamic_info[DT_RELRENT],
-				   filedata->dynamic_info[DT_RELR],
-				   NULL,
-				   filedata->dynamic_symbols,
-				   filedata->num_dynamic_syms,
-				   filedata->dynamic_strings,
-				   filedata->dynamic_strings_length,
-				   do_reloc);
+	    {
+	      if (all_relocations_count == 0)
+		{
+		  /* Count DT_RELR relocations when "-D --got-contents"
+		     is passed to readelf.  */
+		  uint64_t num_reloc;
+		  uint64_t *relrs = NULL;
+		  Elf_Internal_Shdr section = {};
+		  section.sh_offset
+		    = filedata->dynamic_info[DT_RELR];
+		  section.sh_size = rel_size;
+		  section.sh_entsize = rel_entsz;
+		  section.sh_type = SHT_RELR;
+		  validate_section_info (&section, DT_RELR, filedata,
+					 true, false);
+		  num_reloc = count_relr_relocations (filedata,
+						      &section,
+						      &relrs);
+		  free (relrs);
+		  if (num_reloc == 0)
+		    continue;
+		  update_all_relocations (num_reloc);
+		}
+	      dump_relr_relocations (filedata,
+				     filedata->dynamic_info[DT_RELRSZ],
+				     filedata->dynamic_info[DT_RELRENT],
+				     filedata->dynamic_info[DT_RELR],
+				     NULL,
+				     filedata->dynamic_symbols,
+				     filedata->num_dynamic_syms,
+				     filedata->dynamic_strings,
+				     filedata->dynamic_strings_length,
+				     do_reloc);
+	    }
 	  else
 	    {
-	      if (rel_entsz == 0)
+	      if (rel_entsz == 0 || rel_entsz > rel_size)
 		{
 		  printf (_("<missing or corrupt dynamic tag: %s>\n"),
 			  entsz_name);
@@ -21534,7 +21632,7 @@ process_got_section_contents (Filedata * filedata)
   bool res = true;
   bool found = false;
 
-  if (!do_got_section_contents)
+  if (!do_got_section_contents || all_relocations_count == 0)
     return res;
 
   switch (filedata->file_header.e_type)
diff --git a/ld/testsuite/ld-x86-64/binutils.exp b/ld/testsuite/ld-x86-64/binutils.exp
index cc466bdbc6d..e08834a2838 100644
--- a/ld/testsuite/ld-x86-64/binutils.exp
+++ b/ld/testsuite/ld-x86-64/binutils.exp
@@ -41,6 +41,30 @@ if [ld_supports_emul "elf_x86_64"] {
 	    {{readelf {-rW --got-contents} libgot-1.rd}} \
 	    "libgot-1-x64.so" \
 	] \
+	[list \
+	    "Build libgot-2-x64.so (--got-contents)" \
+	    "-shared -melf_x86_64 --no-ld-generated-unwind-info \
+	     -z noseparate-code -z max-page-size=0x200000 \
+	     --hash-style=sysv -z nomark-plt $DT_RELR_LDFLAGS \
+	     --rosegment" \
+	    "" \
+	    "--64 -mx86-used-note=no --generate-missing-build-notes=no" \
+	    {got-2.s} \
+	    {{readelf {--got-contents} libgot-2.rd}} \
+	    "libgot-2-x64.so" \
+	] \
+	[list \
+	    "Build libgot-2-x64.so (-D --got-contents)" \
+	    "-shared -melf_x86_64 --no-ld-generated-unwind-info \
+	     -z noseparate-code -z max-page-size=0x200000 \
+	     --hash-style=sysv -z nomark-plt $DT_RELR_LDFLAGS \
+	     --rosegment" \
+	    "" \
+	    "--64 -mx86-used-note=no --generate-missing-build-notes=no" \
+	    {got-2.s} \
+	    {{readelf {-D --got-contents} libgot-2.rd}} \
+	    "libgot-2-x64.so" \
+	] \
     ]
 }
 
@@ -58,5 +82,29 @@ if [ld_supports_emul "elf32_x86_64"] {
 	    {{readelf {-rW --got-contents} libgot-1-x32.rd}} \
 	    "libgot-1-x32.so" \
 	] \
+	[list \
+	    "Build libgot-1-x32.so (--got-contents)" \
+	    "-shared -melf32_x86_64 --no-ld-generated-unwind-info \
+	     -z noseparate-code -z max-page-size=0x200000 \
+	     --hash-style=sysv -z nomark-plt $DT_RELR_LDFLAGS \
+	     --rosegment" \
+	    "" \
+	    "--x32 -mx86-used-note=no --generate-missing-build-notes=no" \
+	    {got-2.s} \
+	    {{readelf {--got-contents} libgot-2.rd}} \
+	    "libgot-2-x32.so" \
+	] \
+	[list \
+	    "Build libgot-1-x32.so (-D --got-contents)" \
+	    "-shared -melf32_x86_64 --no-ld-generated-unwind-info \
+	     -z noseparate-code -z max-page-size=0x200000 \
+	     --hash-style=sysv -z nomark-plt $DT_RELR_LDFLAGS \
+	     --rosegment" \
+	    "" \
+	    "--x32 -mx86-used-note=no --generate-missing-build-notes=no" \
+	    {got-2.s} \
+	    {{readelf {-D --got-contents} libgot-2.rd}} \
+	    "libgot-2-x32.so" \
+	] \
     ]
 }
diff --git a/ld/testsuite/ld-x86-64/got-2.s b/ld/testsuite/ld-x86-64/got-2.s
new file mode 100644
index 00000000000..d930447fd89
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/got-2.s
@@ -0,0 +1,10 @@
+	.section .bar,"aw",@progbits
+	.p2align 3
+	.dc.a	__ehdr_start@gotpcrel
+	.dc.a	__ehdr_start
+
+	.section .foo,"aw",@progbits
+	.p2align 3
+	.dc.a	__ehdr_start@gotpcrel
+	.dc.a	__ehdr_start
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/libgot-2.rd b/ld/testsuite/ld-x86-64/libgot-2.rd
new file mode 100644
index 00000000000..1dc41c14805
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/libgot-2.rd
@@ -0,0 +1,9 @@
+Global Offset Table '\.got' contains 1 entry:
+.*
+.* R_X86_64_RELATIVE +0
+
+Global Offset Table '\.got\.plt' contains 3 entries:
+.*
+.* [^0][0-9a-f]+
+.* 0
+.* 0
-- 
2.54.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.