Linux kernel debug
Содержание
Использование только отладчика GDB
Запуск отладчика для работы с ядром почти ни чем не отличается от отладки выполняющегося процесса.
$ sudo gdb vmlinux /proc/kcore
Можно пользоваться практически всеми командами программы GDB для чтения информации. Если ядро было собрано с отладочной информацией, то GDB сможет выдавать больше информации (разыменовывать указатели и выводить дампы структур данных). Нельзя изменять данные ядра. Нет возможности пошагово выполнять код ядра, или устанавливать точки остановки.
Отладка ядра Linux с использованием отладчика GDB и эмулятора аппаратного обеспечения QEMU
Подготовка ядра
Ядро необходимо собрать с отладочной информацией:
$ cd /usr/src/linux $ make menuconfig
Kernel hacking ---> [*] Compile the kernel with debug info [*] Compile the kernel with frame pointers
Или в файле .config изменить строки "# CONFIG_DEBUG_KERNEL is not set" и "# CONFIG_FRAME_POINTER is not set" на "CONFIG_DEBUG_KERNEL=y" и "CONFIG_FRAME_POINTER=y" соответственно.
Запуск QEMU
После того как ядро будет собрано можно ( в том случае если не требуется отлаживать модули ) сразу же приступать к запуску QEMU без подготовки образа диска.
#Запускаем QEMU с ядром, которое собираемся отлаживать. #Эта команда предназначена для того чтобы отлаживать ядро до того, как оно начнёт обращаться к диску. #Для неё не требуется образа диска с установленной системой. $ qemu -s -S -hda /dev/zero -kernel /usr/src/linux/arch/x86/boot/bzImage &
#Для данной команды требуется образ диска с установленной системой. #В параметре root необходимо указать устройство с корневым разделом. #Команда позволяет отлаживать любую функциональность ядра, за исключением модулей. $ qemu -s -S -hda hda.img -kernel /usr/src/linux/arch/x86/boot/bzImage -append root=/dev/sda &
Аргумент -s необходим для того, чтобы эмулятор ожидал подключения отладчика к порту 1234. Сменить порт можно аргументом [-p порт].
Аргумент -S необходим для того, чтобы QEMU не запускал процессор при запуске эмулятора ( это необходимо для того, что бы ядро можно было отлаживать начиная со start_kernel ).
В том случае, если требуется отладка модулей то необходимо сначала скопировать модули и ядро на образ диска QEMU. Далее предполагается что образ диска в формате raw.
Примечание:
Преобразовать из формата, например qcow, в raw можно следующей командой:
#qemu-img convert filename [-O output_format ] output_filename $ qemu-img convert qemu-disk-image.qcow -O raw qemu-disk-image.raw
Из других форматов преобразуется аналогичным образом.
Монтирование образа диска:
$ sudo fdisk -ul hda.img Диск hda.img: 0 МБ, 0 байт 255 heads, 63 sectors/track, 0 cylinders, всего 0 секторов Units = секторы of 1 * 512 = 512 bytes Disk identifier: 0x00000000 Устр-во Загр Начало Конец Блоки Id Система hda.img1 * 63 64259 32098+ 83 Linux hda.img2 64260 562274 249007+ 82 Linux своп / Solaris hda.img3 562275 7164989 3301357+ 83 Linux $ sudo mkdir /mnt/guest #Аргумент offset рассчитывается следующим образом: начало * Units #63 * 512 = 32256 #562275 * 512 = 287884800 $ sudo mount -o loop,offset=287884800 hda.img /mnt/guest/ $ sudo mount -o loop,offset=32256 hda.img /mnt/guest/boot/
После этого нужно установить ядро и модули на образ диска и прописать загрузчику параметры, необходимые для загрузки отлаживаемого ядра. Для того, чтобы установить ядро нужно собрать пакет одной из команд:
$ make binrpm-pkg $ make deb-pkg $ make tarbz2-pkg $ make targz-pkg
Далее необходимо скопировать пакет на образ и уже после запуска виртуальной машины с помощью пакетного менеджера установить ядро.
Пример для rpm и дистрибутива Fedora 9:
$ cp /usr/src/redhat/RPMS/i386/kernel-2.6.29-2.i386.rpm /mnt/guest/root/ $ sudo mount -t proc none /mnt/guest/proc $ sudo mount -o bind /dev /mnt/guest/dev $ sudo chroot /mnt/guest /bin/bash $ rpm -i --force root/kernel-2.6.29-2.i386.rpm $ sudo umount /mnt/guest/proc /mnt/guest/dev $ exit
Примечание:
Иногда установить пакет не получается, тогда можно сделать это вручную.
#Установка откомпилированных модулей в созданную директорию $ sudo make modules_install INSTALL_MOD_PATH=/mnt/guest/ #Копирование файла Sysmap и ядра $ sudo cp System.map /mnt/guest/root $ sudo cp arch/i386/boot/bzImage /mnt/guest/root $ sudo mount -t proc none /mnt/guest/proc $ sudo mount -o bind /dev /mnt/guest/dev $ sudo chroot /mnt/guest /bin/bash #Установка ядра и настройка загрузчика #installkernel <version> <image> <System.map> $ /sbin/installkernel 2.6.29 /root/bzImage /root/System.map $ sudo umount /mnt/guest/proc /mnt/guest/dev $ exit
Если скрипта installkernel нет, то необходимо скопировать ядро и настроить загрузчик самостоятельно информацию о том, как это сделать можно найти, например, здесь:
Grub: http://en.gentoo-wiki.com/wiki/Quick_GRUB
Lilo: http://www.acm.uiuc.edu/workshops/linux_install/lilo.html
#Отмонтируем образ и запускаем QEMU $ sudo umount /mnt/guest/boot /mnt/guest $ qemu -s -S -hda hda.img &
Запуск GDB
# Запускаем gdb на основной машине и подключаемся к порту 1234 $ gdb (gdb) target remote localhost:1234 # Подключаем образ ядра (должен совпадать с отлаживаемым ядром) (gdb) file vmlinux (gdb) break start_kernel
Файл vmlinux — это декомпрессированный исполняемый образ ядра, который хранится в корне каталога исходных кодов, где выполнялась сборка работающего ядра. Сжатые файлы zImage, или bzImage использовать нельзя.
Чтобы отлаживать модуль необходимо импортировать таблицу имён модуля в GDB и указать адрес в памяти, куда он был загружен.
Загрузка модуля:
# На виртуальной машине загружаем модуль $ sudo modprobe lg # Узнать адреса, куда он был загружен $ cat /sys/module/lg/sections/.text 0xe0baa000 $ cat /sys/module/lg/sections/.data 0xe0baec50 $ cat /sys/module/lg/sections/.bss 0xe0baf20c # На основной машине. Загрузка модуля для отладки с расширением *.o, а не *.ko. (gdb) add-symbol-file /usr/src/linux/drivers/lguest/lg.o 0xe0baa000 -s .data 0xe0baec50 \ -s .bss 0xe0baf20c add symbol table from file "/usr/src/linux/drivers/lguest/lg.o" at .text_addr = 0xe0baa000 .data_addr = 0xe0baec50 .bss_addr = 0xe0baf20c (y or n) y Reading symbols from /usr/src/linux/drivers/lguest/lg.o...done. (gdb) print lguest_lock $1 = {count = {counter = 1}, wait_lock = {raw_lock = {slock = 0}}, wait_list = {next = 0xe0baec58, prev = 0xe0baec58}}