]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf tools: Fix module symbol resolution for non-zero .text sh_addr
authorChuck Lever <chuck.lever@oracle.com>
Mon, 23 Mar 2026 15:58:04 +0000 (11:58 -0400)
committerNamhyung Kim <namhyung@kernel.org>
Thu, 2 Apr 2026 19:51:10 +0000 (12:51 -0700)
When perf resolves symbols from kernel module ELF files (ET_REL),
it converts symbol addresses to file offsets so that sample IPs
can be matched to the correct symbol. The conversion adjusts each
symbol's st_value:

  sym->st_value -= shdr->sh_addr - shdr->sh_offset;

For vmlinux (ET_EXEC), st_value is a virtual address and sh_addr
is the section's virtual base, so subtracting sh_addr and adding
sh_offset correctly yields a file offset.

For kernel modules (ET_REL), st_value is a section-relative
offset. The module loader ignores sh_addr entirely and places
symbols at module_base + st_value. Converting to file offset
requires only adding sh_offset; subtracting sh_addr introduces an
error equal to sh_addr bytes.

When .text has sh_addr == 0 -- the historical norm for simple
modules -- both formulas produce the same result and the bug is
latent. As modules gain more metadata sections before .text (.note,
.static_call.text, etc.), the linker assigns .text a non-zero
sh_addr, exposing the defect. For example, nfsd.ko on this kernel
has sh_addr=0xa80, kvm-intel.ko has sh_addr=0x1e90.

The effect is that all .text symbols in affected modules
shift by sh_addr bytes relative to sample IPs, causing perf
report to attribute samples to incorrect, nearby symbols. This
was observed as 13% of LLC-load-miss samples misattributed
to nfsd_file_get_dio_attrs when the actual hot function was
nfsd_cache_lookup, approximately 0xa80 bytes away in the symbol
table.

Use the existing dso__rel() flag (already set for ET_REL modules)
to select the correct adjustment: add sh_offset for ET_REL,
subtract (sh_addr - sh_offset) for ET_EXEC/ET_DYN.

Fixes: 0131c4ec794a ("perf tools: Make it possible to read object code from kernel modules")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Tested-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/util/symbol-elf.c

index 3cd4e5a03cc5d6f668ae2ea05dd782d8e97d44b8..7afa8a1171396b1770dbf6b78e7a32bc172440d7 100644 (file)
@@ -1354,8 +1354,12 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
        char dso_name[PATH_MAX];
 
        /* Adjust symbol to map to file offset */
-       if (adjust_kernel_syms)
-               sym->st_value -= shdr->sh_addr - shdr->sh_offset;
+       if (adjust_kernel_syms) {
+               if (dso__rel(dso))
+                       sym->st_value += shdr->sh_offset;
+               else
+                       sym->st_value -= shdr->sh_addr - shdr->sh_offset;
+       }
 
        if (strcmp(section_name, (dso__short_name(curr_dso) + dso__short_name_len(dso))) == 0)
                return 0;