GDB 逐步完成 QEMU 上裸机开发中特定内核的指令



我正在学习ARM上的裸机开发,为此我选择在QEMU上模拟Raspi3。因此,它是一个虚拟的ARM Cortex A-53,嵌入了ARMv8架构。我编译了以下简单的裸机代码:

.global _start
_start:
1:  wfe
b 1b

我使用以下命令启动它:

qemu-system-aarch64 -M raspi3 -kernel kernel8.img -display none -S -s

GDB 使用以下方法从另一个终端连接到它:

gdb-multiarch ./kernel8.elf -ex 'target remote localhost:1234' -ex 'break *0x80000' -ex 'continue'

到目前为止,一切都很好,我可以注意到 gdb 中的断点。

Reading symbols from ./kernel8.elf...
Remote debugging using localhost:1234
0x0000000000000000 in ?? ()
Breakpoint 1 at 0x80000: file start.S, line 5.
Continuing.
Thread 1 hit Breakpoint 1, _start () at start.S:5
5   1:  wfe
(gdb) info threads
Id   Target Id                    Frame
* 1    Thread 1.1 (CPU#0 [running]) _start () at start.S:5
2    Thread 1.2 (CPU#1 [running]) 0x0000000000000300 in ?? ()
3    Thread 1.3 (CPU#2 [running]) 0x000000000000030c in ?? ()
4    Thread 1.4 (CPU#3 [running]) 0x000000000000030c in ?? ()
(gdb) list
1   .section ".text.boot"
2
3   .global _start
4   _start:
5   1:  wfe
6       b 1b
(gdb)

根据我的理解,在ARM的情况下,所有内核将在重置时执行相同的代码,因此理想情况下,所有内核都必须运行相同的代码。我只想通过放置断点来验证这一点,这就是问题所在。不会命中其他内核的断点。如果我没记错的话,我案例中的线程只不过是核心。我尝试休息,但不起作用:

(gdb) break *0x80000 thread 2
Note: breakpoint 1 (all threads) also set at pc 0x80000.
Breakpoint 2 at 0x80000: file start.S, line 5.
(gdb) thread 2
[Switching to thread 2 (Thread 1.2)]
#0  0x0000000000000300 in ?? ()
(gdb) info threads
Id   Target Id                    Frame
1    Thread 1.1 (CPU#0 [running]) _start () at start.S:5
* 2    Thread 1.2 (CPU#1 [running]) 0x0000000000000300 in ?? ()
3    Thread 1.3 (CPU#2 [running]) 0x000000000000030c in ?? ()
4    Thread 1.4 (CPU#3 [running]) 0x000000000000030c in ?? ()
(gdb) s
Cannot find bounds of current function
(gdb) c
Continuing.
[Switching to Thread 1.1]
Thread 1 hit Breakpoint 1, _start () at start.S:5
5   1:  wfe
(gdb)

我删除了核心 1 断点,然后核心 2 永远挂起:

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000080000 start.S:5
breakpoint already hit 2 times
2       breakpoint     keep y   0x0000000000080000 start.S:5 thread 2
stop only in thread 2
(gdb) delete br 1
(gdb) info break
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000080000 start.S:5 thread 2
stop only in thread 2
(gdb) thread 2
[Switching to thread 2 (Thread 1.2)]
#0  0x000000000000030c in ?? ()
(gdb) c
Continuing.

我可以在核心 2 上获取断点吗?我在这里做错了什么?

编辑

我尝试了set scheduler-locking on(假设这是我所需要的),但这似乎也不适合我。

(gdb) break *0x80000
Breakpoint 3 at 0x80000: file start.S, line 5.
(gdb) thread 2
[Switching to thread 2 (Thread 1.2)]
#0  0x000000000000030c in ?? ()
(gdb) set scheduler-locking on
(gdb) c
Continuing.

^C/build/gdb-OxeNvS/gdb-9.2/gdb/inline-frame.c:367: internal-error: void skip_inline_frames(thread_info*, bpstat): Assertion `find_inline_frame_state (thread) == NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) n
This is a bug, please report it.  For instructions, see:
<http://www.gnu.org/software/gdb/bugs/>.
/build/gdb-OxeNvS/gdb-9.2/gdb/inline-frame.c:367: internal-error: void skip_inline_frames(thread_info*, bpstat): Assertion `find_inline_frame_state (thread) == NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Create a core file of GDB? (y or n)

编辑 2

在@Frank的建议下,我在本地构建了(最新的)qemu 6.2.0,并使用了 arm 工具链中可用的gdb

naveen@workstation:~/.repos/src/arm64/baremetal/raspi3-tutorial/01_bareminimum$ /opt/qemu-6.2.0/build/qemu-system-aarch64 -version
QEMU emulator version 6.2.0
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers
naveen@workstation:~/.repos/src/arm64/baremetal/raspi3-tutorial/01_bareminimum$ /opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gdb -version
GNU gdb (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29)) 10.2.90.20210621-git

但我仍然有问题。我的其他内核 2、3 和 4 从未遇到断点。似乎他们甚至没有运行我的代码,因为他们指向的地址看起来不正常。

(gdb) info threads
Id   Target Id                    Frame
* 1    Thread 1.1 (CPU#0 [running]) _start () at start.S:5
2    Thread 1.2 (CPU#1 [running]) 0x000000000000030c in ?? ()
3    Thread 1.3 (CPU#2 [running]) 0x000000000000030c in ?? ()
4    Thread 1.4 (CPU#3 [running]) 0x000000000000030c in ?? ()

编辑 3

问题似乎出在我的 Makefile 上,因为当我按照 Frank 的建议使用该命令进行构建时,它对我有用。 有人可以看看这个制作文件有什么问题:

CC = /opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin/aarch64-none-elf
CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostartfiles -nostdlib -g
all: clean kernel8.img
start.o: start.S
${CC}-gcc $(CFLAGS) -c start.S -o start.o
kernel8.img: start.o
${CC}-ld -g -nostdlib start.o -T link.ld -o kernel8.elf
${CC}-objcopy -O binary kernel8.elf kernel8.img
clean:
rm kernel8.elf kernel8.img *.o >/dev/null 2>/dev/null || true

编辑 4

事实证明,当我将kernel8.elf与 QEMU 一起使用进行引导时,一切都按预期工作。但是当我使用二进制格式kernel8.img时,我遇到了问题。通过一些阅读,我了解到ELF包含使示例工作所需的"额外"信息。但为了澄清,我怎样才能使kernel8.img起作用?

您可能正在使用的gdbqemu版本有问题,因为我无法在Ubuntu 20.04.3 LTS系统上从头开始编译的 aarch64-elf-gdb 的 10.1 版和 qemu-system-aarch64 的 6.2.0 版重现您的问题:

wfe.s

.global _start
_start:
1:      wfe
b 1b

wfe.elf

/opt/arm/10/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc -g -ffreestanding -nostdlib -nostartfiles -Wl,-Ttext=0x80000 -o wfe.elf wfe.s

查看生成的代码:

/opt/arm/10/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin/aarch64-none-elf-objdump -d wfe.elf
wfe.elf:     file format elf64-littleaarch64

Disassembly of section .text:
0000000000080000 <_stack>:
80000:       d503205f        wfe
80004:       17ffffff        b       80000 <_stack>

在外壳会话中启动qemu

/opt/qemu-6.2.0/bin/qemu-system-aarch64 -M raspi3b -kernel wfe.elf -display none -S -s

在另一个中开始gdb

/opt/gdb/gdb-10.1-aarch64-elf-x86_64-linux-gnu/bin/aarch64-elf-gdb wfe.elf -ex 'target remote localhost:1234' -ex 'break *0x80000' -ex 'continue'

GDB 会话:

GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=aarch64-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x0000000000080000 in ?? ()
Breakpoint 1 at 0x80000
Continuing.
[Switching to Thread 1.4]
Thread 4 hit Breakpoint 1, 0x0000000000080000 in ?? ()
(gdb) break *0x80000 thread 2
Note: breakpoint 1 (all threads) also set at pc 0x80000.
Breakpoint 2 at 0x80000
(gdb) info threads
Id   Target Id                    Frame 
1    Thread 1.1 (CPU#0 [running]) 0x0000000000080000 in ?? ()
2    Thread 1.2 (CPU#1 [running]) 0x0000000000080000 in ?? ()
3    Thread 1.3 (CPU#2 [running]) 0x0000000000080000 in ?? ()
* 4    Thread 1.4 (CPU#3 [running]) 0x0000000000080000 in ?? ()
(gdb) c
Continuing.
[Switching to Thread 1.2]
Thread 2 hit Breakpoint 1, 0x0000000000080000 in ?? ()
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000080000 
breakpoint already hit 2 times
2       breakpoint     keep y   0x0000000000080000  thread 2
stop only in thread 2
breakpoint already hit 1 time
(gdb) del 1
(gdb) info b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000080000  thread 2
stop only in thread 2
breakpoint already hit 1 time
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, 0x0000000000080000 in ?? ()
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, 0x0000000000080000 in ?? ()
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, 0x0000000000080000 in ?? ()
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, 0x0000000000080000 in ?? ()
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, 0x0000000000080000 in ?? ()
(gdb) 

因此,你的两个问题是:

  1. 我可以在核心 2 上获取断点吗?

正是你在做什么。

  1. 我在这里做错了什么?

什么都没有,但可能正在使用gdb和/或qemu的旧/错误版本 - 我的猜测是 gdb 是你的情况的罪魁祸首,但我可能是错的。

您可以使用 Arm 提供的 gcc 工具链中提供的gdb版本再次测试轻松验证,AArch64 ELF 裸机目标 (aarch64-none-elf) - 我试过了,它工作正常:

/opt/arm/10/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gdb wfe.elf -ex 'target remote localhost:1234' -ex 'break *0x80000' -ex 'continue'
GNU gdb (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29)) 10.2.90.20210621-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=aarch64-none-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.linaro.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from wfe.elf...
Remote debugging using localhost:1234
_start () at wfe.s:3
3       1:      wfe
Breakpoint 1 at 0x80000: file wfe.s, line 3.
Continuing.
Thread 1 hit Breakpoint 1, _start () at wfe.s:3
3       1:      wfe
(gdb) break *0x80000 thread 2
Note: breakpoint 1 (all threads) also set at pc 0x80000.
Breakpoint 2 at 0x80000: file wfe.s, line 3.
(gdb)  info threads
Id   Target Id                    Frame 
* 1    Thread 1.1 (CPU#0 [running]) _start () at wfe.s:3
2    Thread 1.2 (CPU#1 [running]) _start () at wfe.s:3
3    Thread 1.3 (CPU#2 [running]) _start () at wfe.s:3
4    Thread 1.4 (CPU#3 [running]) _start () at wfe.s:3
(gdb) c
Continuing.
[Switching to Thread 1.2]
Thread 2 hit Breakpoint 1, _start () at wfe.s:3
3       1:      wfe
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000080000 wfe.s:3
breakpoint already hit 2 times
2       breakpoint     keep y   0x0000000000080000 wfe.s:3 thread 2
stop only in thread 2
breakpoint already hit 1 time
(gdb) del 1
(gdb) info b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000080000 wfe.s:3 thread 2
stop only in thread 2
breakpoint already hit 1 time
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, _start () at wfe.s:3
3       1:      wfe
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, _start () at wfe.s:3
3       1:      wfe
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, _start () at wfe.s:3
3       1:      wfe
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, _start () at wfe.s:3
3       1:      wfe
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, _start () at wfe.s:3
3       1:      wfe
(gdb) 

请注意,解释如何构建最新版本的gdbqemu超出了当前答案的范围。

最新更新