iProgram
iProgram

Reputation: 6547

How to compile assembly and C together as bin format

I am trying to make my own operating system from scratch and am making my own boot loader. I have a function to print a string onto the screen.

Here is some code that I have:

ORG 0x7C00
BITS 16

mov si, msg
call Print

cli
hlt

Print:
  lodsb
  cmp al, 0
  je Done
  mov ah, 0Eh
  mov bh, 0
  int 10h
  jmp Print

Done:
  ret

msg db 'Hello World!', 0
times 510-($-$$) db 0
dw 0xAA55

This is then compiled with the following command:

nasm -f bin bootloader.asm -o bootloader.bin

The question is, how would I be able to access the print function within C? I know I have to use the extern keyword, but how would I compile this into a binary format file?

Upvotes: 3

Views: 1497

Answers (1)

user6889435
user6889435

Reputation:

Basically you have to run gcc with -ffreestanding (don't link) and then link using ld with the flags -static, -nostdlib.

Creating bootloader in C is not exactly good idea. I'd recommend you to get copy of GRUB and work on top of it. OSDEV wiki has explained this incredibly well.

To sum things up, whenever you'll try to create bootloader in C, use these to compile it:

$ gcc -m16 -c -g -Os -march=i686 -ffreestanding -Wall -Werror -I. -o bootloader.o bootloader.c
$ ld -static -T linker.ld -nostdlib --nmagic -o bootloader.elf bootloader.o
$ objcopy -O binary bootloader.elf bootloader.bin

Second thing, you can't use extern! You didn't set up stack, so C code will probably bail out pretty quickly. C compiler doesn't know in which format do you pass parameters to it, because your function doesn't follow any of usual conventions. Possible linker script:

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);  
    }
}

Also, GCC is by default emitting 32-bit code - you need to force it to generate 16-bit code using __asm__(".code16gcc\n") or, as suggested in comments, pass -m16 parameter to compilers' commandline.

You can rewrite your function to C (to make it complain any of calling conventions) like so:

void print(const unsigned char * s){
        while(*s){
                __asm__ __volatile__ ("int  $0x10" : : "a"(0x0E00 | *s), "b"(7));
                s++;
        }
}

And of course, right after .code16gcc, you'd have to jump directly to your bootloader start: __asm__ ("jmpl $0, $main\n");

Upvotes: 4

Related Questions