From: Pengfei Li <[email protected]> dwfl_linux_kernel_module_section_address reads loaded kernel module section addresses from /sys/module/<name>/sections. Some sections in the module ELF intentionally have no corresponding sysfs file.
The kernel clears SHF_ALLOC from __versions, __version_ext_crcs, and __version_ext_names before laying out a module. It also clears SHF_ALLOC from .data..percpu because that section is allocated separately, and skips zero-sized sections when creating the module section sysfs attributes. Treat those sections as absent from the normal runtime section layout when opening their sysfs files fails with ENOENT. Keep accepting the existing .data.percpu spelling while also accepting the .data..percpu spelling used by current kernels. Other errors and missing nonzero sections continue to abort the callback. Add a regression test that calls the callback with a nonexistent per-process module name. It covers the existing special cases, the three module version sections, .data..percpu, a zero-sized section, and the failure path for an unknown nonzero section. Tested on Ubuntu x86-64 with: make check TESTS=dwfl-kernel-module-section-address https://sourceware.org/bugzilla/show_bug.cgi?id=34030 * libdwfl/linux-kernel-modules.c (dwfl_linux_kernel_module_section_address): Handle module version sections, .data..percpu, and zero-sized sections missing from sysfs. * tests/dwfl-kernel-module-section-address.c: New test. * tests/Makefile.am (check_PROGRAMS, TESTS): Add it. (dwfl_kernel_module_section_address_LDADD): New variable. Signed-off-by: Pengfei Li <[email protected]> diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index e9faba26..4a27a6e6 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -900,7 +900,7 @@ dwfl_linux_kernel_module_section_address void **userdata __attribute__ ((unused)), const char *modname, Dwarf_Addr base __attribute__ ((unused)), const char *secname, Elf32_Word shndx __attribute__ ((unused)), - const GElf_Shdr *shdr __attribute__ ((unused)), + const GElf_Shdr *shdr, Dwarf_Addr *addr) { char *sysfile; @@ -914,16 +914,22 @@ dwfl_linux_kernel_module_section_address { if (errno == ENOENT) { - /* The .modinfo and .data.percpu sections are never kept - loaded in the kernel. If the kernel was compiled without - CONFIG_MODULE_UNLOAD, the .exit.* sections are not - actually loaded at all. + /* The .modinfo and module version sections are not kept loaded + in the kernel. The kernel also does not expose zero-sized + sections. If the kernel was compiled without + CONFIG_MODULE_UNLOAD, the .exit.* sections are not loaded + either. The per-CPU section is allocated separately. Setting *ADDR to -1 tells the caller this section is actually absent from memory. */ - if (!strcmp (secname, ".modinfo") + if ((shdr != NULL && shdr->sh_size == 0) + || !strcmp (secname, ".modinfo") + || !strcmp (secname, "__versions") + || !strcmp (secname, "__version_ext_crcs") + || !strcmp (secname, "__version_ext_names") || !strcmp (secname, ".data.percpu") + || !strcmp (secname, ".data..percpu") || startswith (secname, ".exit")) { *addr = (Dwarf_Addr) -1l; diff --git a/tests/Makefile.am b/tests/Makefile.am index 8f087798..3c4c581a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -41,7 +41,8 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ find-prologues funcretval allregs rdwrmmap \ dwfl-bug-addr-overflow arls dwfl-bug-fd-leak \ dwfl-addr-sect dwfl-bug-report early-offscn \ - dwfl-bug-getmodules dwarf-getmacros dwarf-ranges addrcfi \ + dwfl-bug-getmodules dwfl-kernel-module-section-address \ + dwarf-getmacros dwarf-ranges addrcfi \ dwfl-core-noncontig dwarfcfi \ test-flag-nobits dwarf-getstring rerequest_tag \ alldts typeiter typeiter2 low_high_pc \ @@ -147,6 +148,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-debuglink.sh run-debugaltlink.sh run-buildid.sh \ dwfl-bug-addr-overflow run-addrname-test.sh \ dwfl-bug-fd-leak dwfl-bug-report dwfl-report-segment-contiguous \ + dwfl-kernel-module-section-address \ run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh \ run-disasm-x86.sh run-disasm-x86-64.sh \ run-early-offscn.sh run-dwarf-getmacros.sh run-dwarf-ranges.sh \ @@ -795,6 +797,8 @@ dwfl_bug_fd_leak_LDADD = $(libeu) $(libdw) $(libebl) $(libelf) dwfl_bug_report_LDADD = $(libdw) $(libebl) $(libelf) dwfl_bug_getmodules_LDADD = $(libeu) $(libdw) $(libebl) $(libelf) dwfl_addr_sect_LDADD = $(libeu) $(libdw) $(libebl) $(libelf) $(argp_LDADD) +dwfl_kernel_module_section_address_LDADD = \ + $(libdw) $(libelf) dwfl_core_noncontig_LDADD = $(libdw) $(libelf) dwarf_getmacros_LDADD = $(libdw) dwarf_ranges_LDADD = $(libdw) diff --git a/tests/dwfl-kernel-module-section-address.c b/tests/dwfl-kernel-module-section-address.c new file mode 100644 index 00000000..32c40ed0 --- /dev/null +++ b/tests/dwfl-kernel-module-section-address.c @@ -0,0 +1,83 @@ +/* Test handling of kernel module sections absent from sysfs. + 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/>. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include ELFUTILS_HEADER(dwfl) + +static char modname[64]; + +static void +check_absent (const char *secname, GElf_Xword size) +{ + GElf_Shdr shdr = { .sh_size = size }; + Dwarf_Addr addr = 0; + + int result = dwfl_linux_kernel_module_section_address (NULL, NULL, + modname, 0, + secname, 0, + &shdr, &addr); + assert (result == DWARF_CB_OK); + assert (addr == (Dwarf_Addr) -1l); +} + +static void +check_unknown (void) +{ + GElf_Shdr shdr = { .sh_size = 1 }; + Dwarf_Addr addr = 0; + + errno = 0; + int result = dwfl_linux_kernel_module_section_address (NULL, NULL, + modname, 0, + ".unknown", 0, + &shdr, &addr); + assert (result == DWARF_CB_ABORT); + assert (errno == ENOENT); + assert (addr == 0); +} + +int +main (void) +{ + int written = snprintf (modname, sizeof (modname), + "elfutils_test_%ld", (long) getpid ()); + assert (written > 0 && (size_t) written < sizeof (modname)); + + /* Existing special cases. */ + check_absent (".modinfo", 1); + check_absent (".data.percpu", 1); + check_absent (".exit.text", 1); + + /* The kernel clears SHF_ALLOC for these module version sections. */ + check_absent ("__versions", 1); + check_absent ("__version_ext_crcs", 1); + check_absent ("__version_ext_names", 1); + + /* The kernel module loader uses two dots in the per-CPU section name. */ + check_absent (".data..percpu", 1); + + /* Zero-sized sections are not exposed through module sysfs. */ + check_absent (".bss", 0); + + /* Other missing sections must still abort the callback. */ + check_unknown (); + + return 0; +} -- 2.34.1
