GDB+Qemu调试ARM64 linux内核

参考:

VSCode+GDB+Qemu调试ARM64 linux内核 - 知乎

如有侵权请联系删除。

安装编译工具链

由于Ubuntu是X86架构,为了编译arm64的文件,需要安装交叉编译工具链

sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev  build-essential git bison flex libssl-dev

制作根文件系统

linux的启动需要配合根文件系统,这里我们利用busybox来制作一个简单的根文件系统

编译busybox

wget  https://busybox.net/downloads/busybox-1.33.1.tar.bz2
tar -xjf busybox-1.33.1.tar.bz2
cd busybox-1.33.1

打开静态库编译选项

make menuconfig
Settings --->
 [*] Build static binary (no shared libs) 

指定编译工具

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

编译

make
make install

编译完成,在busybox目录下生成_install目录

定制文件系统

为了init进程能正常启动, 需要再额外进行一些配置

根目录添加etc、dev和lib目录

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install [1:02:17]
$ mkdir etc dev lib
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install [1:02:17]
$ ls
bin  dev  etc  lib  linuxrc  sbin  usr

在etc分别创建文件:

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:13]
$ cat profile
#!/bin/sh
export HOSTNAME=bryant
export USER=root
export HOME=/home
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:16]
$ cat inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:19]
$ cat fstab
#device  mount-point    type     options   dump   fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
kmod_mount /mnt 9p trans=virtio 0 0

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:26]
$ ls init.d
rcS

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:30]
$ cat init.d/rcS
mkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

这里对这几个文件做一点说明:

  1. busybox 作为linuxrc启动后, 会读取/etc/profile, 这里面设置了一些环境变量和shell的属性
  2. 根据/etc/fstab提供的挂载信息, 进行文件系统的挂载
  3. busybox 会从 /etc/inittab中读取sysinit并执行, 这里sysinit指向了/etc/init.d/rcS
  4. /etc/init.d/rcS 中 ,mdev -s 这条命令很重要, 它会扫描/sys目录,查找字符设备和块设备,并在/dev下mknod

dev目录:

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/dev [1:17:36]
$ sudo mknod console c 5 1
$ sudo mknod null c 1 3

这一步很重要, 没有console这个文件, 用户态的输出没法打印到串口上

