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
@@ -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 (§ion, DT_RELR, filedata,
+ true, false);
+ num_reloc = count_relr_relocations (filedata,
+ §ion,
+ &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)
@@ -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" \
+ ] \
]
}
new file mode 100644
@@ -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
new file mode 100644
@@ -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