如何通过qemu运行调试Lab1练习2的软件?

2026-05-22 08:141阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1733个文字,预计阅读时间需要7分钟。

如何通过qemu运行调试Lab1练习2的软件?

练习二:使用qemu执行并调试lab1中的软件1. 目的:为了熟悉使用qemu和gdb进行调试的工作,我们进行以下小练习:+ 从CPU加电后执行的第一条指令开始,逐步跟踪BIOS的执行。

练习二:使用qemu执行并调试lab1中的软件。 1.题目要求:

为了熟悉使用qemu和gdb进行的调试工作,我们进行如下的小练习:

  1. 从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。
  2. 在初始化位置0x7c00设置实地址断点,测试断点正常。
  3. 从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。
  4. 自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

提示:参考附录“启动后第一条执行的指令”,可了解更详细的解释,以及如何单步调试和查看BIOS代码。

提示:查看 labcodes_answer/lab1_result/tools/lab1init 文件,用如下命令试试如何调试bootloader第一条指令:

$ cd labcodes_answer/lab1_result/ $ make lab1-mon

补充材料: 我们主要通过硬件模拟器qemu来进行各种实验。在实验的过程中我们可能会遇上各种各样的问题,调试是必要的。qemu支持使用gdb进行的强大而方便的调试。所以用好qemu和gdb是完成各种实验的基本要素。

默认的gdb需要进行一些额外的配置才进行qemu的调试任务。qemu和gdb之间使用网络端口1234进行通讯。在打开qemu进行模拟之后,执行gdb并输入

target remote localhost:1234

即可连接qemu,此时qemu会进入停止状态,听从gdb的命令。

另外,我们可能需要qemu在一开始便进入等待模式,则我们不再使用make qemu开始系统的运行,而使用make debug来完成这项工作。这样qemu便不会在gdb尚未连接的时候擅自运行了。

*gdb的地址断点*

在gdb命令行中,使用b *[地址]便可以在指定内存地址设置断点,当qemu中的cpu执行到指定地址时,便会将控制权交给gdb。

*关于代码的反汇编*

有可能gdb无法正确获取当前qemu执行的汇编指令,通过如下配置可以在每次gdb命令行前强制反汇编当前的指令,在gdb命令行或配置文件中添加:

define hook-stop x/i $pc end

即可

*gdb的单步命令*

在gdb中,有next, nexti, step, stepi等指令来单步调试程序,他们功能各不相同,区别在于单步的“跨度”上。

next 单步到程序源代码的下一行,不进入函数。 nexti 单步一条机器指令,不进入函数。 step 单步到下一个不同的源代码行(包括进入函数)。 stepi 单步一条机器指令。 2.预备知识:lab1init解释

实际上练习二就是把tools/gdbinit改为tools/lab1init的样子,lab1init是课程给的答案,我们需要完成相应的内容。不熟的可以先看看lab1init中有什么。lab1init是在哪里调用了呢?

首先看lab1-mon,进入Makefile查看发现:

