ita*_*ato 5 c x86 gdb qemu osdev
基于os-dev 教程,使用 GDB 远程调试 Qemu 中运行的代码。
我的版本在这里。该问题仅在 qemu 中远程调试代码时发生,而不是在正常操作系统下构建普通可执行文件以直接在 GDB 中运行时发生。
代码如下所示:
#define BUFSIZE 255
static char buf[BUFSIZE];
void foo() {
// Making sure it's all zero.
for (int i = 0; i < BUFSIZE; i++) buf[i] = 0;
// Setting first char:
buf[0] = 'a';
// >> insert breakpoint right after setting the char <<
// Prints 'a'.
printf("%s", buf);
}
Run Code Online (Sandbox Code Playgroud)
如果我在标记的位置放置一个断点并打印缓冲区,p buf我会从随机位置获得随机值,似乎来自我的代码部分。如果我得到地址,p &buf我会得到一些看起来不正确的东西,有两件事:
如果我执行 achar* p_buf = buf并用p p_buf它检查地址会给我一个完全不同的地址,该地址在执行过程中是稳定的(另一个则不是)。然后我检查那个内存部分,x /255b 0x____我可以看到a然后是零(97 0 0 0 ... 0)。
下一个命令 ( printf("%s", buf);) 实际上会打印a.
这让我相信如果我只检查静态变量,它可能是 GDB 不知道正确的位置。
我应该从哪里开始调试?
有关编译条件的详细信息:
-g -Wall -Wextra -pedantic -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -m32GDB 的输出示例:
(gdb) p buf
$1 = "dfghjkl;'`\000\\zxcvbnm,./\000*\000 ", '\000' <repeats 198 times>...
(gdb) p p_buf
$2 = 0x40c0 <buf+224> "a"
(gdb) p &buf
$3 = (char (*)[255]) 0x3fe0 <buf>
(gdb) info address buf
Symbol "buf" is static storage at address 0x3fe0.
Run Code Online (Sandbox Code Playgroud)
更新 2:
反汇编了显示差异的代码版本:
; void foo
0x19f1 <foo> push %ebp
0x19f2 <foo+1> mov %esp,%ebp
0x19f4 <foo+3> sub $0x10,%esp
; char* p_buf = char_buf; --> `p &char_buf` is 0x4040 (incorrect) but `p p_buf` is 0x4100
0x19f7 <foo+6> movl $0x4100,-0x4(%ebp)
; void* p_p_buf = (void*)p_buf; --> `p p_p_buf` gives 0x4100
0x19fe <foo+13> mov -0x4(%ebp),%eax
0x1a01 <foo+16> mov %eax,-0x8(%ebp)
; void* p_char_buf = (void*)&char_buf; --> `p p_char_buf` gives 0x4100
0x1a04 <foo+19> movl $0x4100,-0xc(%ebp)
; char_buf[0] = 'a'; --> correct address
0x1a0b <foo+26> movb $0x61,0x4100
; char_buf[1] = 'b'; --> correct address (asking `p &char_buf` here is still incorrectly 0x4040)
0x1a12 <foo+33> movb $0x62,0x4101
; void foo return
0x1a19 <foo+40> nop
0x1a1a <foo+41> leave
0x1a1b <foo+42> ret
Run Code Online (Sandbox Code Playgroud)
我Makefile用于构建项目的样子:
C_SOURCES = $(wildcard kernel/*.c drivers/*.c)
C_HEADERS = $(wildcard kernel/*.h drivers/*.h)
OBJ = ${C_SOURCES:.c=.o kernel/interrupt_table.o}
CC = /home/itarato/code/os/i386elfgcc/bin/i386-elf-gcc
# GDB = /home/itarato/code/os/i386elfgcc/bin/i386-elf-gdb
GDB = /usr/bin/gdb
CFLAGS = -g -Wall -Wextra -ffreestanding -fno-exceptions -pedantic -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -m32
QEMU = qemu-system-i386
os-image.bin: boot/boot.bin kernel.bin
cat $^ > $@
kernel.bin: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary
kernel.elf: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $@ -Ttext 0x1000 $^
kernel.dis: kernel.bin
ndisasm -b 32 $< > $@
run: os-image.bin
${QEMU} -drive format=raw,media=disk,file=$<,index=0,if=floppy
debug: os-image.bin kernel.elf
${QEMU} -s -S -drive format=raw,media=disk,file=$<,index=0,if=floppy &
${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" -ex "tui enable" -ex "layout split" -ex "focus cmd"
%.o: %.c ${C_HEADERS}
${CC} ${CFLAGS} -c $< -o $@
%.o: %.asm
nasm $< -f elf -o $@
%.bin: %.asm
nasm $< -f bin -o $@
build: os-image.bin
echo Pass
clean:
rm -rf *.bin *.o *.dis *.elf
rm -rf kernel/*.o boot/*.bin boot/*.o
Run Code Online (Sandbox Code Playgroud)
这是一个有趣的问题。归根结底,LD(链接器)为 ELF 可执行文件生成的代码与使用该选项时kernel.elfLD 生成的代码不同。虽然人们期望它们是相同的,但事实并非如此。kernel.bin--oformat binary
更简单地说,这些Makefile规则不会生成与您预期相同的代码:
kernel.elf: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $@ -Ttext 0x1000 $^
Run Code Online (Sandbox Code Playgroud)
和
kernel.bin: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary
Run Code Online (Sandbox Code Playgroud)
看来差异在于链接器在使用和不使用 时如何对齐各部分--oformat binary。ELF 文件(以及用于调试的符号)被视为位于一处,而实际在 QEMU 中运行的二进制文件在不同的偏移量处生成了代码和数据。
我从未观察到这个问题,因为我使用自己的链接器脚本,并且总是使用 OBJCOPY 从 ELF 可执行文件生成二进制文件,而不是使用 LD 链接两次。OBJCOPY 可以获取 ELF 可执行文件并将其转换为二进制文件。规则Makefile可以修改为:
kernel.bin: kernel.elf
i386-elf-objcopy -O binary $^ $@
kernel.elf: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $@ -Ttext 0x1000 $^
Run Code Online (Sandbox Code Playgroud)
这样做将确保生成的二进制文件与为 ELF 可执行文件生成的文件相匹配。