wsl下qemu调试benos

Posted by 婷 on August 26, 2023 本文总阅读量

简介

主要记录下gdb + qemu调试benos的过程,以及对benos基础实验代码的一些小小解析。

准备

交叉编译工具链

安装aarch64-linux-gnu-gcc,这里就不赘述了

gdb安装

sudo apt install gdb-multiarch

image-20230730200810934

benos代码编译

代码仓库

https://github.com/runninglinuxkernel/arm64_programming_practice/tree/main/chapter_2/lab01_hello_benos/BenOS

下载后代码结构如下

image-20230723104832952

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

image-20230814225319828

我们模拟raspi4b,输入下列命令,编译

 export board=rpi4
 make

最后可以看到编译得到的elf文件跟bin文件

image-20230723104913632

运行

输入这条命令,就可以运行,看到程序的输出了

 ./qemu-system-aarch64 -machine raspi4b2g -nographic -kernel ~/benos/benos.bin 

image-20230820183304877

调试

调试命令如下,用elf文件,然后加上了-S -s选项

 ./qemu-system-aarch64 -machine raspi4b2g -nographic -kernel ~/benos/benos.elf -S -s

这个时候再开个新的终端

gdb-multiarch --tui benos/build/benos.elf

image-20230731222005123

接着进入这个界面

image-20230820183743816

敲下回车键

image-20230820183844849

接着跟qemugdbserver进行连接,输入target remote localhost:1234

image-20230820184106252

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

image-20230810235953042

image-20230811000014242

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

image-20230820184541080

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

image-20230820185356853

代码解析

编译过程

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

image-20230823232604377

生成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