lab1-mon: $(UCOREIMG) $(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -monitor stdio -hda $< -serial null" $(V)sleep 2 $(V)$(TERMINAL) -e "gdb -q -x tools/lab1init"

这条命令可以看出来大致干的两件事:

​ 1.让qemu把它的执行指令记录下来,放到q.log中

​ 2.和GDB结合来调试正在执行的bootloader

其中"gdb -q -x tools/lab1init"是一条初始化执行指令,lab1init文件内容如下:

file bin/kernel target remote :1234 set architecture i8086 b *0x7c00 continue x /2i $pc

这里面都是gdb能够识别的命令。

第一行的意思是加载bin/kernel文件,这其实就是加载符号信息了,实际上是ucore的信息。

第二、三行的意思是与qemu进行连接,通过TRP进行连接。刚开始BIOS是进入8086的16位实模式方式,一直到0x7C00在BIOS这个阶段启动,最后把bootloader加载进去,把控制权交给bootloader,那么bootloader第一条指令就是在0X7C00处。

第四行的意思是在这个位置设置一个断点,break 0x7C00

第五行continue是让系统继续运行

最后一条x/2i $pc 就是显示两条pc指令

可以输入make lab1-mon,它将启动两个窗口,一个是调试窗口,另一个是qemu,但是断下来了,就是断在了0X7C00处。

如果你想显示更多调试信息,可以输入x/10i $pc,可以把当前的十条指令都显示出来。

这些指令在什么地方?我们启动代码boot文件夹下存放bootloader,可以看到在bootasm.S中第16行开始,它这个指令和刚才看到的gdb里面的指令是一样的

让qemu继续运行,可以输入continue

看到这时候它跑的很快,就是这里面已经把ucore都加进来了

最后可以按ctrl+C终止,输入quit退出

3.问题一、从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。 1.修改gdbinit文件

可以直接在eclipse里面修改也可以使用vim命令进行修改

这里我使用vim命令,注意要先进入~/ucore/labcodes_answer/lab1_result/tools目录下

输入vim gdbinit

将gdbinit内容更改为:

set architecture i8086 target remote :1234 2.make debug

在Makefile中可以看到debug这个命令

debug: $(UCOREIMG) $(V)$(QEMU) -S -s -parallel stdio -hda $< -serial null & $(V)sleep 2 $(V)$(TERMINAL) -e "cgdb -q -x tools/gdbinit"

有两个功能

如何通过qemu运行调试Lab1练习2的软件?

​ 1.连接qemu

​ 2.和CGDB结合来调试正在执行的bootloader

输入cd ..,退回到上一级目录

输入make debug,这里我出现了cgdb错误

具体原因还没弄清,只能将Makefile文件中对应的cgdb修改为gdb,输入make debug

在gdb窗口中使用si命令即可单步追踪(注意:你不必每次输入si,输入一次si后,只要按回车即可执行上次的指令)

也可以用nexti:(nexti 单步一条机器指令,不进入函数。)

在gdb界面下,可通过如下命令来看BIOS的代码:x /2i $pc

4.问题二、在初始化位置0x7c00设置实地址断点,测试断点正常。 1.修改gdbinit文件

target remote :1234 //连接qemu,此时qemu会进入停止状态,听从gdb的命令 set architecture i8086 //设置当前调试的CPU是8086 b *0x7c00 //在0x7c00处设置断点。此地址是bootloader入口点地址,可看boot/bootasm.S的start地址处 c //continue简称,表示继续执行 x/10i $pc //显示当前eip处的汇编指令 2.make debug

5.问题三、从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。

反汇编得到的前几条代码是:

下面是bootasm.S中部分代码:

start: .code16 # Assemble for 16-bit mode cli # Disable interrupts cld # String operations increment # Set up the important data segment registers (DS, ES, SS). xorw %ax, %ax # Segment number zero movw %ax, %ds # -> Data Segment movw %ax, %es # -> Extra Segment movw %ax, %ss # -> Stack Segment # Enable A20: # For backwards compatibility with the earliest PCs, physical # address line 20 is tied low, so that addresses higher than # 1MB wrap around to zero by default. This code undoes this. seta20.1: inb $0x64, %al # Wait for not busy(8042 input buffer empty). testb $0x2, %al jnz seta20.1 movb $0xd1, %al # 0xd1 -> port 0x64 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port

下面是bootblock.asm中的部分代码:

start: .code16 # Assemble for 16-bit mode cli # Disable interrupts 7c00: fa cli cld # String operations increment 7c01: fc cld # Set up the important data segment registers (DS, ES, SS). xorw %ax, %ax # Segment number zero 7c02: 31 c0 xor %eax,%eax movw %ax, %ds # -> Data Segment 7c04: 8e d8 mov %eax,%ds movw %ax, %es # -> Extra Segment 7c06: 8e c0 mov %eax,%es movw %ax, %ss # -> Stack Segment 7c08: 8e d0 mov %eax,%ss

比较可知,三者基本一致。

6.问题四、自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

同上面的一样,修改gdbinit文件中的断点位置

本文共计1733个文字,预计阅读时间需要7分钟。

如何通过qemu运行调试Lab1练习2的软件?

练习二:使用qemu执行并调试lab1中的软件1. 目的:为了熟悉使用qemu和gdb进行调试的工作,我们进行以下小练习:+ 从CPU加电后执行的第一条指令开始,逐步跟踪BIOS的执行。

练习二:使用qemu执行并调试lab1中的软件。 1.题目要求:

为了熟悉使用qemu和gdb进行的调试工作,我们进行如下的小练习:

  1. 从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。
  2. 在初始化位置0x7c00设置实地址断点,测试断点正常。
  3. 从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。
  4. 自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

提示:参考附录“启动后第一条执行的指令”,可了解更详细的解释,以及如何单步调试和查看BIOS代码。

提示:查看 labcodes_answer/lab1_result/tools/lab1init 文件,用如下命令试试如何调试bootloader第一条指令:

$ cd labcodes_answer/lab1_result/ $ make lab1-mon

补充材料: 我们主要通过硬件模拟器qemu来进行各种实验。在实验的过程中我们可能会遇上各种各样的问题,调试是必要的。qemu支持使用gdb进行的强大而方便的调试。所以用好qemu和gdb是完成各种实验的基本要素。

默认的gdb需要进行一些额外的配置才进行qemu的调试任务。qemu和gdb之间使用网络端口1234进行通讯。在打开qemu进行模拟之后,执行gdb并输入

target remote localhost:1234

即可连接qemu,此时qemu会进入停止状态,听从gdb的命令。

另外,我们可能需要qemu在一开始便进入等待模式,则我们不再使用make qemu开始系统的运行,而使用make debug来完成这项工作。这样qemu便不会在gdb尚未连接的时候擅自运行了。

*gdb的地址断点*

在gdb命令行中,使用b *[地址]便可以在指定内存地址设置断点,当qemu中的cpu执行到指定地址时,便会将控制权交给gdb。

*关于代码的反汇编*

有可能gdb无法正确获取当前qemu执行的汇编指令,通过如下配置可以在每次gdb命令行前强制反汇编当前的指令,在gdb命令行或配置文件中添加:

define hook-stop x/i $pc end

即可

*gdb的单步命令*

在gdb中,有next, nexti, step, stepi等指令来单步调试程序,他们功能各不相同,区别在于单步的“跨度”上。

next 单步到程序源代码的下一行,不进入函数。 nexti 单步一条机器指令,不进入函数。 step 单步到下一个不同的源代码行(包括进入函数)。 stepi 单步一条机器指令。 2.预备知识:lab1init解释

实际上练习二就是把tools/gdbinit改为tools/lab1init的样子,lab1init是课程给的答案,我们需要完成相应的内容。不熟的可以先看看lab1init中有什么。lab1init是在哪里调用了呢?

首先看lab1-mon,进入Makefile查看发现:

lab1-mon: $(UCOREIMG) $(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -monitor stdio -hda $< -serial null" $(V)sleep 2 $(V)$(TERMINAL) -e "gdb -q -x tools/lab1init"

这条命令可以看出来大致干的两件事:

​ 1.让qemu把它的执行指令记录下来,放到q.log中

​ 2.和GDB结合来调试正在执行的bootloader

其中"gdb -q -x tools/lab1init"是一条初始化执行指令,lab1init文件内容如下:

file bin/kernel target remote :1234 set architecture i8086 b *0x7c00 continue x /2i $pc

这里面都是gdb能够识别的命令。

第一行的意思是加载bin/kernel文件,这其实就是加载符号信息了,实际上是ucore的信息。

第二、三行的意思是与qemu进行连接,通过TRP进行连接。刚开始BIOS是进入8086的16位实模式方式,一直到0x7C00在BIOS这个阶段启动,最后把bootloader加载进去,把控制权交给bootloader,那么bootloader第一条指令就是在0X7C00处。

第四行的意思是在这个位置设置一个断点,break 0x7C00

第五行continue是让系统继续运行

最后一条x/2i $pc 就是显示两条pc指令

可以输入make lab1-mon,它将启动两个窗口,一个是调试窗口,另一个是qemu,但是断下来了,就是断在了0X7C00处。

如果你想显示更多调试信息,可以输入x/10i $pc,可以把当前的十条指令都显示出来。

这些指令在什么地方?我们启动代码boot文件夹下存放bootloader,可以看到在bootasm.S中第16行开始,它这个指令和刚才看到的gdb里面的指令是一样的

让qemu继续运行,可以输入continue

看到这时候它跑的很快,就是这里面已经把ucore都加进来了

最后可以按ctrl+C终止,输入quit退出

3.问题一、从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。 1.修改gdbinit文件

可以直接在eclipse里面修改也可以使用vim命令进行修改

这里我使用vim命令,注意要先进入~/ucore/labcodes_answer/lab1_result/tools目录下

输入vim gdbinit

将gdbinit内容更改为:

set architecture i8086 target remote :1234 2.make debug

在Makefile中可以看到debug这个命令

debug: $(UCOREIMG) $(V)$(QEMU) -S -s -parallel stdio -hda $< -serial null & $(V)sleep 2 $(V)$(TERMINAL) -e "cgdb -q -x tools/gdbinit"

有两个功能

如何通过qemu运行调试Lab1练习2的软件?

​ 1.连接qemu

​ 2.和CGDB结合来调试正在执行的bootloader

输入cd ..,退回到上一级目录

输入make debug,这里我出现了cgdb错误

具体原因还没弄清,只能将Makefile文件中对应的cgdb修改为gdb,输入make debug

在gdb窗口中使用si命令即可单步追踪(注意:你不必每次输入si,输入一次si后,只要按回车即可执行上次的指令)

也可以用nexti:(nexti 单步一条机器指令,不进入函数。)

在gdb界面下,可通过如下命令来看BIOS的代码:x /2i $pc

4.问题二、在初始化位置0x7c00设置实地址断点,测试断点正常。 1.修改gdbinit文件

target remote :1234 //连接qemu,此时qemu会进入停止状态,听从gdb的命令 set architecture i8086 //设置当前调试的CPU是8086 b *0x7c00 //在0x7c00处设置断点。此地址是bootloader入口点地址,可看boot/bootasm.S的start地址处 c //continue简称,表示继续执行 x/10i $pc //显示当前eip处的汇编指令 2.make debug

5.问题三、从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。

反汇编得到的前几条代码是:

下面是bootasm.S中部分代码:

start: .code16 # Assemble for 16-bit mode cli # Disable interrupts cld # String operations increment # Set up the important data segment registers (DS, ES, SS). xorw %ax, %ax # Segment number zero movw %ax, %ds # -> Data Segment movw %ax, %es # -> Extra Segment movw %ax, %ss # -> Stack Segment # Enable A20: # For backwards compatibility with the earliest PCs, physical # address line 20 is tied low, so that addresses higher than # 1MB wrap around to zero by default. This code undoes this. seta20.1: inb $0x64, %al # Wait for not busy(8042 input buffer empty). testb $0x2, %al jnz seta20.1 movb $0xd1, %al # 0xd1 -> port 0x64 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port

下面是bootblock.asm中的部分代码:

start: .code16 # Assemble for 16-bit mode cli # Disable interrupts 7c00: fa cli cld # String operations increment 7c01: fc cld # Set up the important data segment registers (DS, ES, SS). xorw %ax, %ax # Segment number zero 7c02: 31 c0 xor %eax,%eax movw %ax, %ds # -> Data Segment 7c04: 8e d8 mov %eax,%ds movw %ax, %es # -> Extra Segment 7c06: 8e c0 mov %eax,%es movw %ax, %ss # -> Stack Segment 7c08: 8e d0 mov %eax,%ss

比较可知,三者基本一致。

6.问题四、自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

同上面的一样,修改gdbinit文件中的断点位置