为什么这个引导加载程序只打印 'S'

why does this bootloader only prints 'S'

我正在编写一个简单的 x86 引导加载程序。

这是我遇到问题的 c 程序:test4.c

__asm__(".code16\n");
__asm__("jmpl [=11=]x0, $main\n");

void prints ( char* str )
{
    char* pStr = str;

    while( *pStr )
    {
        __asm__ __volatile (
            "int [=11=]x10"
            :
            : "a"(0x0e00 | *pStr), "b"(7)
        );
        pStr++;
    }
}

void main ( ) 
{
    char*  str = "\n\rHello World\n\r";
    char* pStr = str;

    while( *pStr )
    {
        __asm__ __volatile (
            "int [=11=]x10"
            :
            : "a"(0x0e00 | *pStr)
        );
        pStr++;
    }
    prints ( str );

}

当我尝试在 main 函数中打印一个字符串时,它起作用了。但是当我将字符串传递给另一个执行相同指令但仍然只在屏幕上打印 S 的函数时。所以最终输出看起来像这样:

Hello World
S

这是我使用的 linker 文件:test.ld

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

这是我用来编译 c 程序和link它的命令

$ gcc -c -g -Os -m32 -march=i686 -ffreestanding -Wall -Werror test4.c -o test4.o
$ ld -melf_i386 -static -Ttest.ld -nostdlib --nmagic -o test4.elf test4.o
$ objcopy -O binary test4.elf test4.bin

并且我使用 bochs 模拟器来测试这个引导加载程序

您不能使用 GCC 执行此操作。忽略所有说你可以的教程——它们是错误的。

最重要的是要记住 GCC 不是 16 位编译器__asm__(".code16\n") 指令不会把它变成一个;它只会混淆汇编程序,将 GCC 的输出从 32 位 x86 重定向到 16 位。这将导致奇怪和意外的行为,尤其是在任何使用指针的代码中。

如果要编写 x86 引导加载程序,您将需要:

  • 使用可以专门针对 16 位 x86 ("real mode") 的 C 编译器。例如,考虑 OpenWatcom 工具链。

  • 变得非常熟悉 x86 实模式的怪癖 -- 特别是分段。

  • 在汇编中编写引导加载程序的一些部分,尤其是启动代码。