我有一个 2 阶段引导加载程序和一个 C 内核函数。问题是它没有进入第二阶段或内核。我在第一阶段和第二阶段都进行了字符测试,以便通过输出进行测试,其转换正确但不打印第二阶段字符。
这让我知道从第一阶段到第二阶段的 jmp 存在问题,因为它甚至没有到达第二阶段的第一个标签。在名为 _core 的 C 内核函数中还有一个打印测试,它也不打印输出。这当然是行不通的,因为控制在第一个标签之前就丢失了,所以没有理由相信它会进入内核调用。
第一阶段是常规 .bin 文件,由于我使用了 extern 指令,第二阶段是 .o 文件。我按照预期将它们分开,但我不确定一切应该如何连接。
那么,如何使用引导加载程序正确设置 link.ld 来处理从第一阶段到第二阶段,然后从第二阶段到内核的跳转?
osboot.asm - 第一阶段
[BITS 16]
org 0x7C00
osboot:
mov ah, 0x0e ; BIOS teletype function
mov al, 'B' ; Character to print
int 0x10 ; Call BIOS interrupt
call read_osload
jmp 0:0x7e00
%include "read_osload.inc"
times 510-($-$$) db 0 ; Limit the sector to 510 bytes
dw 0xAA55 ; Boot signature and last 2 bytes of the first sector
osload.asm - 第二阶段
[BITS 16]
section .osload
global _start
_start: jmp switch_pmode
%include "gdt.inc"
switch_pmode:
mov ah, 0x0e ; BIOS teletype function
mov al, 'L' ; Character to print
int 0x10 ; Call BIOS interrupt
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp code_seg:p_mode
[BITS 32]
p_mode:
mov ax, cs
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
[EXTERN _core]
call _core
hlt
core.c - 内核
void _core()
{
char* video_memory = (char*)0xb8000;
video_memory[0] = 'E';
video_memory[1] = 'L';
video_memory[2] = 'L';
video_memory[3] = 'E';
video_memory[4] = 'O';
video_memory[5] = 'S';
}
read_osload.inc
[BITS 16]
read_osload:
mov ah, 0 ; reset the drive function
mov dl, 0 ; drive 0 is the floppy drive
int 0x13 ; call the BIOS interrupt to reset drive
mov ax, 0x7E00 ; read address 0x7E00 so we can jmp to the second stage
mov es, ax
xor bx, bx
mov ah, 0x02 ; read sector function
mov al, 1 ; read 1 sector
mov ch, 0 ; 0 = track 1 (18 sectors per track) no need to move from track 0
mov cl, 2 ; read sector 2
mov dh, 0 ; head number
mov dl, 0 ; drive number. drive 0 is the floppy drive
int 0x13 ; call the BIOS interrupt to read drive
ret
gdt.inc
; null segment descriptor
gdt_start:
dq 0x0
; code segment descriptor
gdt_code:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x08 ; segment base, bits 24-31
; data segment descriptor
gdt_data:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10010010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x10 ; segment base, bits 24-31
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit)
dd gdt_start ; address (32 bit)
code_seg equ gdt_code - gdt_start
data_seg equ gdt_data - gdt_start
链接.ld
ENTRY(_core)
OUTPUT_FORMAT("binary")
OUTPUT(core.bin)
SECTIONS
{
. = 0x7C00; /* osboot.asm starts at 0x7C00*/
.osboot : AT(0x7C00)
{
*(.text)
*(.rodata)
*(.data)
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
. = 0x7E00; /* osload.asm starts at 0x7E00? we need a place to jmp to from osboot */
.osload : AT(0x7E00)
{
*(.text)
*(.rodata)
*(.data)
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
. = 0x1000; /* core.c starts at 0x1000? we need a place to call kernel _core */
._core : AT(0x1000)
{
*(.text)
*(.rodata)
*(.data)
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
kernel_sectors = (SIZEOF(._core) + 511) / 512;
. = ALIGN(4); /* Ensure alignment between sections */
/DISCARD/ : {
*(.eh_frame)
}
}
makefile
all: elle.flp
osboot.bin: osboot.asm
nasm -f bin osboot.asm -o osboot.bin
osload.o: osload.asm
nasm osload.asm -f elf32 -o osload.o
core.o: core.c
gcc -m32 -fno-pie -Wall -ffreestanding --no-builtin -c core.c -o core.o
core.bin: core.o osload.o
ld -T link.ld -melf_i386 -o core.bin core.o osload.o
elle.flp: osboot.bin core.bin
cat osboot.bin core.bin > elle.flp
clean:
rm -f osboot.bin osload.o core.o load_core.o core.bin elle.flp
我尝试过以多种不同的方式更改 link.ld。 我尝试过以多种方式更改 make 文件。 我尝试分别编译 osload.o 和 core.o,然后将它们组合成 .bin,然后再链接它们。 我尝试分别编译 osload.o 和 core.o,然后分别链接它们。
我通过编写一个创建自定义 FS 映像的工具而不是使用链接器解决了这个问题。当时我使用的是软盘,因此基于 FAT-12 的 FS 足以满足我的需求。我读入一系列文件名并将它们与目录条目一起写入图像。