22

我是编写引导加载程序的新手。我已经用 asm 编写了一个 helloworld 引导加载程序,现在我正在尝试用 C 编写一个。我已经用 C 编写了一个 helloworld 引导加载程序,但我无法编译它。

这是我的代码。我究竟做错了什么?为什么编译不出来?

void print_char();
int main(void){
char *MSG = "Hello World!";
int i;

__asm__(
    "mov %0, %%SI;"
    :
    :"g"(MSG)
);
for(i=0;i<12;i++){
    __asm__(
        "mov %0, %%AL;"
        :
        :"g"(MSG[i])
    );
    print_char();
}

return 0;
}

void print_char(){
__asm__(
    "mov $0X0E, %AH;"
    "mov $0x00, %BH;"
    "mov $0x04, %BL;"
    "int $0x10"
);
}
4

5 回答 5

17

让我在这里假设很多事情:你想在 x86 系统上运行引导加载程序,你在 *nix 机器上设置了 gcc 工具链。

编写引导加载程序时需要考虑以下几点:

  1. VBR 的 510 字节限制,由于分区表,MBR 的限制更小(如果您的系统需要一个)
  2. 实模式 - 16 位寄存器和 seg:off 寻址
  3. 引导加载程序必须是平面二进制文件,必须链接才能在物理地址 7c00h 处运行
  4. 没有外部“库”引用(呃!)

现在如果你想让 gcc 输出这样的二进制文件,你需要用它玩一些技巧。

  1. gcc 默认拆分出 32 位代码。要让 gcc 输出在实模式下运行的代码,__asm__(".code16gcc\n")请在每个 C 文件的顶部添加。
  2. gcc 在 ELF 中输出编译的对象。我们需要一个在 7c00h 静态链接的 bin。创建一个linker.ld包含以下内容的文件

    ENTRY(main);
    SECTIONS
    {    
        . = 0x7C00;    
        .text : AT(0x7C00)
        {
            _text = .;
            *(.text);
            _text_end = .;
        }
        .data :
        {
            _data = .;
            *(.bss);
            *(.bss*);
            *(.data);
            *(.rodata*);
            *(COMMON)
            _data_end = .;
        }    
        .sig : AT(0x7DFE)    
        {        
            SHORT(0xaa55);
        }    
        /DISCARD/ :
        {
            *(.note*);
            *(.iplt*);
            *(.igot*);
            *(.rel*);
            *(.comment);
            /* add any unwanted sections spewed out by your version of gcc and flags here */    
        }
    }
    
  3. 将您的引导加载程序代码写入bootloader.c并构建引导加载程序

    $ gcc -c -g -Os -march=i686 -ffreestanding -Wall -Werror -I. -o bootloader.o bootloader.c
    $ ld -static -Tlinker.ld -nostdlib --nmagic -o bootloader.elf bootloader.o
    $ objcopy -O binary bootloader.elf bootloader.bin
    
  4. 由于您已经使用 ASM 构建了引导加载程序,我想其余的对您来说是显而易见的。

- 取自我的博客:http ://dc0d32.blogspot.in/2010/06/real-mode-in-c-with-gcc-writing.html

于 2012-05-23T11:59:58.773 回答
7

引导加载程序是用 ASM 编写的。

在编译 C 代码(或 C++ 或其他)时,编译器会将您的人类可读代码“转换”为机器代码。所以你不能确定结果。

当 PC 启动时,BIOS 将执行来自特定地址的代码。该代码需要直接可执行。

这就是您将使用汇编的原因。这是拥有未经更改的代码的唯一方法,该代码将由处理器按编写的方式运行。

如果您想用 C 编写代码,您仍然需要编写 ASM 引导加载程序,该引导加载程序将负责正确加载您使用的编译器生成的机器代码。

您需要了解每个编译器会生成不同的机器代码,这可能需要在执行前进行预处理。

BIOS 不会让您预处理机器代码。PC 启动只是跳转到内存位置,这意味着位于该位置的机器代码将被直接执行。

于 2011-08-16T14:37:32.243 回答
2

由于您使用的是 GCC,因此您应该阅读有关不同“目标环境”的信息页面。您很可能想要使用-ffreestanding标志。我还必须使用-fno-stack-protector标志来避免编译器的一些丑陋的魔力。

然后,您将收到链接器错误,提示找不到memset等。所以你应该实现你自己的版本并将它们链接起来。

于 2011-08-16T15:04:12.587 回答
1

几年前我尝试过这个——选项可能已经改变。

您必须gcc使用-ffreestanding(don't link) 运行,然后使用ldflags进行链接-static-nostdlib

于 2011-08-16T15:29:26.087 回答
-3

据我所知,您不能用 C 编写引导加载程序。那是因为,C 需要您在 32 位保护模式下工作,而在引导加载程序中,某些部分处于 16 位模式。

于 2020-01-31T16:15:26.687 回答