diff options
| -rw-r--r-- | libelf/elf32_getshdr.c | 14 | ||||
| -rw-r--r-- | libelf/elf_begin.c | 26 | ||||
| -rw-r--r-- | libelf/elf_scnshndx.c | 52 | ||||
| -rw-r--r-- | libelf/libelf.h | 9 | ||||
| -rw-r--r-- | libelf/libelfP.h | 4 | ||||
| -rw-r--r-- | src/readelf.c | 32 | ||||
| -rw-r--r-- | tests/Makefile.am | 26 | ||||
| -rw-r--r-- | tests/elf-print-reloc-syms.c | 4 | ||||
| -rw-r--r-- | tests/manyfuncs.c | 53 | ||||
| -rwxr-xr-x | tests/run-readelf-r-manyfuncs.sh | 40 | ||||
| -rwxr-xr-x | tests/run-test-manyfuncs.sh | 23 | ||||
| -rw-r--r-- | tests/test-manyfuncs.c | 343 |
12 files changed, 560 insertions, 66 deletions
diff --git a/libelf/elf32_getshdr.c b/libelf/elf32_getshdr.c index 19b690a8..e4bebe18 100644 --- a/libelf/elf32_getshdr.c +++ b/libelf/elf32_getshdr.c @@ -146,20 +146,6 @@ load_shdr_wrlock (Elf_Scn *scn) CONVERT_TO (shdr[cnt].sh_addralign, notcvt[cnt].sh_addralign); CONVERT_TO (shdr[cnt].sh_entsize, notcvt[cnt].sh_entsize); - - /* If this is a section with an extended index add a - reference in the section which uses the extended - index. */ - if (shdr[cnt].sh_type == SHT_SYMTAB_SHNDX - && shdr[cnt].sh_link < shnum) - elf->state.ELFW(elf,LIBELFBITS).scns.data[shdr[cnt].sh_link].shndx_index - = cnt; - - /* Set the own shndx_index field in case it has not yet - been set. */ - if (elf->state.ELFW(elf,LIBELFBITS).scns.data[cnt].shndx_index == 0) - elf->state.ELFW(elf,LIBELFBITS).scns.data[cnt].shndx_index - = -1; } if (copy) diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index 2b3b465f..3ed1f8d7 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -412,19 +412,6 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident, ((char *) map_address + offset + elf->state.elf32.shdr[cnt].sh_offset); elf->state.elf32.scns.data[cnt].list = &elf->state.elf32.scns; - - /* If this is a section with an extended index add a - reference in the section which uses the extended - index. */ - if (elf->state.elf32.shdr[cnt].sh_type == SHT_SYMTAB_SHNDX - && elf->state.elf32.shdr[cnt].sh_link < scncnt) - elf->state.elf32.scns.data[elf->state.elf32.shdr[cnt].sh_link].shndx_index - = cnt; - - /* Set the own shndx_index field in case it has not yet - been set. */ - if (elf->state.elf32.scns.data[cnt].shndx_index == 0) - elf->state.elf32.scns.data[cnt].shndx_index = -1; } } else @@ -510,19 +497,6 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident, ((char *) map_address + offset + elf->state.elf64.shdr[cnt].sh_offset); elf->state.elf64.scns.data[cnt].list = &elf->state.elf64.scns; - - /* If this is a section with an extended index add a - reference in the section which uses the extended - index. */ - if (elf->state.elf64.shdr[cnt].sh_type == SHT_SYMTAB_SHNDX - && elf->state.elf64.shdr[cnt].sh_link < scncnt) - elf->state.elf64.scns.data[elf->state.elf64.shdr[cnt].sh_link].shndx_index - = cnt; - - /* Set the own shndx_index field in case it has not yet - been set. */ - if (elf->state.elf64.scns.data[cnt].shndx_index == 0) - elf->state.elf64.scns.data[cnt].shndx_index = -1; } } else diff --git a/libelf/elf_scnshndx.c b/libelf/elf_scnshndx.c index 5b783faa..61662b9a 100644 --- a/libelf/elf_scnshndx.c +++ b/libelf/elf_scnshndx.c @@ -1,5 +1,6 @@ /* Get the section index of the extended section index table. Copyright (C) 2007 Red Hat, Inc. + Copyright (C) 2025 Mark J. Wielaard <[email protected]> This file is part of elfutils. Contributed by Ulrich Drepper <[email protected]>, 2007. @@ -37,14 +38,53 @@ int elf_scnshndx (Elf_Scn *scn) { - if (unlikely (scn->shndx_index == 0)) + size_t scnndx; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + Elf *elf; + Elf_Scn *nscn; + + if (scn == NULL) + return -1; + + scnndx = scn->index; + elf = scn->elf; + + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return -1; + + /* Only SYMTAB sections can have a SHNDX section. */ + if (shdr->sh_type != SHT_SYMTAB) + return 0; + + /* By convention the SHT_SYMTAB_SHNDX section is right after the the + SHT_SYMTAB section, so start there. */ + nscn = scn; + while ((nscn = elf_nextscn (elf, nscn)) != NULL) { - /* We do not have the value yet. We get it as a side effect of - getting a section header. */ - GElf_Shdr shdr_mem; - (void) INTUSE(gelf_getshdr) (scn, &shdr_mem); + shdr = gelf_getshdr (nscn, &shdr_mem); + if (shdr == NULL) + return -1; + + if (shdr->sh_type == SHT_SYMTAB_SHNDX && shdr->sh_link == scnndx) + return nscn->index; + } + + /* OK, not found, start from the top. */ + nscn = NULL; + while ((nscn = elf_nextscn (elf, nscn)) != NULL + && nscn->index != scnndx) + { + shdr = gelf_getshdr (nscn, &shdr_mem); + if (shdr == NULL) + return -1; + + if (shdr->sh_type == SHT_SYMTAB_SHNDX && shdr->sh_link == scnndx) + return nscn->index; } - return scn->shndx_index; + /* No shndx found, but no errors. */ + return 0; } INTDEF(elf_scnshndx) diff --git a/libelf/libelf.h b/libelf/libelf.h index d3f057b4..7bf88bb2 100644 --- a/libelf/libelf.h +++ b/libelf/libelf.h @@ -315,7 +315,14 @@ extern Elf_Scn *elf_nextscn (Elf *__elf, Elf_Scn *__scn); extern Elf_Scn *elf_newscn (Elf *__elf); /* Get the section index of the extended section index table for the - given symbol table. */ + given symbol table. Returns -1 when the given Elf_Scn is NULL or + if an error occurred during lookup, elf_errno will be set. Returns + 0 if the given Elf_Scn isn't a symbol table (sh_type is not + SHT_SYMTAB) or no extended section index table could be + found. Otherwise the section index of the extended section index + table for the given Elf_Scn is returned. An extended index table + has a sh_type of SHT_SYMTAB_SHNDX and a sh_link equal to the given + symbol table section index. */ extern int elf_scnshndx (Elf_Scn *__scn); /* Get the number of sections in the ELF file. If the file uses more diff --git a/libelf/libelfP.h b/libelf/libelfP.h index a9213ab3..66e7e4dd 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -218,9 +218,6 @@ struct Elf_Scn int data_read; /* Nonzero if the section was created by the user or if the data from the file/memory is read. */ - int shndx_index; /* Index of the extended section index - table for this symbol table (if this - section is a symbol table). */ size_t index; /* Index of this section. */ struct Elf *elf; /* The underlying ELF file. */ @@ -524,7 +521,6 @@ extern Elf_Scn *__elf_getscn_internal (Elf *__elf, size_t __index) attribute_hidden; extern Elf_Scn *__elf_nextscn_internal (Elf *__elf, Elf_Scn *__scn) attribute_hidden; -extern int __elf_scnshndx_internal (Elf_Scn *__scn) attribute_hidden; extern Elf_Data *__elf_getdata_internal (Elf_Scn *__scn, Elf_Data *__data) attribute_hidden; extern Elf_Data *__elf_getdata_rdlock (Elf_Scn *__scn, Elf_Data *__data) diff --git a/src/readelf.c b/src/readelf.c index c9aebd5b..12d85472 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -1,6 +1,6 @@ /* Print information from ELF file in human-readable form. Copyright (C) 1999-2018 Red Hat, Inc. - Copyright (C) 2023 Mark J. Wielaard <[email protected]> + Copyright (C) 2023, 2025 Mark J. Wielaard <[email protected]> This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -2111,9 +2111,10 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) return; } - /* Search for the optional extended section index table. */ + /* Search for the optional extended section index table if there are + more than 64k sections. */ Elf_Data *xndxdata = NULL; - int xndxscnidx = elf_scnshndx (scn); + int xndxscnidx = shnum >= SHN_LORESERVE ? elf_scnshndx (symscn) : 0; if (unlikely (xndxscnidx > 0)) xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL); @@ -2218,7 +2219,11 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) _("INVALID SYMBOL"), (long int) GELF_R_SYM (rel->r_info)); } - else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION + && !(GELF_ST_TYPE (sym->st_info) == STT_NOTYPE + && GELF_ST_BIND (sym->st_info) == STB_LOCAL + && sym->st_shndx != SHN_UNDEF + && sym->st_value == 0)) // local start section label printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, likely (ebl_reloc_type_check (ebl, @@ -2232,7 +2237,9 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); else { - /* This is a relocation against a STT_SECTION symbol. */ + /* This is a relocation against a STT_SECTION symbol + or a local start section label for which we print + section name. */ GElf_Shdr secshdr_mem; GElf_Shdr *secshdr; secshdr = gelf_getshdr (elf_getscn (ebl->elf, @@ -2300,9 +2307,10 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) return; } - /* Search for the optional extended section index table. */ + /* Search for the optional extended section index table if there are + more than 64k sections. */ Elf_Data *xndxdata = NULL; - int xndxscnidx = elf_scnshndx (scn); + int xndxscnidx = shnum >= SHN_LORESERVE ? elf_scnshndx (symscn) : 0; if (unlikely (xndxscnidx > 0)) xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL); @@ -2409,7 +2417,11 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) _("INVALID SYMBOL"), (long int) GELF_R_SYM (rel->r_info)); } - else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) + else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION + && !(GELF_ST_TYPE (sym->st_info) == STT_NOTYPE + && GELF_ST_BIND (sym->st_info) == STB_LOCAL + && sym->st_shndx != SHN_UNDEF + && sym->st_value == 0)) // local start section label printf ("\ %#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, @@ -2425,7 +2437,9 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); else { - /* This is a relocation against a STT_SECTION symbol. */ + /* This is a relocation against a STT_SECTION symbol + or a local start section label for which we print + section name. */ GElf_Shdr secshdr_mem; GElf_Shdr *secshdr; secshdr = gelf_getshdr (elf_getscn (ebl->elf, diff --git a/tests/Makefile.am b/tests/Makefile.am index ec6cc901..335f1925 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -64,7 +64,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ getphdrnum leb128 read_unaligned \ msg_tst system-elf-libelf-test system-elf-gelf-test \ nvidia_extended_linemap_libdw elf-print-reloc-syms \ - cu-dwp-section-info declfiles \ + cu-dwp-section-info declfiles test-manyfuncs \ $(asm_TESTS) asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ @@ -82,6 +82,16 @@ backtrace-child-biarch$(EXEEXT): backtrace-child.c $(AM_LDFLAGS) $(LDFLAGS) $(backtrace_child_LDFLAGS) \ -o $@ $< +# A test object with 64K sections and function symbols +# Don't try to optimize or add debuginfo +# Do explicitly add unwind tables, so there are definitely relocations +# to all function symbols/sections. +manyfuncs.o: manyfuncs.c + $(AM_V_CC)$(CC) -fasynchronous-unwind-tables -g0 -O0 -o $@ -c $< + +# We also want the manyfuncs.o (test) file generated +check-local: manyfuncs.o + if GCOV GCOV_FLAGS=-fprofile-arcs -ftest-coverage else @@ -142,6 +152,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \ run-readelf-frames.sh \ run-readelf-n.sh \ + run-readelf-r-manyfuncs.sh \ run-retain.sh \ run-native-test.sh run-bug1-test.sh \ run-debuglink.sh run-debugaltlink.sh run-buildid.sh \ @@ -217,7 +228,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-readelf-dw-form-indirect.sh run-strip-largealign.sh \ run-readelf-Dd.sh run-dwfl-core-noncontig.sh run-cu-dwp-section-info.sh \ run-declfiles.sh \ - run-sysroot.sh + run-sysroot.sh \ + run-test-manyfuncs.sh if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -414,6 +426,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile_gnu_props.32be.o.bz2 \ testfile_gnu_props.64be.o.bz2 \ testfile-gnu-property-note-aarch64.bz2 \ + run-readelf-r-manyfuncs.sh \ run-retain.sh testfile-retain.o.bz2 \ run-allfcts-multi.sh \ test-offset-loop.bz2 test-offset-loop.alt.bz2 \ @@ -688,7 +701,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-dwp-4-cu-index-overflow.dwp.bz2 \ testfile-dwp-cu-index-overflow.source \ testfile-define-file.bz2 \ - testfile-sysroot.tar.bz2 run-sysroot.sh run-debuginfod-seekable.sh + testfile-sysroot.tar.bz2 run-sysroot.sh \ + run-test-manyfuncs.sh manyfuncs.c \ + run-debuginfod-seekable.sh if USE_VALGRIND @@ -869,6 +884,9 @@ nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw) elf_print_reloc_syms_LDADD = $(libelf) cu_dwp_section_info_LDADD = $(libdw) declfiles_LDADD = $(libdw) +test_manyfuncs_LDADD = $(libelf) +# Make sure the manyfunc.o test object is generated before test-manyfuncs +EXTRA_test_manyfuncs_DEPENDENCIES = manyfuncs.o # We want to test the libelf headers against the system elf.h header. # Don't include any -I CPPFLAGS. Except when we install our own elf.h. @@ -888,7 +906,7 @@ system_elf_gelf_test_LDADD = $(libelf) # A lock file used to make sure only one test dumps core at a time MOSTLYCLEANFILES = core-dump-backtrace.lock -CLEANFILES = $(BUILT_SOURCES) +CLEANFILES = $(BUILT_SOURCES) manyfuncs.o if GCOV check: check-am coverage diff --git a/tests/elf-print-reloc-syms.c b/tests/elf-print-reloc-syms.c index b9453f6b..c3a8b073 100644 --- a/tests/elf-print-reloc-syms.c +++ b/tests/elf-print-reloc-syms.c @@ -43,8 +43,8 @@ print_reloc_symnames (Elf *elf, Elf_Scn *scn, GElf_Shdr *shdr, size_t sh_entsize /* Search for the optional extended section index table. */ Elf_Data *xndxdata = NULL; - int xndxscnidx = elf_scnshndx (scn); - if (xndxscnidx) + int xndxscnidx = elf_scnshndx (symscn); + if (xndxscnidx > 0) xndxdata = elf_getdata (elf_getscn (elf, xndxscnidx), NULL); /* Get the section header string table index. */ diff --git a/tests/manyfuncs.c b/tests/manyfuncs.c new file mode 100644 index 00000000..f76d2690 --- /dev/null +++ b/tests/manyfuncs.c @@ -0,0 +1,53 @@ +/* Test file for a binary with 64K+ sections and symbols. + Copyright (C) 2025 Mark J. Wielaard <[email protected]> + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + + +/* We need a quoted string starting with a dot (the section name). */ +#define DQ(x) #x +#define DOTQUOTE(x) DQ(.x) + +/* We want 2 * (8 ^ 5) = 2 * ((2 ^ 3) ^ 5) = 2 ^ 16 = 64K functions. */ +#define f(x) __attribute__ ((section (DOTQUOTE(x)))) void x(void) {}; +#define A(x) f(x##0) f(x##1) f(x##2) f(x##3) f(x##4) f(x##5) f(x##6) f(x##7) +#define B(x) A(x##0) A(x##1) A(x##2) A(x##3) A(x##4) A(x##5) A(x##6) A(x##7) +#define C(x) B(x##0) B(x##1) B(x##2) B(x##3) B(x##4) B(x##5) B(x##6) B(x##7) +#define D(x) C(x##0) C(x##1) C(x##2) C(x##3) C(x##4) C(x##5) C(x##6) C(x##7) +#define E(x) D(x##0) D(x##1) D(x##2) D(x##3) D(x##4) D(x##5) D(x##6) D(x##7) +E(y) +E(z) + +#undef f +#undef A +#undef B +#undef C +#undef D +#undef E + +/* Call all functions from main. */ +#define f(x) x(); +#define A(x) f(x##0) f(x##1) f(x##2) f(x##3) f(x##4) f(x##5) f(x##6) f(x##7) +#define B(x) A(x##0) A(x##1) A(x##2) A(x##3) A(x##4) A(x##5) A(x##6) A(x##7) +#define C(x) B(x##0) B(x##1) B(x##2) B(x##3) B(x##4) B(x##5) B(x##6) B(x##7) +#define D(x) C(x##0) C(x##1) C(x##2) C(x##3) C(x##4) C(x##5) C(x##6) C(x##7) +#define E(x) D(x##0) D(x##1) D(x##2) D(x##3) D(x##4) D(x##5) D(x##6) D(x##7) + +int +main () +{ +E(y) +E(z) +} diff --git a/tests/run-readelf-r-manyfuncs.sh b/tests/run-readelf-r-manyfuncs.sh new file mode 100755 index 00000000..a7bb0478 --- /dev/null +++ b/tests/run-readelf-r-manyfuncs.sh @@ -0,0 +1,40 @@ +#! /bin/sh +# Test readelf -r on file containing 64K+ functions and sections +# Copyright (C) 2025 Mark J. Wielaard <[email protected]> +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $srcdir/test-subr.sh + +# Tests readelf -r on file containing 64K+ functions and sections +# This forces the use of elf_scnshndx to lookup section indexes +# The test makes sure that for relocations against sectio symbols +# the section names are resolved even for section indexes > 64K. +# +# Note the sections are named after the symbols (with a '.' in front). + +testrun ${abs_top_builddir}/src/readelf -r \ + ${abs_top_builddir}/tests/manyfuncs.o | grep -F ".y00000" || exit 1 + +testrun ${abs_top_builddir}/src/readelf -r \ + ${abs_top_builddir}/tests/manyfuncs.o | grep -F ".z00000" || exit 1 + +testrun ${abs_top_builddir}/src/readelf -r \ + ${abs_top_builddir}/tests/manyfuncs.o | grep -F ".y77777" || exit 1 + +testrun ${abs_top_builddir}/src/readelf -r \ + ${abs_top_builddir}/tests/manyfuncs.o | grep -F ".z77777" || exit 1 + +exit 0 diff --git a/tests/run-test-manyfuncs.sh b/tests/run-test-manyfuncs.sh new file mode 100755 index 00000000..110c599c --- /dev/null +++ b/tests/run-test-manyfuncs.sh @@ -0,0 +1,23 @@ +#! /bin/sh +# Copyright (C) 2025 Mark J. Wielaard <[email protected]> +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $srcdir/test-subr.sh + +testrun ${abs_top_builddir}/tests/test-manyfuncs \ + ${abs_top_builddir}/tests/manyfuncs.o + +exit 0 diff --git a/tests/test-manyfuncs.c b/tests/test-manyfuncs.c new file mode 100644 index 00000000..72569020 --- /dev/null +++ b/tests/test-manyfuncs.c @@ -0,0 +1,343 @@ +/* Test program for reading file with 64k+ sections and symbols. + Copyright (C) 2025 Mark J. Wielaard <[email protected]> + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "system.h" + +#include ELFUTILS_HEADER(elf) +#include <gelf.h> + +/* Various fields (Elf_Half) can only contain up to 64k values. So + they need to be accessed through libelf functions which know where + the "true" value can be found. Also to get the section associated + with a symbol will need an extended symbol table entry. Use + elf_scnshndx to get at it and check we can get both symbol and + associated section names. */ + +static void +check_elf (const char *fname, bool use_mmap) +{ + printf ("\nfname (use_mmap: %d): %s\n", use_mmap, fname); + + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + printf ("cannot open `%s': %s\n", fname, strerror (errno)); + exit (1); + } + + Elf *elf = elf_begin (fd, use_mmap ? ELF_C_READ_MMAP : ELF_C_READ, NULL); + if (elf == NULL) + { + printf ("cannot create ELF descriptor: %s\n", elf_errmsg (-1)); + exit (1); + } + + /* How many sections are there? */ + size_t shnum; + if (elf_getshdrnum (elf, &shnum) < 0) + { + printf ("elf_getshdrstrndx: %s\n", elf_errmsg (-1)); + exit (1); + } + + if (shnum < SHN_LORESERVE) + { + printf ("Not enough section for test, %zd < %d\n", + shnum, SHN_LORESERVE); + exit (1); + } + + printf ("shnum: %zd\n", shnum); + + /* Get the section that contains the section header names. Check it + can be found and it contains the substring 'str' in its own + name. */ + size_t shstrndx; + if (elf_getshdrstrndx (elf, &shstrndx) < 0) + { + printf ("elf_getshdrstrndx: %s\n", elf_errmsg (-1)); + exit (1); + } + + Elf_Scn *scn = elf_getscn (elf, shstrndx); + if (scn == NULL) + { + printf ("cannot get section at index %zd: %s\n", shstrndx, + elf_errmsg (-1)); + exit (1); + } + + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + printf ("cannot get header for shstrndx section: %s\n", + elf_errmsg (-1)); + exit (1); + } + + /* Assume the section name contains at least the substring "str". */ + const char *sname = elf_strptr (elf, shstrndx, shdr->sh_name); + if (sname == NULL || strstr (sname, "str") == NULL) + { + printf ("Bad section name: %s\n", sname); + exit (1); + } + + printf ("shstrndx section name: %s\n", sname); + + /* The shstrndx section isn't a SYMTAB section, so elf_scnshndx will + return zero. */ + int shndx = elf_scnshndx (scn); + if (shndx < 0) + { + printf ("elf_scnshndx error for shstrndx section: %s\n", + elf_errmsg (-1)); + exit (1); + } + if (shndx > 0) + { + printf ("elf_scnshndx not zero for shstrndx section: %d\n", shndx); + exit (1); + } + + /* Find the symtab and symtab_shndx by hand. */ + size_t symtabndx = 0; + size_t symtabstrndx = 0; + size_t symtabsndx = 0; + size_t scns = 1; /* scn zero is skipped. */ + Elf_Scn *symtabscn = NULL; + Elf_Scn *xndxscn = NULL; + scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + if (scns != elf_ndxscn (scn)) + { + printf ("Unexpected elf_ndxscn %zd != %zd\n", + scns, elf_ndxscn (scn)); + exit (1); + } + + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + printf ("couldn't get shdr\n"); + exit (1); + } + + if (shdr->sh_type == SHT_SYMTAB) + { + symtabndx = elf_ndxscn (scn); + symtabstrndx = shdr->sh_link; + symtabscn = scn; + } + + if (shdr->sh_type == SHT_SYMTAB_SHNDX) + { + symtabsndx = elf_ndxscn (scn); + xndxscn = scn; + } + + /* We could break if we have both symtabndx and symtabsndx, but + we want to run through all sections to see if we get them + all and sanity check shnum. */ + scns++; + } + + printf ("scns: %zd\n", scns); + if (scns != shnum) + { + printf ("scns (%zd) != shnum (%zd)\n", scns, shnum); + exit (1); + } + + printf ("symtabndx: %zd\n", symtabndx); + if (symtabndx == 0 || symtabscn == NULL) + { + printf ("No SYMTAB\n"); + exit (1); + } + + printf ("symtabsndx: %zd\n", symtabsndx); + if (symtabsndx == 0) + { + printf ("No SYMTAB_SNDX\n"); + exit (1); + } + + int scnshndx = elf_scnshndx (symtabscn); + printf ("scnshndx: %d\n", scnshndx); + if (scnshndx < 0) + { + printf ("elf_scnshndx failed: %s\n", elf_errmsg (-1)); + exit (1); + } + + if (scnshndx == 0) + { + printf ("elf_scnshndx couldn't find scnshndx, returned zero\n"); + exit (1); + } + + if ((size_t) scnshndx != symtabsndx) + { + printf ("elf_scnshndx found wrong scnshndx (%d)," + " should have been (%zd)\n", scnshndx, symtabsndx); + exit (1); + } + + Elf_Data *symdata = elf_getdata (symtabscn, NULL); + if (symdata == NULL) + { + printf ("Couldn't elf_getdata for symtabscn: %s\n", elf_errmsg (-1)); + exit (1); + } + + size_t nsyms = symdata->d_size / (gelf_getclass (elf) == ELFCLASS32 + ? sizeof (Elf32_Sym) + : sizeof (Elf64_Sym)); + + Elf_Data *xndxdata = elf_getdata (xndxscn, NULL); + if (xndxdata == NULL) + { + printf ("Couldn't elf_getdata for xndxscn: %s\n", elf_errmsg (-1)); + exit (1); + } + + /* Now for every [yz]ddddd symbol , check that it matches the + section name (minus the starting dot). */ + size_t yzsymcnt = 0; + for (size_t cnt = 0; cnt < nsyms; ++cnt) + { + const char *sym_name; + const char *section_name; + GElf_Word xndx; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, cnt, + &sym_mem, &xndx); + + if (sym == NULL) + { + printf ("gelf_getsymshndx failed: %s\n", elf_errmsg (-1)); + exit (1); + } + + if (sym->st_shndx != SHN_XINDEX) + xndx = sym->st_shndx; + + sym_name = elf_strptr (elf, symtabstrndx, sym->st_name); + if (sym_name == NULL) + { + printf ("elf_strptr returned NULL for sym %zd: %s\n", + cnt, elf_errmsg (-1)); + exit (1); + } + + scn = elf_getscn (elf, xndx); + if (scn == NULL) + { + printf ("cannot get section at index %d: %s\n", xndx, + elf_errmsg (-1)); + exit (1); + } + + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + { + printf ("cannot get header for sym xndx section: %s\n", + elf_errmsg (-1)); + exit (1); + } + + section_name = elf_strptr (elf, shstrndx, shdr->sh_name); + if (section_name == NULL) + { + printf ("elf_strptr returned NULL for section: %s\n", + elf_errmsg (-1)); + exit (1); + } + + if (GELF_ST_TYPE (sym->st_info) == STT_FUNC + && (sym_name[0] == 'y' || sym_name[0] == 'z') + && strlen (sym_name) == 6) + { + yzsymcnt++; + /* Every [yz]ddddd symbol comes from a section named after + the symbol prefixed with '.'. Except for powerpc ELFv1, + where all symbols point into the .opd. */ + if ((section_name[0] != '.' + || strcmp (sym_name, §ion_name[1]) != 0) + && strcmp (section_name, ".opd") != 0) + { + printf ("BAD SYM/SECTION %zd sym: %s, section: %s\n", + cnt, sym_name, section_name); + exit (1); + } + } + } + +#define YZSYMCNT (size_t) (2 * 8 * 8 * 8 * 8 * 8) + printf ("yzsymcnt: %zd\n", yzsymcnt); + if (yzsymcnt != YZSYMCNT) + { + printf ("Wrong number of yzsymcnts: %zd, should be %zd\n", + yzsymcnt, YZSYMCNT); + exit (1); + } + + if (elf_end (elf) != 0) + { + printf ("failure in elf_end: %s\n", elf_errmsg (-1)); + exit (1); + } + + close (fd); +} + +int +main (int argc, char *argv[] __attribute__ ((unused))) +{ + elf_version (EV_CURRENT); + + if (argc < 2) + { + printf ("Need at least one (file name) argument\n"); + exit (1); + } + + /* Test all file using ELF_C_READ and ELF_C_READ_MMAP. */ + for (int i = 1; i < argc; i++) + { + check_elf (argv[i], false); + check_elf (argv[i], true); + } + + return 0; +} |
