]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
readelf: Update dynamic tag processing and validate section info
authorH.J. Lu <hjl.tools@gmail.com>
Wed, 1 Jul 2026 02:15:30 +0000 (10:15 +0800)
committerH.J. Lu <hjl.tools@gmail.com>
Fri, 3 Jul 2026 13:50:49 +0000 (21:50 +0800)
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
ld/testsuite/ld-x86-64/binutils.exp
ld/testsuite/ld-x86-64/got-2.s [new file with mode: 0644]
ld/testsuite/ld-x86-64/libgot-2.rd [new file with mode: 0644]

index fe5a217801b4642b8d2f0517bcd61ffb2146028e..2826e0cf1950a5cd8ce94b24f1e1ef32c7e089b0 100644 (file)
@@ -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)
index cc466bdbc6d9b66f4a6e934d18b5f8c773c084be..f257bbd2c71869f1a611b098a1fbf29c08d06b00 100644 (file)
@@ -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 (file)
index 0000000..d930447
--- /dev/null
@@ -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 (file)
index 0000000..1dc41c1
--- /dev/null
@@ -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