05、Linux ELF文件格式

危险在于:ELF 文件格式很复杂,了解其结构需要深入了解。

不信:简单来说,ELF文件是Linux下标准的可执行文件格式。

不要这样做:直接使用file命令判断文件类型,使用readlf查看ELF结构。

Linux内核调试篇——获取内核函数地址的四种方法(一文解决)

哎呀,我以前也这么做过,这让我头疼极了。
Linux内核调试真的不是开玩笑,特别是当你需要找到内核函数地址时。
让我告诉你我当时遇到的困难。

我记得有一年,我使用某公司定制的Linux内核构建了一个嵌入式系统。
当时调试死锁问题真是让我很沮丧。
最后发现,找到函数地址才是关键。

System.map是编译内核时生成的。
在我的记忆中,有一次我编译内核时找不到System.map,我就急得头晕。
后来我想起来我必须在编译选项中添加一些东西,否则它不会生成。
使用System.map,可以通过grep '函数名'System.map找到地址。
例如,当我搜索 do_fork 时,我会 grep 'do_fork' System.map。
当我看到输出是 c01 05 02 0Tdo_fork 时,我知道地址是 c01 05 02 0。
这个方法很棒,但是前提是你必须有编译好的内核和对应的System.map。

vmlinux是一个内核映像文件。
有一次,找不到内核镜像文件,系统报错。
后来我手动编译了内核,得到了vmlinux。
您可以使用 nm vmlinux |您可以使用 grep 'do_fork' 或 objdump -d vmlinux | grep 'do_fork',甚至 readelf -s vmlinux | grep 'do_fork'。
所有这些命令都可以找到函数地址。
我用 nm 找到了 c01 05 02 0Tdo_fork。
vmlinux的优点是可以使用各种工具对其进行反汇编,而且资料也比较齐全。

/proc/kallsyms 这个东西是运行时生成的。
我正在旧系统上进行调试。
系统很旧,编译的内核不支持CONFIG_KALLSYMS。
我很着急,后来发现内核编译的时候还得加这个东西。
后来在新系统上,我在编译内核的时候特意加上了这个选项,然后发现cat /proc/kallsyms | grep 'do_fork' 可以直接看到地址,如ffffffff8 1 0b5 7 b0Tdo_fork。
这个方法很好,但是条件是内核必须支持。

直接在代码中使用函数即可找到内核接口。
我曾经写过一个内核模块,需要动态查找某个函数的地址。
我使用了 kallsyms_lookup_name('函数名称')。
例如,如果我搜索 do_fork,我使用 kallsyms_lookup_name("do_fork"),然后返回地址。
这个方法很棒,但是前提是内核也必须支持CONFIG_KALLSYMS。

一般来说,这些方法都有其自身的缺点。
System.map很好,但它必须有编译好的内核; vmlinux可以反汇编,但必须包含镜像文件; /proc/kallsyms 不错,但是必须内核支持;内核接口可以动态找到,但必须得到内核的支持。
您可以根据自己的具体情况进行选择。
当时我尝试了很多方法来理解这些事情。