最准的符号地址查看方式是读取/proc/kallsyms,但默认全为0因kptr_restrict=2启用地址掩码;需设为0才能显示真实地址,并用grep " T "等筛选导出函数。

直接看 /proc/kallsyms 是最准的,但它默认地址全为 0000000000000000——这不是没符号,是内核把地址掩码了。
为什么 cat /proc/kallsyms 全是 0?
从 Linux 2.6.38 起,默认 kptr_restrict=2,所有符号地址强制显示为零,root 也一样。这不是权限问题,是内核主动保护机制。
- 临时放开:运行
echo 0 > /proc/sys/kernel/kptr_restrict(需 root) - 永久生效:在
/etc/sysctl.conf加kernel.kptr_restrict = 0,再执行sysctl -p -
kptr_restrict=1时,只有带CAP_SYSLOG能力的进程(比如syslogd或某些调试工具)能看到真实地址
怎么精准过滤出导出的函数(不是变量、不是静态符号)?
导出函数在 /proc/kallsyms 中类型标识是 T(全局文本段)或 t(局部文本段),但模块导出的函数基本都是 T;而变量常用 D/d,未定义符号是 U。
- 查某个函数名(比如
do_sys_open):grep " T " /proc/kallsyms | grep -i "do_sys_open" - 查整个模块(比如
ip6table_filter):grep "\[ip6table_filter\]" /proc/kallsyms | grep " T " - 忽略前缀差异(如
__do_sys_ioctl和do_sys_ioctl):grep -E "(do_sys|__do_sys)_ioctl" /proc/kallsyms - 只输出地址和符号名:
grep " T " /proc/kallsyms | awk '{print $1, $3}'
nm 查 .ko 文件里的导出函数,比 /proc/kallsyms 更早一步
模块还没加载时,/proc/kallsyms 里根本看不到它;这时得靠 nm 直接解析文件。注意:nm 看的是编译后符号表,不反映运行时地址,但能确认是否真导出了函数。
- 列出所有全局函数:
nm -C /lib/modules/$(uname -r)/kernel/net/ipv6/ip6table_filter.ko | grep " T " -
-C参数必须加,否则 C++ 风格符号(如模板函数)会是乱码 - 输出中
T表示全局函数,t是局部函数(不会被其他模块调用),U是未定义引用(依赖外部模块) - 如果想确认是否真正“导出”,还得配合
modinfo看exported symbols字段:modinfo /path/to/module.ko | grep -A5 "exported symbols"
kallsyms_lookup_name() 在模块里调用失败?别硬上
这个函数自 5.7 起不再导出,5.12 起彻底移出导出列表;RHEL 8.8+、Ubuntu 22.04+ 等主流发行版默认禁用。即使编译通过,运行时返回 NULL 很常见。
- 必须包含:
#include <linux></linux>(不是<linux></linux>) - 调用前必须判空:
unsigned long addr = kallsyms_lookup_name("my_func"); if (!addr) return -EINVAL; - 它不保证成功——返回
NULL不代表符号不存在,只说明接口被内核配置屏蔽或CONFIG_KALLSYMS未启用 - 替代方案:改用
/proc/kallsyms+ 用户态解析(比如open/read/grep),或改用kprobe_on_func_entry(仅限探测场景)
真正麻烦的不是找不到符号,而是混淆了 /proc/kallsyms(动态、含模块、受 kptr_restrict 控制)和 System.map(静态、不含模块、地址固定但可能被 KASLR 偏移)。调试 panic 日志里的地址时,错用 System.map 就永远对不上。


















