【发布时间】:2021-06-28 14:16:01
【问题描述】:
我最近更换了我的计算机,从那时起,我的 makefile 链输出了一个 512 字节的二进制文件,其中只有 0x00s 或引导加载程序,但没有其他任何内容。我将以下内容创建为 MRE:
boot.asm:
BITS 16
SECTION boot
GLOBAL _entry
EXTERN _start
_entry:
mov [disk],dl
mov ah, 0x2 ; read sectors
mov al, 6 ; amount = 6
mov ch, 0 ; zylinder = 0
mov cl, 2 ; first sector to read = 2
mov dh, 0 ; head = 0 (up)
mov dl, [disk] ; disk
mov bx, _start ; segment:offset address
int 0x13
cli
lgdt [GDT_POINTER]
mov eax, cr0
or al, 1
mov cr0, eax
mov ax, DATA_SEGMENT
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp CODE_SEGMENT:_start
disk: DB 0x00
GDT_POINTER:
DW GDT_EXIT - GDT_ENTRY
DD GDT_ENTRY
CODE_SEGMENT EQU GDT_CODE - GDT_ENTRY
DATA_SEGMENT EQU GDT_DATA - GDT_ENTRY
GDT_ENTRY:
DQ 0x00
GDT_CODE:
DW 0xffff
DW 0x0000
DB 0x00
DB 0x9a
DB 0xcf
DB 0x00
GDT_DATA:
DW 0xffff
DW 0x0000
DB 0x00
DB 0x92
DB 0xcf
DB 0x00
GDT_EXIT:
TIMES 510 - ($ - $$) DB 0x00
DW 0xAA55
kernel.c:
int _main() {
while(1) {}
}
linker16.ld:
ENTRY(_entry);
OUTPUT_FORMAT(elf32-i386);
OUTPUT_ARCH(i386);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(boot)
*(.text)
}
.data :
{
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
}
}
linker32.ld:
ENTRY(_main);
OUTPUT_FORMAT(elf32-i386);
OUTPUT_ARCH(i386);
SECTIONS
{
. = 0x7E00;
.text : AT(0x7E00)
{
*(.text)
}
.data :
{
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
}
}
生成文件:
all:
nasm -O32 -f elf -o boot.o boot.asm
gcc -m32 -c -g -ffreestanding -nostdlib -nostdinc -Wall -Werror -o kernel.o kernel.c
ld -static -nostdlib -build-id=none -relocatable -T linker16.ld -o boot.elf boot.o
ld -static -nostdlib -build-id=none -relocatable -T linker32.ld -o kernel.elf kernel.o
objcopy -O binary boot.elf boot.bin
objcopy -O binary kernel.elf kernel.bin
cat boot.bin kernel.bin > sys.bin~
rm *.o
rm *.elf
rm *.bin
cat sys.bin~ > sys.bin
rm sys.bin~
qemu-system-i386 sys.bin
qemu:
qemu-system-i386 sys.bin
预期的输出是一个空白屏幕,当查看兼容监视器(“信息寄存器”输出)时,GDT 在 0x7C00 之后设置了几个字节。相反,它卡在引导循环中,因为引导加载程序已正确编译,但它之后的所有内容(while 循环)都丢失了。在 .o 文件之前,一切都按预期进行,但 .elf 和 .bin 太短了。有人有解决方案吗?我使用的版本是:
NASM 版本 2.14.02
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
GNU ld & objcopy (GNU Binutils for Ubuntu) 2.34
编辑: 更新后的代码反而会产生一堆零,是它应该大小的 60 倍。幻数已正确放置,但内核部分仍然无法使用。
编辑 2: 我通过反复试验发现,删除链接器的 -relocatable 参数会清除大部分零,但它仍然无法按预期工作并停留在引导循环中。
编辑 3: 如果有人遇到和我一样的问题,我希望代码能够真正工作。在上面的代码中,我修复了 GDT,因为我在其中犯了一个错误。我将所有 DB 缩小到 DD,但忘记了 little endian 会反转其中的所有字节,因此所有 GDT 描述符中的已使用位都设置为零,从而无法进行跳转。结合fuz的回答,现在可以让这个噩梦运行起来了。
【问题讨论】:
-
谁告诉你可以
cat两个.o 文件来制作一个更大的.o 文件?你知道shell脚本中>>和>的区别吗? -
猫 x.o y.o >> z.o 用于我从多个开源操作系统下载的所有五个源中,我从中学习并在一些教程中使用。自从我大约两年前开始该项目以来,我也使用了它,并且直到大约一周前它都运行良好。这应该(至少在理论上)不会导致问题。
-
好吧,我真的很怀疑将 .o 文件放在一起。对于每次构建时都会在末尾添加更多数据并且从不删除任何数据的巨型 .o 文件真的很怀疑。您是否尝试将两个 .o 文件都提供给链接器?
-
不,我实际上并不知道它在 makefile 中的不同之处,但我只是用 MRE 进行了尝试,它产生了完全相同的输出。这是一个错误/随机吗?
-
哦,在 mre 中,我忘记放置在构建 bin 后删除所有 .o 和 .elf 文件的部分。通常不会发生堆叠。