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

时间:2015-10-04 07:26:19

标签: c++ c gcc assembly bootloader

我尝试创建一个打印“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的存储面板添加软盘并启动我们的虚拟机。

FloppyImage

源代码

来自: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 $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'e' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'o' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $' ' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'w' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'o' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'r' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

    __asm__ __volatile__("movb $'l' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");

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

输出:

TestBootLoader1

错误的源代码

boot.cpp(或boot.c)

void cout(const char* str);

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

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

输出:

TestBootLoader2

为什么输出为空?

我的功能出了什么问题?

我忘了什么?

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

在我的交叉编译器(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    $0x7,%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     $0xe,%ah
  16:   cd 10                   int    $0x10
  18:   42                      inc    %edx
  19:   eb f1                   jmp    c <cout+0xc>
  1b:   5b                      pop    %ebx
  1c:   5d                      pop    %ebp
  1d:   c3                      ret

我对你是否正在使用具有16位内容(BIOS中断)的GCC(非16位兼容编译器)非常感兴趣。如果您要执行16位代码,请在完全组装中执行此操作! GCC会简单地搞砸它,因为它生成的32位代码将在16位模式下运行 。如果你想直接转到C / C ++,那么你想写的东西可能不是引导程序,而是kernel。在这种(常见)情况下,请阅读the unquestionable sacred ritual to initiate you into OSDev。你的第一个例子运作的事实只是运气,任何微小的改变都可能破坏一切,甚至导致the mythical horrifying triple fault, nightmares of kernel panics themselves

无论如何,你最好直接写入VGA DMA内存而不是使用BIOS调用(你需要先到protected modesetup the VGA hardware and modes  (GRUB为你做了这个,但是你要创建一个bootloader,不是吗?)):

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

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

BTW ,您可能会发现the OSDev community, wiki, andforums非常有用。而且,如评论中所示,您应该使用.code16代码real mode,并且您的链接文章已经显示其年龄。

答案 1 :(得分:2)

感谢@ 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 $0x0000, $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 $0x10" : : "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

<强>输出:

Output