lib目录:拷贝lib库,支持动态编译的应用程序运行

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/lib [1:18:43]
$ cp /usr/aarch64-linux-gnu/lib/*.so*  -a .

编译内核

配置内核

linux内核源码可以在github上直接下载。

内核版本:linux-5.16.12.tar.gz

下载地址:The Linux Kernel Archives

根据arch/arm64/configs/defconfig 文件生成.config

make defconfig ARCH=arm64

将下面的配置加入.config文件中,前面两项可以在.config中直接修改,后面两项没有,直接添加就好。

CONFIG_DEBUG_INFO=y 
CONFIG_INITRAMFS_SOURCE="./root"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0

CONFIG_DEBUG_INFO是为了方便调试

CONFIG_INITRAMFS_SOURCE是指定kernel ramdisk的位置,这样指定之后ramdisk会直接被编译到kernel 镜像中。

我们将之前制作好的根文件系统cp到root目录下:

# bryant @ ubuntu in ~/Downloads/linux-arm64 on git:main x [1:26:56]
$ cp -r ../busybox-1.33.1/_install root

执行编译

make ARCH=arm64 Image -j8  CROSS_COMPILE=aarch64-linux-gnu-

这里指定target为Image 会只编译kernel, 不会编译modules, 这样会增加编译速度

启动qemu

下载qemu

需要注意的,qemu最好源码编译, 用apt-get直接安装的qemu可能版本过低,导致无法启动arm64内核。笔者是使用4.2.1版本的qemu

apt-get install build-essential zlib1g-dev pkg-config libglib2.0-dev binutils-dev libboost-all-dev autoconf libtool libssl-dev libpixman-1-dev libpython-dev python-pip python-capstone virtualenv
wget https://download.qemu.org/qemu-4.2.1.tar.xz
tar xvJf qemu-4.2.1.tar.xz
cd qemu-4.2.1
./configure --target-list=x86_64-softmmu,x86_64-linux-user,arm-softmmu,arm-linux-user,aarch64-softmmu,aarch64-linux-user --enable-kvm
make 
sudo make install

编译完成之后,qemu在 /usr/local/bin目录下

$ /usr/local/bin/qemu-system-aarch64 --version
QEMU emulator version 4.2.1
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers

启动linux内核

qemu-system-aarch64 -m 512M -smp 4 -cpu cortex-a57 -machine virt -kernel arch/arm64/boot/Image -append "rdinit=/linuxrc nokaslr console=ttyAMA0 loglevel=8" -nographic -s

这里对于参数做一些解释:

-m 512M 内存为512M

-smp 4 4核

-cpu cortex-a57cpu 为cortex-a57

-kernel kernel镜像文件

-append传给kernel 的cmdline参数。其中rdinit指定了init进程;nokaslr 禁止内核起始地址随机化,这个很重要, 否则GDB调试可能有问题;console=ttyAMA0指定了串口,没有这一步就看不到linux的输出;

-nographic禁止图形输出

-s监听gdb端口, gdb程序可以通过1234这个端口连上来。

这里说明一下console=ttyAMA0是怎么生效的。

查看linux源码可知ttyAMA0对应的是AMBA_PL011这个驱动:

config SERIAL_AMBA_PL011_CONSOLE
    bool "Support for console on AMBA serial port"
    depends on SERIAL_AMBA_PL011=y
    select SERIAL_CORE_CONSOLE
    select SERIAL_EARLYCON
    help
      Say Y here if you wish to use an AMBA PrimeCell UART as the system
      console (the system console is the device which receives all kernel
      messages and warnings and which allows logins in single user mode).

      Even if you say Y here, the currently visible framebuffer console
      (/dev/tty0) will still be used as the system console by default, but
      you can alter that using a kernel command line option such as
      "console=ttyAMA0". (Try "man bootparam" or see the documentation of
      your boot loader (lilo or loadlin) about how to pass options to the
      kernel at boot time.)

AMBA_PL011是arm的一个标准串口设备, qemu 的输出就是模拟的这个串口。

在qemu的源码文件中,也可以看到PL011的相关文件:

# bryant @ ubuntu in ~/Downloads/qemu-4.2.1 [1:46:54]
$ find . -name "*pl011*"
./hw/char/pl011.c

成功启动Linux后, 串口打印如下:

[    2.347988] input: gpio-keys as /devices/platform/gpio-keys/input/input0
[    2.361616] ALSA device list:
[    2.363053]   No soundcards found.
[    2.370163] uart-pl011 9000000.pl011: no DMA platform data
[    2.429632] Freeing unused kernel memory: 18368K
[    2.431664] Run /linuxrc as init process
[    2.431870]   with arguments:
[    2.432023]     /linuxrc
[    2.432194]     nokaslr
[    2.432314]   with environment:
[    2.432452]     HOME=/
[    2.432563]     TERM=linux
[    2.555962] 9pnet_virtio: no channels available for device kmod_mount
mount: mounting kmod_mount on /mnt failed: No such file or directory
/etc/init.d/rcS: line 8: can't create /proc/sys/kernel/hotplug: nonexistent directory

Please press Enter to activate this console.
[root@bryant ]#
[root@bryant ]#

GDB安装

由于我之前已经安装好了,所以显示已经安装。

ubuntu@VM-4-3-ubuntu:~/work/smmu/linux-5.16.12$ sudo apt-get install gdb-multiarch
Reading package lists... Done
Building dependency tree
Reading state information... Done
gdb-multiarch is already the newest version (9.2-0ubuntu1~20.04.1).
The following packages were automatically installed and are no longer required:
  dblatex dblatex-doc dh-strip-nondeterminism docbook-utils dwz eatmydata fonts-gfs-baskerville fonts-gfs-porson libalgorithm-c3-perl libarchive-cpio-perl libarchive-zip-perl libb-hooks-endofscope-perl libb-hooks-op-check-perl
  libclass-c3-perl libclass-c3-xs-perl libclass-data-inheritable-perl libclass-method-modifiers-perl libclass-xsaccessor-perl libdata-optlist-perl libdebhelper-perl libdevel-callchecker-perl libdevel-caller-perl
  libdevel-globaldestruction-perl libdevel-lexalias-perl libdevel-stacktrace-perl libdist-checkconflicts-perl libdynaloader-functions-perl libeatmydata1 libemail-date-format-perl libeval-closure-perl libexception-class-perl
  libfile-homedir-perl libfile-stripnondeterminism-perl libipc-shareable-perl liblog-dispatch-perl liblog-log4perl-perl libmime-charset-perl libmime-lite-perl libmime-types-perl libmodule-implementation-perl libmodule-runtime-perl
  libmro-compat-perl libnamespace-autoclean-perl libnamespace-clean-perl libpackage-stash-perl libpackage-stash-xs-perl libpadwalker-perl libparams-classify-perl libparams-util-perl libparams-validationcompiler-perl
  libreadonly-perl libref-util-perl libref-util-xs-perl librole-tiny-perl libsgmls-perl libsombok3 libspecio-perl libsub-exporter-perl libsub-exporter-progressive-perl libsub-identify-perl libsub-install-perl libsub-override-perl
  libsub-quote-perl libunicode-linebreak-perl libvariable-magic-perl libxstring-perl libyaml-tiny-perl python3-jinja2 python3-json-pointer python3-jsonpatch python3-jsonschema python3-pyrsistent sgmlspl squashfs-tools texlive
  texlive-bibtex-extra texlive-extra-utils texlive-lang-greek texlive-science xmlto
Use 'sudo apt autoremove' to remove them.
0 upgraded, 0 newly installed, 0 to remove and 145 not upgraded.

GDB连接qemu调试 

在刚刚启动qemu的命令后面加上-S

qemu-system-aarch64 -m 512M -smp 4 -cpu cortex-a57 -machine virt -kernel arch/arm64/boot/Image -append "rdinit=/linuxrc nokaslr console=ttyAMA0 loglevel=8" -nographic -s -S

-S表示qemu虚拟机冻结cpu,直到gdb输入相应控制命令

此时qemu启动后无法继续往下运行,直到收到GDB的控制命令,此时卡住没有输出是正常现象。

 gdb调试结果

GDB+Qemu调试ARM64 linux内核_第1张图片

后面就可以根据需求修改对应的代码,并调试了,并进行追踪,学习愉快。

你可能感兴趣的:(linux,arm)