A user reported that crash was reporting the address for certain module variables incorrectly. I was able to track it down specifically to variables which were located in the .data section of a kernel module. While the "sym" command gave the correct value, printing the address of the variable or expressions based on it with "p" would give an incorrect value. For example, the variable "ata_dummy_port_ops" variable is included in the .data section of libata.ko when built as a module:
$ sudo grep '\bata_dummy_port_ops\b' /proc/kallsyms ffffffffc0a71580 d ata_dummy_port_ops [libata] $ sudo crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /proc/kcore crash> sym ata_dummy_port_ops ffffffffc0a71580 (?) ata_dummy_port_ops [libata] crash> mod -s libata MODULE NAME TEXT_BASE SIZE OBJECT FILE ffffffffc0a7b640 libata ffffffffc0a47000 520192 /usr/lib/debug/lib/modules/6.12.0-0.11.8.el9uek.x86_64/kernel/drivers/ata/libata.ko.debug crash> sym ata_dummy_port_ops ffffffffc0a71580 (B) ata_dummy_port_ops [libata] crash> p/x &ata_dummy_port_ops $1 = 0xffffffffc0a6fe80 The symbol value (from kallsyms) is correct, but its address provided by GDB is incorrect. It turns out that the .data section has an sh_addr which is non-zero. The result of this is that calculate_load_order_6_4() incorrectly calculates the base address for the .data section. This patch fixes the base address which is later provided to GDB via add-symbol-file. The impact here is interesting. Only variables within sections that have a non-zero sh_addr are impacted. It turns out that this is relatively common since Linux kernel commit 22d407b164ff7 ("lib: add allocation tagging support for memory allocation profiling"), which was merged in 6.10. That commit added an entry to the scripts/module.lds.S linker script, without specifying a base address of zero. I believe that is the reason for the non-zero base addresses. I was able to verify that, in addition to the Oracle Linux kernel where we initially noticed the issue, kernel modules on Arch Linux and Fedora also have non-zero .data sh_addr values. This is likely the case for most non-clang kernels since 6.10, but those were the only two distros I checked. While my reading of the module.lds.S seems to indicate that kernels built with CONFIG_LTO_CLANG=y should also have non-zero .data, .bss, and .rodata section addresses, I haven't been able to reproduce this with clang LTO kernels. Regardless, crash should properly handle non-zero sh_addr since it exists in the real world now. The core of the issue is that the symbol value returned by BFD includes the sh_addr of the section containing the symbol. For example, suppose a symbol with address 0 is located within a section with virtual address 0xa00. Then, the resulting symbol value will be 0xa00, not 0. calculate_load_order_6_4() computes the base address of each section by using a kallsyms symbol known to be within that section, and then subtracting the value of the symbol from the object file. This implicitly assumes that the section sh_addr is zero, and thus the symbol value is just an offset. To fix the computation, add in the section base address, to account for cases where it is non-zero. Signed-off-by: Stephen Brennan <stephen.s.bren...@oracle.com> --- symbols.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/symbols.c b/symbols.c index 5adbc30..e30fafe 100644 --- a/symbols.c +++ b/symbols.c @@ -12808,6 +12808,7 @@ calculate_load_order_6_4(struct load_module *lm, bfd *bfd, int dynamic, asymbol *store; asymbol *sym; symbol_info syminfo; + bfd_vma secaddr; char *secname; int i, t; @@ -12860,6 +12861,7 @@ calculate_load_order_6_4(struct load_module *lm, bfd *bfd, int dynamic, } if (strcmp(syminfo.name, s1->name) == 0) { secname = (char *)bfd_section_name(sym->section); + secaddr = bfd_section_vma(sym->section); break; } @@ -12890,14 +12892,14 @@ calculate_load_order_6_4(struct load_module *lm, bfd *bfd, int dynamic, } /* Update the offset information for the section */ - sec_start = s1->value - syminfo.value; + sec_start = s1->value - syminfo.value + secaddr; /* keep the address instead of offset */ lm->mod_section_data[i].addr = sec_start; lm->mod_section_data[i].flags |= SEC_FOUND; if (CRASHDEBUG(2)) - fprintf(fp, "update sec offset sym %s @ %lx val %lx section %s\n", - s1->name, s1->value, (ulong)syminfo.value, secname); + fprintf(fp, "update sec offset sym %s @ %lx val %lx section %s @ %lx\n", + s1->name, s1->value, (ulong)syminfo.value, secname, secaddr); if (strcmp(secname, ".text") == 0) lm->mod_text_start = sec_start; -- 2.43.5 -- Crash-utility mailing list -- devel@lists.crash-utility.osci.io To unsubscribe send an email to devel-le...@lists.crash-utility.osci.io https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/ Contribution Guidelines: https://github.com/crash-utility/crash/wiki