Адреса символов ядра Linux не совпадают между /proc/kcore и /proc/kallsyms

#debugging #linux-kernel #gdb

#отладка #linux-ядро #gdb

Вопрос:

По какой-то причине адреса, которые я получаю из /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 amp;init_task
$1 = (struct task_struct *) 0xffffffff81c16480 <init_task>
(gdb) quit

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

Оба адреса, конечно, с одной и той же машины без ее перезагрузки.

Разве адреса не должны совпадать? Почему этот сдвиг?

0xffffffff81c16480

0xffffffffa0a16480

Комментарии:

1. Я думаю, что это эффект рандомизации расположения адресного пространства ядра (KASLR) . Вы можете использовать параметры -s /proc/kallsyms gdb для использования живых символов ядра.

2. AFAIK оба /proc/kcore и /proc/ kallsyms должны быть обновлены с реальными (перемещенными) адресами, поэтому это не должно быть эффектом KASLR.

3. Плюс /proc/kallsyms не является файлом ELF, поэтому он не будет работать с -s .

4. Вы правы, это -s /proc/kallsyms не сработает, потому что это неправильный формат. Вы частично правы /proc/kcore и /proc/kallsyms обновляетесь, но обратите внимание, что /proc/kcore в нем нет таблицы символов. Как вы думаете, почему это не эффект KASLR?

5. Обратите внимание, что в GDB 8.2 есть опция загрузки файла символов со смещением (но я думаю, что это может быть указано только для команды GDB add-symbol-file , а не в параметрах командной строки вызова GDB). Это может быть полезно для отладки действующего ядра после определения смещения KASLR (например, путем сравнения символов из vmlinux с символами в /proc/kallsyms).

Ответ №1:

В итоге я отключил KASLR, добавив параметр nokaslr в командную строку ядра.

Однако даже при отключенном KASLR все равно необходимо исправить смещения символов. Как предложил @Ian в одном из комментариев, я использовал команду 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 amp;init_task
$1 = (struct task_struct *) 0xffffffff81c16480 <init_task>
  

Он работает с gdb 7.6.1.

В любом случае, было бы здорово узнать, как это сделать с включением KASLR.

Комментарии:

1. crash Утилита (построенная поверх gdb) может обрабатывать ядра с включенным kaslr.

Ответ №2:

Обратите внимание, что это init_task находится в сегменте ДАННЫХ.

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

Итак, сначала вам нужно найти начальный адрес сегмента данных вашего ядра, который является адресом _sdata символа в /proc/kallsyms .

 # 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 amp;init_task
$1 = (struct task_struct *) 0xffffffffbb0134c0 <init_task>
  

И — вуаля, теперь адрес init_task is 0xffffffffbb0134c0 , который является именно тем, что вы видите /proc/kallsyms .