JMP无法正常工作

时间:2015-05-23 20:53:16

标签: c assembly x86 x86-16 real-mode

好的,所以我一直在尝试在assembly / C中制作一个两步引导程序,但是我无法让JMP正常工作。起初我认为阅读失败了,但是,在接下来的测试后我排除了这一点:

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

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

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

运行时,我的程序只是挂起,这意味着程序可能会跳到内存中的错误位置。顺便说一句,我显然在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对应物的行为相同。

2 个答案:

答案 0 :(得分:3)

我很确定你的汇编程序正在生成错误的跳转偏移量,它可能会将0x7e00解释为当前文本部分中的相对值,但是它映射到0x7c00通过链接器脚本,您的跳转可能会转到0x7c00+0x7e00=0xfa00。作为一种丑陋的解决方法,您可以尝试使用jmp 0x200或使用间接跳转将其隐藏在mov $0x7e00, %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(限量优惠;))。

答案 1 :(得分:0)

我写了很多启动加载器。

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

1) the boot loader
2) the main code

因此可以更新加载的代码(主程序),而无需对引导加载程序执行任何操作。

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

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

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

引导加载程序的链接器命令文件和主程序的链接器命令文件必须同意已知位置的地址

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

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

最好是主程序采用摩托罗拉S1(或类似)格式而不是原始.elf或.coff格式;否则引导装载程序会很快变大。