Отладка на уровне ядра
Модули ядра (формат ELF, расширение ko, лежат в /lib/modules/) — фрагменты кода, которые могут загружаться и выгружаться из ядра по требованию. Большинство драйверов ПК реализованы в виде модулей. Для встраевоемых устройств большинство драйверов обычно статически компилируются в ядро Linux. По умолчанию модули ядра находятся в каталоге. Модуль ядра использует память ядра без механизмов защиты.
Модуль должен содержать init_module() (вызывается insmod) и cleanup_module() (вызывается rmmod).
lsmod– список загруженных модулей.modinfo– получить информацию о модуле.modprobe– 'smart' загрузка/выгрузка модулей с зависимостями.
obj-m += hellomod.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make ic /lib/modules/$(shell uname -r)/build M=$(PWD) clean
cd kernel_debugging
make all
sudo insmod hellomod.ko
lsmod | grep hellomod
Отладка
Для отладки полезен printk. Например printk(KERN_INFO "Message: %s\n", arg); содержит KERN_INFO— это log level (он связан со строкой формата, log level не является отдельным аргументом). Все сообщения, напечатанные с помощью printk(), пересылаются в специальный кольцевой буфер ядра. Для доступа к сообщениям можно использовать команду dmesg
Для проверки текущего значения console_log_level:
$ cat /proc/sys/kernel/printk
4 4 1 7
Результат текущий (4), по умолчанию(4), минимальный(1) и boot-time-default(8).
Для изменения текущего console_loglevel можно изменить /proc/sys/kernel/printk. Для печати всех сообщений на консоль:
echo 8 > /proc/sys/kernel/printk
Чтобы изменить console_loglevel для печати с KERN_WARNING (4) и более для консоли:
dmesg –n 5
Дополнительно можно исползовать чуть более гибкое DebugFS с макросом pr_devel(). Важно пересобрать ядро linux с DEBUG и CONFIG_DYNAMIC_DEBUG (дополнительно).
Если ARM процессор CoreSight, то через JTag/SWD (тот же STLink) можно управления потоком выполнения.
KGDB
В linux нельзя отлаживать ядро на нем же, но можно на другом хосте.
Настроить .config:
- Set CONFIG_DEBUG_INFO to y
- Set CONFIG_KGDB to y
- Comment out CONFIG_RANDOMIZE_BASE
- Set CONFIG_GDB_SCRIPTS to y
- Set CONFIG_KGDB_SERIAL_CONSOLE to y
и собрать ядро.
Выполнение задания
Убедиться, что включены в конфигурации следующие поля:
- CONFIG_DEBUG_INFO=y
- CONFIG_KGDB=y
- CONFIG_RANDOMIZE_BASE=y
- CONFIG_GDB_SCRIPTS=y
- CONFIG_KGDB_SERIAL_CONSOLE=y
Собираем ОС и запускаем:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image -j$(nproc)
cd ..
qemu-system-aarch64 \
-M virt -cpu cortex-a57 -m 512M \
-kernel linux-v6.18.8/arch/arm64/boot/Image \
-initrd initramfs.cpio.gz \
-append "console=ttyAMA0 rw nokaslr" -nographic \
-s -S
qemu запущен с флагом
S s= приостанавливает загрузку и запускает сервер gdb (порт по умолчанию — 1234).
В новой консоли:
cd build/linux-v6.18.8
gdb-multiarch vmlinux
set arch aarch64
target remote :1234
Дальше начинается пошаговая стратегия, где можно отследить каждый шаг ОС:
b start_kernel
c
list
n
(gdb) set arch aarch64
The target architecture is set to "aarch64".
(gdb) target remote :1234
Remote debugging using :1234
0x0000000040000000 in ?? ()
(gdb) b start_kernel
Breakpoint 1 at 0xffff800081e60930: file init/main.c, line 915.
(gdb) c
Continuing.
Breakpoint 1, start_kernel () at init/main.c:915
915 set_task_stack_end_magic(&init_task);
(gdb) list
910 void start_kernel(void)
911 {
912 char *command_line;
913 char *after_dashes;
914
915 set_task_stack_end_magic(&init_task);
916 smp_setup_processor_id();
917 debug_objects_early_init();
918 init_vmlinux_build_id();