C 为什么此引导加载程序只打印';S';

C 为什么此引导加载程序只打印';S';,c,gcc,x86,bootloader,C,Gcc,X86,Bootloader,我正在编写一个简单的x86引导加载程序 这就是我遇到麻烦的c程序:test4.c __asm__(".code16\n"); __asm__("jmpl $0x0, $main\n"); void prints ( char* str ) { char* pStr = str; while( *pStr ) { __asm__ __volatile ( "int $0x10" :

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

这就是我遇到麻烦的c程序:
test4.c

__asm__(".code16\n");
__asm__("jmpl $0x0, $main\n");

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

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

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

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

}
当我尝试在main函数中打印字符串时,它会工作。但当我将字符串传递给另一个函数时,该函数执行相同的指令,但仍然只在屏幕上打印
S
。因此,最终输出如下所示:

Hello World
S
下面是我使用的链接器文件:
test.ld

ENTRY(main);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00)
    {
        *(.text);
    }
    .sig : AT(0x7DFE)
    {
        SHORT(0xaa55);
    }
}
下面是我用来编译c程序和链接它的命令

$ 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
emulator来测试这个引导加载程序

你不能用GCC来做这件事。忽略所有说你可以的教程——它们是错误的

最重要的是要记住GCC不是16位编译器。
\uuuu asm\uuu(“.code16\n”)
指令没有将其转换为一个指令;它只会使汇编程序将GCC的输出从32位x86重定目标为16位。这将导致奇怪和意外的行为,尤其是在任何使用指针的代码中

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

  • 使用可以专门针对16位x86(“实模式”)的C编译器。考虑OpenWATCOM工具链,例如:

  • 非常熟悉x86实模式的怪癖——特别是分段

  • 在汇编中编写引导加载程序的某些部分,特别是启动代码


使用GCC无法做到这一点。忽略所有说你可以的教程——它们是错误的

最重要的是要记住GCC不是16位编译器。
\uuuu asm\uuu(“.code16\n”)
指令没有将其转换为一个指令;它只会使汇编程序将GCC的输出从32位x86重定目标为16位。这将导致奇怪和意外的行为,尤其是在任何使用指针的代码中

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

  • 使用可以专门针对16位x86(“实模式”)的C编译器。考虑OpenWATCOM工具链,例如:

  • 非常熟悉x86实模式的怪癖——特别是分段

  • 在汇编中编写引导加载程序的某些部分,特别是启动代码


独立程序必须设置堆栈和段寄存器。请参阅Daskwuff关于不使用GCC 16位代码的评论。Openwatcom是针对16位代码IMHO的C编译器的最佳选择。但是,如果您知道自己在做什么,了解GCC生成的代码的细微差别(及其局限性),您可以这样看。这基本上是为了使用引导加载程序的BIOS中断进行打印而设计的。无论如何,这几乎是一个完全相同的副本,在您的情况下,问题出在这一行
\uuu asm\uu(“.code16\n”)。它应该是
\uuuu asm\uuuuu(“.code16gcc\n”)。使用
.code16
会弄乱堆栈,因为它将假定16位调用(而不是GCC真正期望的32位调用)。这将导致参数出现在堆栈上的错误位置。我本来打算写一个答案,但发现这个问题在
main
的底部几乎是重复的,没有地方可以返回。因此,你应该考虑在<代码>主语< /代码>的底部放置一个无限循环,如“代码”>(1);<代码>或使用
\u asm\u(“cli;hlt;”)独立程序必须设置堆栈和段寄存器。请参阅Daskwuff关于不使用GCC的16位代码的评论。Openwatcom是针对16位代码IMHO的C编译器的最佳选择。但是,如果您知道自己在做什么,了解GCC生成的代码的细微差别(及其局限性),您可以这样看。这基本上是为了使用引导加载程序的BIOS中断进行打印而设计的。无论如何,这几乎是一个完全相同的副本,在您的情况下,问题出在这一行
\uuu asm\uu(“.code16\n”)。它应该是
\uuuu asm\uuuuu(“.code16gcc\n”)。使用
.code16
会弄乱堆栈,因为它将假定16位调用(而不是GCC真正期望的32位调用)。这将导致参数出现在堆栈上的错误位置。我本来打算写一个答案,但发现这个问题在
main
的底部几乎是重复的,没有地方可以返回。因此,你应该考虑在<代码>主语< /代码>的底部放置一个无限循环,如“代码”>(1);<代码>或使用
\u asm\u(“cli;hlt;”)我还建议,如果您正在使用OpenWatcom瞄准16位代码,并且您正在考虑从引导加载程序执行此操作,那么链接器就是天赐良机。不幸的是,它是一个没有源代码的DOS程序,但在Linux上可以很好地与DOSEMU之类的仿真器一起运行。由于您给出的原因,我不建议对16位代码使用GCC。然而,这实际上是可行的,但前提是你对引擎盖下发生的事情有很好的理解。指针的所有端都会有问题,但如果整个程序可以在第一个64k内存中加载到内存中,并且如果CS=DS=ES=SS=0,则问题可以最小化。当然,GCC的
.code16
指令生成的所有代码都不会在386处理器(8086/80186/80286和其他变体)之前的任何处理器上运行。我还建议,如果您使用OpenWatcom瞄准16位代码,并且您正在考虑从引导加载程序执行此操作,那么链接器就是天赐良机。不幸的是,它是一个没有源代码的DOS程序,但在Linux上可以很好地与DOSEMU之类的仿真器一起运行。由于您给出的原因,我不建议对16位代码使用GCC。然而,这实际上是可行的,但前提是你对引擎盖下发生的事情有很好的理解。指针的所有端都会有问题,但如果整个程序可以在第一个64k内存中加载到内存中,并且如果CS=DS=ES=SS=0,则问题可以最小化。当然没有