Linux内核中调用栈不带偏移的接口

在Linux内核中,获取调用栈信息(stack trace)时,如果只需要函数地址不带偏移量(即不显示函数内部的指令偏移),可以通过以下接口实现:

核心接口

  1. stack_trace_save()
    这是最直接的接口,用于捕获当前调用栈的纯地址列表(不带偏移)。

    1
    2
    3
    unsigned int stack_trace_save(unsigned long *store, 
    unsigned int size,
    unsigned int skipnr);
    • 参数
      • store:存储地址的数组
      • size:数组大小
      • skipnr:跳过的栈帧数(例如设为 0 捕获所有帧)
    • 返回值:实际保存的地址数量
    • 特点
      保存的地址是调用点的返回地址(即函数内部的指令地址),不包含符号名或偏移量
  2. 手动处理 struct stack_trace
    通过 save_stack_trace() 获取原始地址,再自行打印:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    struct stack_trace trace;
    unsigned long entries[16]; // 存储地址的数组

    trace.nr_entries = 0;
    trace.max_entries = ARRAY_SIZE(entries);
    trace.entries = entries;
    trace.skip = 0; // 跳过帧数

    save_stack_trace(&trace); // 捕获栈

    // 打印纯地址(不带偏移)
    for (int i = 0; i < trace.nr_entries; i++) {
    printk("%pS\n", (void *)trace.entries[i]); // %pS 会带符号+偏移,需避免!
    printk("%lx\n", trace.entries[i]); // 直接打印地址(无符号/偏移)
    }

关键说明

  • 不带偏移的本质
    上述接口返回的地址是原始的指令指针(Instruction Pointer)值,即函数内部的调用点地址。若需函数入口地址(无偏移),需额外使用 kallsyms_lookup() 解析(但通常不需要)。
  • 避免符号解析
    不要使用 %pS(会打印 函数名+偏移),而是用 %lx 直接输出地址值。
  • 依赖配置
    需启用内核选项:
    • CONFIG_STACKTRACE
    • CONFIG_FRAME_POINTER(或架构相关的栈展开支持)

示例代码(捕获纯地址)

1
2
3
4
5
6
7
8
9
10
11
12
#include <linux/stacktrace.h>

void dump_pure_addresses(void) {
unsigned long entries[16];
unsigned int nr_entries;

nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 0);

for (int i = 0; i < nr_entries; i++) {
printk(KERN_INFO "Address: %#lx\n", entries[i]); // 输出纯地址
}
}

输出示例

1
2
3
4
Address: 0xffffffff810a3d12
Address: 0xffffffff81180145
Address: 0xffffffff811d2fc0
...

其他接口(不推荐)

  • dump_stack()会带符号+偏移(通过 %pB/%pS 打印),不符合需求。
  • print_stack_trace():同样会解析符号和偏移。

总结:使用 stack_trace_save() 直接获取原始地址数组,或通过 save_stack_trace() + 遍历数组 是获取无偏移调用栈的标准方法。