找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2788

积分

0

好友

372

主题
发表于 5 天前 | 查看: 22| 回复: 0

在前两篇文章中,我们讨论了编译Linux内核vmlinux,本文就围绕Linux内核符号表展开,简单记录其核心概念。内核符号表在内核编译后对应文件System.map,它伴随编译过程产生,并在vmlinux压缩时嵌入。本文将聚焦三个核心问题:符号表是什么、怎么来的,以及用来做什么。

什么是符号表?

符号表本质上是一个列表,记录了Linux内核中的符号(即变量和函数名称)及其在内存中的虚拟地址。你可能马上会联想到 /proc/kallsyms 下显示的映射关系,它与System.map有何区别?

简单来说,System.map是在编译阶段生成的静态内核符号表,而 /proc/kallsyms 则是内核启动后动态生成的运行时符号表。动态符号表会随内核模块的加载而更新,因此开发者插入模块后,其export的函数也能在 /proc/kallsyms 中查到映射。

符号表System.map是怎样生成的?

符号表的生成源于编译过程。具体来说,它由kernel目录下的 scripts/kallsyms 工具产生。我们不深究嵌入细节,先看看编译期间的生成流程。

根Makefile会执行 scripts/Makefile.vmlinux,进而调用脚本 scripts/link-vmlinux.sh。在这个脚本中,mksysmap 函数定义如下:

mksysmap()
{
  info NM ${2}
  ${CONFIG_SHELL} "${srctree}/scripts/mksysmap" ${1} ${2}
}

编译时通过命令 mksysmap vmlinux System.map 触发。继续查看 scripts/mksysmap 脚本,其内容实为调用NM工具:

$NM -n $1 | grep -v  \
 -e ' [aNUw] '  \
 -e ' \$'  \
 -e ' \.L'  \
 -e ' __crc_'  \
 -e ' __kstrtab_'  \
 -e ' __kstrtabns_'  \
 -e ' L0$'  \
> $2

由此可见,System.map的实际生成工具是NM。其具体实现在同目录的 scripts/kallsyms.c 中,涉及三个关键变量:

  • kallsyms_addresses:按地址升序排列的符号地址数组。
  • kallsyms_num_syms:符号数量。
  • kallsyms_names:与地址一一对应的符号名称数组。

符号表用来做什么?

a. 内核模块开发

在内核中,常看到通过 EXPORT_SYMBOL 导出的函数。编写内核模块时,我们可用 extern 声明这些外部符号。模块加载时,内核会查找符号表中对应地址,并填充到模块符号表,使模块能调用这些函数。反之,模块自身导出的函数也可供其他模块使用。

示例代码:

//kernel:
void function_xxx(void)
{
   ...
}
EXPORT_SYMBOL(function_xxx);

//编写ko:
extern void function_xxx(void);

b. 内核崩溃调试

内核崩溃(panic)时,调用栈会打印一系列地址。借助符号表,这些地址可转换为函数名称,极大便利问题定位。类似地,dump_stack 也依赖符号表查询来打印函数名。

c. 函数Hook与高级调试

利用符号表的地址映射,我们可以根据函数名查询地址,实现Hook操作。例如,kprobe 在钩子函数时正是通过查询符号表来获取目标地址,从而支持调试或新功能开发。

更多操作系统与底层技术讨论,欢迎访问云栈社区。




上一篇:深入解析Linux内核模块:原理、build-in与动态加载机制详解
下一篇:Linux内核核心解析:vmlinux从编译到加载的关键概念与ELF文件分析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-9 16:26 , Processed in 0.659350 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表