[PATCH CRASH] Fix module section load address when sh_addr != 0
by Stephen Brennan
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.brennan(a)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
5 hours, 39 minutes
[PATCH] RISCV64: Add more system properties to the 'mach' command
by Austin Kim
Currently, 'mach' command displays only basic system properties for
RISC-V-based vmcores. This commit enhances the mach command by adding
additional system details, including virtual memory addresses, IRQ stacks,
and overflow stacks.
(before)
crash> mach
MACHINE TYPE: riscv64
MEMORY SIZE: 4 GB
CPUS: 4
PROCESSOR SPEED: (unknown)
HZ: 100
PAGE SIZE: 4096
KERNEL STACK SIZE: 16384
(after)
crash> mach
MACHINE TYPE: riscv64
MEMORY SIZE: 4 GB
CPUS: 4
PROCESSOR SPEED: (unknown)
HZ: 100
PAGE SIZE: 4096
KERNEL VIRTUAL BASE: ffffffd800000000
KERNEL MODULES BASE: ffffffff01d08000
KERNEL VMALLOC BASE: ffffffc800000000
KERNEL VMEMMAP BASE: ffffffc700000000
KERNEL STACK SIZE: 16384
IRQ STACK SIZE: 16384
IRQ STACKS:
CPU 0: ffffffc800000000
CPU 1: ffffffc800008000
CPU 2: ffffffc800010000
CPU 3: ffffffc800018000
OVERFLOW STACK SIZE: 4096
OVERFLOW STACKS:
CPU 0: ffffffd8fc7433c0
CPU 1: ffffffd8fc75f3c0
CPU 2: ffffffd8fc77b3c0
CPU 3: ffffffd8fc7973c0
Signed-off-by: Austin Kim <austindh.kim(a)gmail.com>
---
riscv64.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/riscv64.c b/riscv64.c
index 98bf02a..d934b22 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -135,6 +135,7 @@ static void riscv64_get_struct_page_size(struct machine_specific *ms)
static void
riscv64_display_machine_stats(void)
{
+ int i, pad;
struct new_utsname *uts;
char buf[BUFSIZE];
ulong mhz;
@@ -151,7 +152,31 @@ riscv64_display_machine_stats(void)
fprintf(fp, "(unknown)\n");
fprintf(fp, " HZ: %d\n", machdep->hz);
fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE());
+ fprintf(fp, "KERNEL VIRTUAL BASE: %lx\n", machdep->machspec->page_offset);
+ fprintf(fp, "KERNEL MODULES BASE: %lx\n", machdep->machspec->modules_vaddr);
+ fprintf(fp, "KERNEL VMALLOC BASE: %lx\n", machdep->machspec->vmalloc_start_addr);
+ fprintf(fp, "KERNEL VMEMMAP BASE: %lx\n", machdep->machspec->vmemmap_vaddr);
fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE());
+ if (machdep->machspec->irq_stack_size) {
+ fprintf(fp, " IRQ STACK SIZE: %ld\n",
+ machdep->machspec->irq_stack_size);
+ fprintf(fp, " IRQ STACKS:\n");
+ for (i = 0; i < kt->cpus; i++) {
+ pad = (i < 10) ? 3 : (i < 100) ? 2 : (i < 1000) ? 1 : 0;
+ fprintf(fp, "%s CPU %d: %lx\n", space(pad), i,
+ machdep->machspec->irq_stacks[i]);
+ }
+ }
+ if (machdep->machspec->overflow_stack_size) {
+ fprintf(fp, "OVERFLOW STACK SIZE: %ld\n",
+ machdep->machspec->overflow_stack_size);
+ fprintf(fp, " OVERFLOW STACKS:\n");
+ for (i = 0; i < kt->cpus; i++) {
+ pad = (i < 10) ? 3 : (i < 100) ? 2 : (i < 1000) ? 1 : 0;
+ fprintf(fp, "%s CPU %d: %lx\n", space(pad), i,
+ machdep->machspec->overflow_stacks[i]);
+ }
+ }
}
static void
--
2.34.1
10 hours, 42 minutes