如何使用引导加载程序正确设置链接器脚本?

问题描述 投票:0回答:1

我有一个 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,然后分别链接它们。

assembly x86 nasm ld bootloader
1个回答
0
投票

我通过编写一个创建自定义 FS 映像的工具而不是使用链接器解决了这个问题。当时我使用的是软盘,因此基于 FAT-12 的 FS 足以满足我的需求。我读入一系列文件名并将它们与目录条目一起写入图像。

© www.soinside.com 2019 - 2024. All rights reserved.