简介
主要记录下gdb + qemu调试benos的过程,以及对benos基础实验代码的一些小小解析。
准备
交叉编译工具链
安装aarch64-linux-gnu-gcc,这里就不赘述了
gdb安装
sudo apt install gdb-multiarch

benos代码编译
代码仓库
https://github.com/runninglinuxkernel/arm64_programming_practice/tree/main/chapter_2/lab01_hello_benos/BenOS
下载后代码结构如下

查看Makefile,可知默认的工具链跟板子类型

我们模拟raspi4b,输入下列命令,编译
export board=rpi4
make
最后可以看到编译得到的elf文件跟bin文件

运行
输入这条命令,就可以运行,看到程序的输出了
./qemu-system-aarch64 -machine raspi4b2g -nographic -kernel ~/benos/benos.bin

调试
调试命令如下,用elf文件,然后加上了-S -s选项
./qemu-system-aarch64 -machine raspi4b2g -nographic -kernel ~/benos/benos.elf -S -s
这个时候再开个新的终端
gdb-multiarch --tui benos/build/benos.elf

接着进入这个界面

敲下回车键

接着跟qemu的gdbserver进行连接,输入target remote localhost:1234

然后,我们使用b命令设置断点,比如b _start就是在_start处设置端点,再执行c,就可以使程序运行到第一个端点处,并在上面的窗口显示代码,如下图所示:


接着可以使用s命令进行单步调试,这里可以看到已经走完了打印We的流程了

查看另一边开着qemu的终端,可以看到We的输出

代码解析
编译过程
先从bin跟elf的怎么生成说起,通过Makefile可知elf文件是通过链接文件linker.ld,集合了多个.o之后,生成的。而bin文件则是strip掉一些调试后生成的直接在裸机上运行的二进制文件。前面用qemu模拟的时候也是用bin文件运行,elf文件来调试。

生成elf文件的命令,-T指定链接文件linker.ld,-o指定输出的产物,-e指定程序的入口
aarch64-linux-gnu-ld -T src/linker.ld -o build/benos.elf build/pl_uart_c.o build/kernel_c.o build/mm_s.o build/boot_s.o -e _start
生成bin文件的命令,利用objcopy去掉elf文件中的调试信息,最终生成可直接在裸机上运行的二进制文件(注意是裸机,像linux操作系统有加载器,可以直接运行elf文件)
aarch64-linux-gnu-objcopy build/benos.elf -O binary benos.bin
在生成.o目标文件的时候,也有几个编译选项
aarch64-linux-gnu-gcc -DCONFIG_BOARD_PI4B -g -Wall -nostdlib -nostdinc -Iinclude -MMD -c src/pl_uart.c -o build/pl_uart_c.o
aarch64-linux-gnu-gcc -DCONFIG_BOARD_PI4B -g -Wall -nostdlib -nostdinc -Iinclude -MMD -c src/kernel.c -o build/kernel_c.o
aarch64-linux-gnu-gcc -g -Iinclude -MMD -c src/mm.S -o build/mm_s.o
aarch64-linux-gnu-gcc -g -Iinclude -MMD -c src/boot.S -o build/boot_s.o
-g:表示编译的时候加入调试符号表等信息-Wall:表示打开所有的警告信息(也就是warning all)-nostdlib:表示不连接系统的标准启动文件和标准库文件,只把指定的文件传递给连接器,这个选项用于编译内核,bootloader等程序,它们不需要标准启动文件和标准库文件-nostdinc:表示不包含C语言的标准库的头文件
链接文件
前面提到生成elf文件的命令,需要-T指定链接文件linker.ld,本次实验用的linker.ld文件如下
SECTIONS
{
. = 0x80000,
.text.boot : { *(.text.boot) }
.text : { *(.text) }
.rodata : { *(.rodata) }
.data : { *(.data) }
. = ALIGN(0x8);
bss_begin = .;
.bss : { *(.bss*) }
bss_end = .;
}
-
SECTIONS:是LS (Linker Script)语法中的关键命令,描述输出文件的内存布局 . = 0x80000:程序加载在DDR中的0x80000处.text.boot:所有的.o文件的.text.boot部分组成,启动首先要执行的代码(从下面的分析,就是-e指定的_start).text:所有的.o文件的.text部分组成,代码段-
.rodata:所有的.o文件的.rodata部分组成,只读数据段 .data:所有的.o文件的.data部分组成,数据段.bss:所有的.o文件的.bss部分组成,包含没有初始化的全局变量跟静态变量
其他
如果qemu不支持raspi4g,可以参考之前我的链接编译一个出来支持raspi4g的,或者是用-machine virt也可以用来模拟。
qemu-system-aarch64 -machine virt -serial null -serial mon:stdio -nographic -kernel benos/build/benos.bin -S -s