简介
主要记录下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