From: H.J. Lu Date: Wed, 1 Jul 2026 02:15:30 +0000 (+0800) Subject: readelf: Update dynamic tag processing and validate section info X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1dcfefd8dd49e32a9880a79c8fbb9276eaade23c;p=thirdparty%2Fbinutils-gdb.git 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 --- 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 (§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 (_("\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