JMP 不工作

JMP not working

好的,所以我一直在尝试在 assembly/C 中制作一个两步引导加载程序,但我无法让 JMP 工作。起初我以为读取失败了,但是经过以下测试我排除了这种可能性:

__asm__ __volatile__(
    "xorw %ax, %ax;"
    "movw %ax, %ds;"
    "movw %ax, %es;"
    "movb [=10=]x02, %ah;"
    "movb [=10=]x01, %al;"
    "movw [=10=]x7E00, %bx;"
    "movw [=10=]x0003, %cx;"
    "xorb %dh, %dh;"
    "int [=10=]x13;"
    "movb 0x7E00, %al;"
    "movb [=10=]x0e, %ah;"
    "int [=10=]x10;"
    //"jmp 0x7E00"
);

这按预期打印了'f'(扇区的第一个字节是0x66,这是'f'的ASCII码)证明读取成功并且jmp是问题。这是我的代码:

__asm__(".code16\n");
__asm__(".code16gcc\n");
__asm__("jmpl [=11=]x0000, $main\n");
void main(){
    __asm__ __volatile__(
        "xorw %ax, %ax;"
        "movw %ax, %ds;"
        "movw %ax, %es;"
        "movb [=11=]x02, %ah;"
        "movb [=11=]x01, %al;"
        "movw [=11=]x7E00, %bx;"
        "movw [=11=]x0003, %cx;"
        "xorb %dh, %dh;"
        "int [=11=]x13;"
        "jmp [=11=]x200;"
    );
}

当运行时,我的程序只是挂起,这意味着程序可能跳到了内存中的错误位置。顺便说一下,我显然 运行 在 VMWare 播放器下以实模式进行此操作。我正在使用以下命令编译它:

gcc -c -0s -march=i686 -ffreestanding -Wall -Werror boot.c -o boot.o
ld -static -Ttest.ld -nostdlib --nmagic -o boot.elf boot.o --no-check-sections
objcopy -0 binary boot.elf boot.bin

这是 test.ld:

ENTRY(main);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00)
    {
        *(.test);
    }
    .sig : AT(0x7DFE)
    {
        SHORT(0xAA55);
    }
}

注意:我已经确认这不是内联 asm 的问题 - 我也尝试了纯汇编实现,结果相同 - 我使用 C 的唯一原因是因为我计划稍微扩展它并且我更喜欢 C 循环和函数...

编辑: 我已经上传了我的软驱的前三个扇区 here

编辑 2: 我一直无法使用任何建议让我的引导加载程序工作,并且根据@RossRidge 的建议,我编写了相同的程序集版本程序和一个简单的汇编程序来回显输入。可悲的是,这些也不起作用..

引导程序:

org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ah, 0x02
mov al, 0x01
mov bx, 0x7E00
mov cx, 0x0003
xor dh, dh
int 0x13
jmp 0x7E00

部门 3 中的程序:

xor ax, ax
int 0x16
mov ah, 0xe
int 0x10

它们都是用以下代码编译的:nasm Linux/boot.S -o Linux/asm.bin 并且行为与它们的 C 对应物相同..

我很确定你的汇编程序生成了错误的跳转偏移量,它可能将 0x7e00 解释为当前文本部分中的相对值,但链接器脚本将其映射到 0x7c00,因此你的跳转可能会转到 0x7c00+0x7e00=0xfa00。作为一个丑陋的解决方法,您可以尝试 jmp 0x200 代替或使用间接跳转将其隐藏在您的工具中,例如 mov [=15=]x7e00, %ax; jmp *%ax。或者 jmp .text+0x200 也可以。

另请注意,在 32 位 C 编译器中编写 16 位 asm 会生成错误代码,加载扇区的第一个字节为 0x66(操作数大小覆盖前缀)也暗示了这一点。由于编译器采用 32 位模式,它会生成带有前缀的 16 位代码,当 运行 在 16 位模式下时会切换回 32 位。一般来说,为此目的滥用内联 asm 不是一个好主意,你应该使用一个单独的 asm 文件并确保你告诉你的汇编器代码是为 16 位实模式设计的。

PS: 学习使用调试器。


更新:使用 nasm boot.asm -o boot.bin 汇编时的以下代码在 qemubochs 中工作正常:

org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ah, 0x02
mov al, 0x01
mov bx, 0x7E00
mov cx, 0x0003
xor dh, dh
int 0x13
jmp 0x7E00

times 510-($-$$) db 0
dw 0xaa55
; second sector
times 512 db 0
; Program in sector 3:
xor ax, ax
int 0x16
mov ah, 0xe
int 0x10
jmp $
times 1536-($-$$) db 0

您可以下载 image here(限时优惠 ;))。

我写过很多boot loader

需要两 (2) 个单独的可执行文件,

1) the boot loader
2) the main code

因此加载的代码(主程序)可以在不对引导加载程序执行任何操作的情况下进行更新。

引导加载程序完成后需要跳转到已知位置。

主程序需要在已知位置放置一个跳转指令,跳转到librt库中的一个函数。 (通常是start())

start() 将设置 I/O、堆等并调用 main()

引导加载程序的链接器命令文件和主程序的链接器命令文件必须就已知位置的地址达成一致

已知位置通常是两个链接器命令文件的唯一部分中的唯一内容。

引导加载程序必须足够智能,能够将主程序的所有部分放置在内存中的正确区域。

主程序最好是motorola S1(或类似)格式,而不是原始的.elf或.coff格式;否则引导加载程序会很快变得很大。