摘要 本文主要通过 QEMU + Linux Kernel + Busybox 组合搭建 Linux Kernel 开发环境,
Qemu模拟ARM64运行环境
Busybox用于制作Linux运行的根文件系统
QEMU安装 1 2 3 4 5 6 7 8 9 wget https://download.qemu.org/qemu-9.0.1.tar.xz tar Jxvf qemu-9.0.1.tar.xz mkdir buildcd build../configure --prefix=~/opt/qemu --target-list=aarch64-linux-user arm-linux-user i386-linux-user riscv32-linux-user riscv64-linux-user x86_64-linux-user aarch64-softmmu arm-softmmu i386-softmmu riscv32-softmmu riscv64-softmmu x86_64-softmmu --enable-debug make
ARM64编译器安装 1 2 3 4 5 wget https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz tar Jxvf arm-gnu-toolchain-13.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
编译器路径加入环境变量
~/.bashrc中设置PATH
1 export PATH=$PATH :~/arm-gnu-toolchain-13.2.rel1-x86_64-aarch64-none-linux-gnu/
Busybox编译 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 wget https://www.busybox.net/downloads/busybox-1.36.1.tar.bz2 tar jxvf busybox-1.36.1.tar.bz2 cd busybox-1.36.1make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- menuconfig make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- install
_install 目录下文件结构如下
1 2 3 4 5 6 7 8 . ├── bin ├── linuxrc -> bin/busybox ├── sbin └── usr 4 directories, 1 file
基于 _install 制作 Linux kernel 启动根文件系统
1 2 3 mkdir initramfscd initramfscp -r ../busybox-1.36.1/_install/* .
然后执行如下脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 cd initramfsmkdir dev etc lib sys proc tmp var home root mntcd etcecho "#!/bin/sh" > profileecho "export HOSTNAME=qemu" >> profileecho "export USER=root" >> profileecho "export HOME=/home" >> profileecho "export PS1=\"[$USER @$HOSTNAME \W]\# \"" >> profileecho "PATH=/bin:/sbin:/usr/bin:/usr/sbin" >> profileecho "LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH " >> profileecho "export PATH LD_LIBRARY_PATH" >> profileecho "::sysinit:/etc/init.d/rcS" > inittabecho "::respawn:-/bin/sh" >> inittabecho "::askfirst:-/bin/sh" >> inittabecho "::ctrlaltdel:/bin/umount -a -r " >> inittabecho "#device mount-point type options dump fsck order" > fstabecho "proc /proc proc defaults 0 0" >> fstabecho "tmpfs /tmp tmpfs defaults 0 0" >> fstabecho "sysfs /sys sysfs defaults 0 0" >> fstabecho "tmpfs /dev tmpfs defaults 0 0" >> fstabecho "debugfs /sys/kernel/debug debugfs defaults 0 0" >> fstabecho "kmod_mount /mnt 9p trans=virtio 0 0" >> fstabmkdir init.dcd init.decho "mkdir -p /sys" > rcSecho "mkdir -p /tmp" >> rcSecho "mkdir -p /proc" >> rcSecho "mkdir -p /mnt" >> rcSecho "/bin/mount -a" >> rcSecho "mkdir -p /dev/pts" >> rcSecho "mount -t devpts devpts /dev/pts" >> rcSecho "echo /sbin/mdev > /proc/sys/kernel/hotplug" >> rcSecho "mdev -s" >> rcSchmod 777 rcScd ..cd ..cd devsudo mknod console c 5 1 sudo mknod null c 1 3 cd ..cp -a /home/w512/workarea/linux_kernel_debug/arm-gnu-toolchain-13.2.Rel1-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/lib64/lib*.so.* libcp -a /home/w512/workarea/linux_kernel_debug/arm-gnu-toolchain-13.2.Rel1-x86_64-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/lib/*.so.* libfind . | cpio -o -H newc | gzip -c > ../initramfs.cpio.gz cd ..
执行以上脚本后, 在脚本目录下生成对应的 initramfs.cpio.gz
Linux Kernel编译 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 wget https://mirrors.nju.edu.cn/kernel.org/linux/kernel/v6.x/linux-6.8.12.tar.xz tar jxvf linux-6.8.12.tar.xz cd linux-6.8.12cp arch /arm/configs/vexpress_defconfig .configmake ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- menuconfig make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- all -j8
QEMU 运行 Linux Kernel 1 2 3 4 5 6 7 8 9 10 11 12 qemu-system-aarch64 \ -machine virt \ -cpu cortex-a57 \ -machine type =virt \ -m 1024 \ -smp 4 \ -kernel linux-6.8.12/arch/arm64/boot/Image \ -initrd initramfs.cpio.gz \ --append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8" \ -nographic \ --fsdev local ,id =kmod_dev,path=kmodules,security_model=none \ -device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount
kmodules 目录用途
用于QEMU 和 Ubuntu Host之间共享的目录
QEMU + GDB 调试 Linux Kernel qemu 端执行如下命令
1 2 3 4 5 6 7 8 9 10 11 12 13 qemu-system-aarch64 \ -machine virt \ -cpu cortex-a57 \ -machine type =virt \ -m 1024 \ -smp 4 \ -kernel linux-6.8.12/arch/arm64/boot/Image \ -initrd initramfs.cpio.gz \ --append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8" \ -nographic \ --fsdev local ,id =kmod_dev,path=kmodules,security_model=none \ -device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount \ -S -s
GDB 端执行如下命令
1 2 3 4 5 gdb-multiarch --tui vmlinux (gdb)target remote localhost:1234 // 通过1234端口远程连接到qemu端 (gdb)b start_kernel // start_kernel 处设置断点 (gdb)c
QEMU + GBD 调试 arm64 启动汇编代码 通过如下命令获取 vmlinux 中 section header 信息,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 w512@w512-pc ~/w/linux_kernel_debug> aarch64-none-linux-gnu-readelf -S linux-6.8.12/vmlinux There are 39 section headers, starting at offset 0xbfd5540: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .head.text PROGBITS ffff800080000000 00010000 0000000000010000 0000000000000000 AX 0 0 65536 [ 2] .text PROGBITS ffff800080010000 00020000 00000000008ee000 0000000000000000 AX 0 0 65536 [ 3] .rodata PROGBITS ffff800080900000 00910000 000000000020f98e 0000000000000000 WA 0 0 4096 ... [13] .rodata.text PROGBITS ffff800080b5d800 00b6d800 0000000000005800 0000000000000000 AX 0 0 2048 [14] .init.text PROGBITS ffff800080b70000 00b80000 00000000000499f4 0000000000000000 AX 0 0 8 [15] .exit.text PROGBITS ffff800080bb99f8 00bc99f8 0000000000002324 0000000000000000 AX 0 0 8 [16] .altinstructions PROGBITS ffff800080bbbd1c 00bcbd1c 0000000000033378 0000000000000000 A 0 0 1 [17] .init.data PROGBITS ffff800080bf5000 00c05000 0000000000019888 0000000000000000 WA 0 0 8 ... [25] .bss NOBITS ffff800080edb000 00eea200 000000000004bed0 0000000000000000 WA 0 0 4096 ... [38] .shstrtab STRTAB 0000000000000000 0bfd538c 00000000000001b2 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), D (mbind), p (processor specific)
其中关键的 section 对应的虚拟地址和物理地址如下:
section name
virtual addr
phy addr
.head.text
ffff800080000000
0x40200000
.text
ffff800080010000
0x40210000
.rodata
ffff800080900000
0x40b00000
.rodata.text
ffff800080b5d800
0x40d5d800
.init.text
ffff800080b70000
0x40d70000
.init.data
ffff800080bf5000
0x40df5000
GDB 启动时不要加载 vmlinux, 在 GDB 中通过 add-symbol-file 加载 vmlinux 中指定 section 要加载的物理地址
1 add-symbol-file linux-6.8.12/vmlinux -s .head.text 0x40200000 -s .text 0x40210000 -s .rodata 0x40b00000 -s .rodata.text 0x40d5d800 -s .init.text 0x40d70000 -s .init.data 0x40df5000
问题以及解决方案 问题1:mount: mounting debugfs on /sys/kernel/debug failed: No such file or directory
解决方案: 内核配置加上CONFIG_DEBUG_FS=y
参考