Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write a simple Bootloader HelloWorld - Error function print string

I try to create a simple bootloader which print "hello world".

I can do it when I call a function which only print "hello world", but when I call a function to print a specific string, nothing is happening.

For it, I use two files. The first one is boot.ld and the second is boot.cpp (it also work in C with boot.c).

Firstly, I create the floppy disk from my terminal:

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

Secondly, I compile the code (boot.cpp and 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

Lastly, I add boot.bin into floppy.img:

dd if=boot.bin of=floppy.img

Now we just need to add the floppy from the storage panel of VirtualBox and launch our Virtual Machine.

FloppyImage

The source code

from: 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 (or 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");
}

Output:

TestBootLoader1

The bugged source code

boot.cpp (or 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;
    }
}

Output:

TestBootLoader2

Why the output is empty?

What is wrong in my function?

I have forget something?

Thanks for your help.

like image 207
tirz Avatar asked Oct 04 '15 07:10

tirz


1 Answers

On my cross-compiler (i686-elf-gcc (GCC) 4.9.2), the later code produces the following (dis)assembly:

    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

I'm very interested in whether you're using GCC (a non-16-bit-compatible compiler) with 16-bit stuff (BIOS interrupts). If you're going to do 16-bit code, do it in full assembly! GCC will simply mess it up, because it's generating 32-bit code that will be run on 16-bit mode. If you want to go directly to C/C++, then what you want to write is probably not a bootloader, but a 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.

Anyway, you're better off writing directly to VGA DMA memory than using BIOS calls (you need to get to protected mode first, and setup the VGA hardware and modes (GRUB does this for you, but you're creating a bootloader, aren't you?)):

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, you may find the OSDev community, wiki, andforums very useful. And, as shown in the comments, you should be using .code16 for real mode code, and your linked article already shows its age.

like image 191
3442 Avatar answered Sep 28 '22 21:09

3442