linux内核$(kallsyms.o)详解续篇 --- 内核符号表的生成和查找过程

嘿,让我告诉你内核符号表。
我在鼓捣Linux内核的时候遇到了很多麻烦,而符号表绝对是我无法避免的。

记得一年前,我在虚拟机中调试服务器内核时,遇到了oops,卡在了某个地址0xc0008 1 2 8 屏幕上出现一堆扭曲的字符,我不知道它们是什么意思。
我在System.map中搜索了这个地址,发现它在__create_page_tables和__enable_mmu之间。
但System.map非常大,有几百MB,直接浏览进去非常费力。
当时我就想,这个内核是怎么检查符号的呢?
后来才知道Linux内核有自己的能力。
它愚蠢地不会每次都读取 System.map 或 /proc/kallsyms 。
当内核编译时,符号表被包含并放置在可执行文件 vmlinux.h 中。
具体来说,它被放置在名为 /tmp_vmlinux2 .o 的目录中。
该符号表是嵌入的,因此查找速度非常快。
与system.map是用于开发的不同,/proc/kallsyms是在运行时生成的,方便检查用户空间,但内核本身使用编译后的。

内核如何使用这个嵌入的符号表?在幕后有一个名为 Scripts/Calsims 的工具。
当编译内核时,它会运行它,扫描内核源代码中的符号,并生成这些嵌入的数据结构。
我记得kallsyms.c文件中有几个关键数组,比如kallsyms_addresses,它存储所有符号的地址,kallsyms_names,它存储名称。
,以及kallsyms_token_table和kallsyms_token_index,用于压缩字符串并节省内存。
重复出现的名字,比如函数名等,会被转换成token,搜索时可以倒过来找到。
还有 kallsyms_num_syms 来记录总共有多少个符号,以及 kallsyms_markers 对符号进行分组以便于搜索。

当时我检查了oops地址0xc0008 1 2 8 ,我使用了这个嵌入的符号表。
我直接使用内核提供的__print_symbol函数并传递地址,它会帮我打印符号名称,即__enable_mmu。
这比翻阅 System.map 要快得多。
内核使用二分查找来检查符号本身,效率很高。

我们来谈谈/proc/kallsyms,那东西是做什么的?它主要用于方便用户空间调试工具,如 systemtap 和 ftrace。
它包含 System.map 通常不包含的模块符号。
我在运行CentOS 6 的服务器上尝试过,/proc/kallsyms文件的大小可以有几MB。
但内核本身启动的时候,主要还是依赖编译后的符号表。

对于内核模块,它们是动态加载的。
当时写了一个简单的模块,用insmod安装了。
内核启动后,其符号表位于structmodule结构体中。
为了检查模块的符号,内核将使用 kallsyms_lookup() 函数,然后内核中的 get_ksymbol() 函数将在模块的 structmodule 中查找它。
这个搜索进程内核其符号搜索逻辑有所不同,但原理是相同的,通过两个地址查找名称。

总结一下,内核符号表有两种:一种是编译到vmlinux中,嵌入式的,内核本身最常用;另一种是编译到vmlinux中的,嵌入式的,最常用的是内核本身。
第二个是/proc/kallsyms,它是在运行时生成的,主要用于用户空间。
System.map是一个转换,在编译期间生成,用于显示内核内存布局。
我当时所经历的危险是我显然不知道的。
我认为内核应该依赖System.map来检查符号。
结果花了很长时间才找到内核函数__print_symbol。
请记住,内核有它自己的绝活,所以你不必那么费力地看文件。

linux显示二进制文件的符号表是什么-nm 命令使用与实例

嗨,关于 nm 命令,它确实是一个强大的工具。
我在Linux系统上调试程序时经常使用它。

例如,上周一位客户问我如何查看文件符号表。
我直接使用nm命令并添加了-a选项,这样我就可以看到所有的符号。
例如,我想查看 ls 命令的符号表,所以我运行 nm -a /bin/ls,然后我可以看到诸如“T_init”和“W_ITM_deregisterTMCloneTable”之类的符号。

有时,当您链接程序时,您会遇到诸如未指定的引用错误之类的问题。
此时,使用 nm 命令的 -u 选项查看未指定的符号就特别有用。
例如, nm -u/bin/ls 列出必须链接到其他库的未定义符号。

C++ 符号系统非常常见,因为编译器为 C++ 函数和变量生成一些丑陋的名称。
我可以使用 nm 命令的 -C 选项看到它们的原始外观。
例如,要查看 C++ 程序的符号表,我使用 nm -C my_program,这样我就可以看到“main”和“std::cout”等名称。

其他时候,如果你想比较一个程序的两个版本之间的差异,可以使用 nm 创建一个符号列表,然后使用 diff 命令进行比较。
这对我来说是一个非常有效的方法。

当然,需要注意的是,如果将文件从符号表中删除,nm可能无法显示数据。
还需要在相应的架构工具链中使用 nm 查看不同架构的二进制文件。

总体来说nm是一个非常实用的工具。
控制它对于Linux的发展会有很大的帮助。
你呢,使用 nm 有什么特别的经历吗?反正你知道了,如果你善于使用它,那么分析二进制文件会更方便。

linux nm命令

nm命令是Linux中分析二进制文件的强大工具。
你主要做什么?查看二进制文件中的符号表,例如可执行文件、库文件等。

任务简单地说: 1 . 解析二进制文件:可以查看预编译、编译和汇编文件。
2 、库文件分析:静态库和动态库都可以分析。
3 .可执行文件:也可以分析多个文件链接的可执行文件。

用途:
查看二进制文件的内容,因为直接查看二进制文件是空的,nm会帮我们解码。

常用参数:
A或o或--printfilename:显示带有符号的文件。

a 或 --debugsyms:显示所有符号,包括调试符号。

B:以BSD格式显示令牌信息。

C 或 --demangle[=style]:指定 C++ 文件所需的低级符号名称。

D 或 --dynamic:显示动态库的动态符号。

fformat或--format=format:指定显示格式,默认bsd,可选sysv和posix。

g 或 --仅显示外部符号。

h 或 --help:显示帮助信息。

n 或 v 或 --numericsort:按地址对符号进行排序。

p 或 --nosort:显示未排序的内容。

P 或 -- 可移植性:使用 POSIX.2 标准显示令牌信息。

V 或 - 版本:查看 nm 版本信息。

--only:仅显示指定的符号。

如何使用?
任何人直接进入终端并阅读详细指南即可了解其使用方法和参数。

你自己看看,nm命令非常有用,特别是在调试和开发时。