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

Message ID CAMe9rOpVFVjuv3xjB4wcbiNM6uwmf8tK+Cji7Pk2N13wxEBujA@mail.gmail.com
State New
Headers
Series [v2] readelf: Update dynamic tag processing and validate section info |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

H.J. Lu July 1, 2026, 10:50 p.m. UTC
  On Wed, Jul 1, 2026 at 2:37 PM H.J. Lu <hjl.tools@gmail.com> 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.
  

Comments

Jan Beulich July 3, 2026, 10:05 a.m. UTC | #1
On 02.07.2026 00:50, H.J. Lu wrote:
> On Wed, Jul 1, 2026 at 2:37 PM H.J. Lu <hjl.tools@gmail.com> 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.

Jan
  
H.J. Lu July 3, 2026, 1:40 p.m. UTC | #2
On Fri, Jul 3, 2026 at 6:05 PM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 02.07.2026 00:50, H.J. Lu wrote:
> > On Wed, Jul 1, 2026 at 2:37 PM H.J. Lu <hjl.tools@gmail.com> 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.
  

Patch

From 5c9082f9d597f34c4dbb33be0c120d5d61c10295 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
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 <hjl.tools@gmail.com>
---
 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