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

"H.J. Lu" <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <CAMe9rOra2_w6CptLiXRKATwBAbo1sjEY6eOywcPUojy=HPrTcw@mail.gmail.com>
On Fri, Jul 3, 2026 at 6:05 PM Jan Beulich <[email protected]> wrote:
>
> On 02.07.2026 00:50, H.J. Lu wrote:
> > 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.
>
> Okay, albeit please with PRIu64 consistently used with uint64_t values.
>

Fixed.  This is the patch I am checking in.

Thanks.

-- 
H.J.
0001-readelf-Update-dynamic-tag-processing-and-validate-s.patch (text/x-patch, 10.5 KB)
From a1c57cc16119395e70226f9e62c123fe9397f41c 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 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 fe5a217801b..2826e0cf195 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 %" PRIu64 " for "
+		"dynamic tag %s\n"), (uint64_t) internal->sh_entsize,
+	      dynamicent_tag);
+      else
+	warn (_("Ignore the out of range sh_entsize value of %"
+		PRIu64 " 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 %" PRIu64 " 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);
@@ -10115,19 +10189,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);
@@ -21537,7 +21635,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.