编写一个简单的 Bootloader HelloWorld - 错误函数打印字符串

Write a simple Bootloader HelloWorld - Error function print string

我尝试创建一个打印 "hello world".

的简单引导加载程序

当我调用一个只打印 "hello world" 的函数时我可以做到这一点,但是当我调用一个函数来打印特定字符串时,什么也没有发生。

为此,我使用了两个文件。第一个是 boot.ld,第二个是 boot.cpp(它也可以在 C 中使用 boot.c)。

首先,我从我的终端创建软盘:

dd if=/dev/zero of=floppy.img bs=512 count=2880

其次,我编译代码(boot.cpp和boot.ld):

gcc -c -g -Os -m64 -ffreestanding -Wall -Werror boot.cpp -o boot.o

ld -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o

objcopy -O binary boot.elf boot.bin

最后,我将 boot.bin 添加到 floppy.img:

dd if=boot.bin of=floppy.img

现在我们只需要从 VirtualBox 的存储面板添加软盘并启动我们的虚拟机。

源码

来自:http://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part

boot.ld

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

boot.cpp(或boot.c)

void cout();

void main()
{
    cout();
}

void cout()
{
    __asm__ __volatile__("movb $'h' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'e' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'o' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $' ' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'w' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'o' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'r' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");

    __asm__ __volatile__("movb $'d' , %al\n");
    __asm__ __volatile__("movb [=11=]x0e, %ah\n");
    __asm__ __volatile__("int  [=11=]x10\n");
}

输出:

有漏洞的源代码

boot.cpp(或boot.c)

void cout(const char* str);

void main()
{
    cout("hello world");
}

void cout(const char* str)
{
    while(*str)
    {
        __asm__ __volatile__ ("int [=12=]x10" : : "a"(0x0e00 | *str), "b"(0x0007));
        ++str;
    }
}

输出:

为什么输出为空?

我的函数有什么问题?

我忘记了什么?

感谢您的帮助。

在我的交叉编译器 (i686-elf-gcc (GCC) 4.9.2) 上,后面的代码生成以下 (dis) 程序集:

    boot.o:     file format elf32-i386


Disassembly of section .text:

00000000 <cout>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   53                      push   %ebx
   4:   bb 07 00 00 00          mov    [=10=]x7,%ebx
   9:   8b 55 08                mov    0x8(%ebp),%edx
   c:   0f be 02                movsbl (%edx),%eax
   f:   84 c0                   test   %al,%al
  11:   74 08                   je     1b <cout+0x1b>
  13:   80 cc 0e                or     [=10=]xe,%ah
  16:   cd 10                   int    [=10=]x10
  18:   42                      inc    %edx
  19:   eb f1                   jmp    c <cout+0xc>
  1b:   5b                      pop    %ebx
  1c:   5d                      pop    %ebp
  1d:   c3                      ret

我很想知道您是否将 GCC(非 16 位兼容编译器)与 16 位内容(BIOS 中断)一起使用。如果您要编写 16 位代码,请使用完整的汇编代码! GCC 只会把它搞砸,因为它生成的 32 位代码将 运行 在 16 位模式下 。如果你想直接上C/C++,那么你要写的很可能不是bootloader,而是一个kernel. In such a (common) case, read the unquestionable sacred ritual to initiate you into OSDev. The fact that your first example works is just luck, and any minimal change may break everything, even leading to the mythical horrifying triple fault, nightmares of kernel panics themselves.

无论如何,你最好直接写入 VGA DMA 内存而不是使用 BIOS 调用(你需要进入 protected mode first, and setup the VGA hardware and modes (GRUB 会为您完成此操作,但您正在创建引导加载程序,不是吗?)):

void PrintString(const char *str) {
    uint16_t *vga = (uint16_t*)0xB8000;

    for(; *str != '[=11=]'; str++, vga++)
        *vga = ((uint16_t)0x07 << 8) | *str; // Light grey on a black background, nice!
}

顺便说一句,您可能会找到 the OSDev community, wiki, andforums very useful. And, as shown in the comments, you should be using .code16 for real mode 代码,并且您链接的文章已经显示了它的年龄。

感谢@MichaelPetch 的回答。

源代码:

boot.ld

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

boot.cpp

也在这里:http://pastebin.com/6NV3UMjE

asm(".code16gcc");
__asm__("jmpl [=11=]x0000, $main\n");

void cout(const char* str);

void main()
{
    __asm__ __volatile__ ("xor %ax, %ax\n");
    __asm__ __volatile__ ("mov %ax, %ds\n");
    cout("Hello World");
    __asm__ __volatile__("cli\n");
    __asm__ __volatile__("hlt\n");
}

void cout(const char* str)
{
    while(*str)
    {
        __asm__ __volatile__("int [=11=]x10" : : "a"(0x0e00 | *str), "b"(0x0007));
        ++str;
    }
}

编译:

gcc -c -g -O0 -m32 -ffreestanding -Wall -Werror boot.cpp -o boot.o

ld -melf_i386 -static -Tboot.ld -nostdlib --nmagic -o boot.elf boot.o

objcopy -O binary boot.elf boot.bin

dd if=boot.bin of=floppy.img conv=notrunc

输出: