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

"H.J. Lu" <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <CAMe9rOpVFVjuv3xjB4wcbiNM6uwmf8tK+Cji7Pk2N13wxEBujA@mail.gmail.com>
On Wed, Jul 1, 2026 at 2:37 PM H.J. Lu <[email protected]> wrote:
>
> 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.
>

Here is the v2 patch.   Changes are:

1. Combine 2 readelf dump entries into one.


-- 
H.J.
---
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 for relocations
from dynamic tags.  Skip if values in DT_RELENT/DT_RELAENT
are larger than values in DT_RELSZ/DT_RELASZ.
(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.
v2-0001-readelf-Update-dynamic-tag-processing-and-validat.patch (text/x-patch, 10.5 KB)
From 5c9082f9d597f34c4dbb33be0c120d5d61c10295 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Wed, 1 Jul 2026 10:15:30 +0800
Subject: [PATCH v2] 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 for relocations
	from dynamic tags.  Skip if values in DT_RELENT/DT_RELAENT
	are larger than values in DT_RELSZ/DT_RELASZ.
	(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 |  26 +++++
 ld/testsuite/ld-x86-64/got-2.s      |  10 ++
 ld/testsuite/ld-x86-64/libgot-2.rd  |   9 ++
 4 files changed, 170 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..f257bbd2c71 100644
--- a/ld/testsuite/ld-x86-64/binutils.exp
+++ b/ld/testsuite/ld-x86-64/binutils.exp
@@ -41,6 +41,19 @@ if [ld_supports_emul "elf_x86_64"] {
 	    {{readelf {-rW --got-contents} libgot-1.rd}} \
 	    "libgot-1-x64.so" \
 	] \
+	[list \
+	    "Build libgot-2-x64.so" \
+	    "-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} \
+	     {readelf {-D --got-contents} libgot-2.rd}} \
+	    "libgot-2-x64.so" \
+	] \
     ]
 }
 
@@ -58,5 +71,18 @@ if [ld_supports_emul "elf32_x86_64"] {
 	    {{readelf {-rW --got-contents} libgot-1-x32.rd}} \
 	    "libgot-1-x32.so" \
 	] \
+	[list \
+	    "Build libgot-2-x32.so" \
+	    "-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} \
+	     {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.