Hi Pengfei,
On Tue, Aug 11, 2026 at 10:29 PM Li Pengfei <[email protected]> wrote:
>
> 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]>
Thanks for the patch, merged.
Aaron
>
> 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
>