Linux 内核符号地址在 /proc/kcore 和 /proc/kallsyms 之间不匹配

Posted

技术标签:

【中文标题】Linux 内核符号地址在 /proc/kcore 和 /proc/kallsyms 之间不匹配【英文标题】:Linux Kernel symbol addresses don't match between /proc/kcore and /proc/kallsyms 【发布时间】:2021-12-18 23:27:53 【问题描述】:

由于某种原因,我从 /proc/kallsyms 获得的地址与我使用 /proc/kcore 调试正在运行的内核获得的地址不同。

# uname -a
Linux localhost.localdomain 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

# rpm -ql kernel-debuginfo-3.10.0-862.14.4.el7.x86_64 | grep vmlinux
/usr/lib/debug/lib/modules/3.10.0-862.14.4.el7.x86_64/vmlinux

# gdb -q /usr/lib/debug/lib/modules/3.10.0-862.14.4.el7.x86_64/vmlinux /proc/kcore
Reading symbols from /usr/lib/debug/usr/lib/modules/3.10.0-862.14.4.el7.x86_64/vmlinux...done.
[New process 1]
Core was generated by `BOOT_IMAGE=/vmlinuz-3.10.0-862.14.4.el7.x86_64 root=/dev/mapper/centos-root ro c'.
#0  0x0000000000000000 in irq_stack_union ()

(gdb) p &init_task
$1 = (struct task_struct *) 0xffffffff81c16480 <init_task>
(gdb) quit

# grep "D init_task" /proc/kallsyms 
ffffffffa0a16480 D init_task

当然,这两个地址来自同一台机器,无需重新启动。

地址不应该匹配吗?为什么会发生这种转变?

0xffffffff81c16480

0xffffffffa0a16480

【问题讨论】:

我觉得这是Kernel Address Space Layout Randomization (KASLR)的效果。您可以使用-s /proc/kallsyms gdb 参数来使用实时内核符号。 AFAIK /proc/kcore 和 /proc/kallsyms 都应该使用真实(重新定位的)地址进行更新,因此它不应该是 KASLR 效应。 加上 /proc/kallsyms 不是 ELF 文件,因此它不适用于 -s。 你说得对,-s /proc/kallsyms 不起作用,因为它是错误的格式。您对正在更新的/proc/kcore/proc/kallsyms 部分正确,但请注意/proc/kcore 不包含符号表。为什么你认为它不是 KASLR 效应? 请注意,GDB 8.2 可以选择加载带偏移量的符号文件(但我认为只能为 GDB add-symbol-file 命令指定,不能在 GDB 调用命令行参数中指定) .一旦确定了 KASLR 偏移量(例如,通过比较来自 vmlinux 的符号与 /proc/kallsyms 中的符号),这对于调试实时内核可能很有用。 【参考方案1】:

我最终禁用了 KASLR,将 nokaslr 参数添加到内核命令行。

然而,即使 KASLR 被禁用,仍然需要修复符号偏移量。正如@Ian 在其中一个 cmets 中建议的那样,我使用了 gdb add-symbol-file 命令。使用文本地址的偏移量加载符号,如下:

# grep "D init_task" /proc/kallsyms
ffffffff81c16480 D init_task

# grep " _text" /proc/kallsyms
ffffffff81000000 T _text

# gdb -q -c /proc/kcore
(gdb) add-symbol-file /usr/lib/debug/lib/modules/3.10.0-862.14.4.el7.x86_64/vmlinux 0xffffffff81000000
add symbol table from file "/usr/lib/debug/lib/modules/3.10.0-862.14.4.el7.x86_64/vmlinux" at
    .text_addr = 0x81000000
(y or n) y
Reading symbols from /usr/lib/debug/usr/lib/modules/3.10.0-862.14.4.el7.x86_64/vmlinux...done.

(gdb) p &init_task
$1 = (struct task_struct *) 0xffffffff81c16480 <init_task>

它适用于 gdb 7.6.1

无论如何,知道如何启用 KASLR 将是一件很棒的事情。

【讨论】:

crash 实用程序(建立在 gdb 之上)可以处理启用了 kaslr 的内核。【参考方案2】:

请注意init_task 在 DATA 段中。

# grep -w init_task /proc/kallsyms
ffffffffbb0134c0 D init_task

所以首先你需要找到你内核数据段的起始地址,也就是/proc/kallsyms_sdata符号的地址。

# grep -w _sdata /proc/kallsyms
ffffffffbb000000 D _sdata

然后,如果您使用正确的数据段地址加载 vmlinux,即使启用了 KASLR,它也可以工作。

# gdb -q -c /proc/kcore
(gdb) add-symbol-file /usr/lib/debug/boot/vmlinux-4.12.14-122.37-default.debug -s .data 0xffffffffbb000000
add symbol table from file "/usr/lib/debug/boot/vmlinux-4.12.14-122.37-default.debug" at
        .data_addr = 0xffffffffbb000000
(y or n) y
Reading symbols from /usr/lib/debug/boot/vmlinux-4.12.14-122.37-default.debug...
(gdb) p &init_task
$1 = (struct task_struct *) 0xffffffffbb0134c0 <init_task>

还有 - 瞧,现在init_task 的地址是0xffffffffbb0134c0,这正是您在/proc/kallsyms 中看到的。

【讨论】:

以上是关于Linux 内核符号地址在 /proc/kcore 和 /proc/kallsyms 之间不匹配的主要内容,如果未能解决你的问题,请参考以下文章

/proc kcore 文件很大

如何查看linux的物理cpu信息

如何在 Linux 内核模块中找到合适的 DWARF 符号作为地址?

linux内核符号表kallsyms简介

怎么查看linux的内核符号表?

深入理解Linux内核 - 第二章 内存寻址 01