gcc – Bootloader and OS kernel are not linked together

Question:

I tried to write a primitive OS based on the "Bare Bones" tutorials on the osdev wiki. The problem is that even at the very beginning, ld throws an error that the main kernel function, _kmain , is undefined. So here's the code:

kernel.cpp

    void _kmain() {  
        return;  
    }

boot.asm

    ; This file is loaded by GRUB, Protected Mode is already enabled

    ; Multiboot constants

    MBALIGN equ 1<<0
    MEMINFO equ 1<<1
    FLAGS equ MBALIGN | MEMINFO
    MAGIC equ 0x1BADB002
    CHECKSUM equ -(MAGIC+FLAGS)

    ; Multiboot header

    section .multiboot
    align 4 ; Yes, I know, it's not necessary
        dd MAGIC
        dd FLAGS
        dd CHECKSUM

    ; Bootloader stack (we'll use another stack for OS, of course)

    section .bootstrap_stack, nobits
    align 4
        stack_bottom:
            resb 16384 ; 16K of stack, seems enough for a bootloader
        stack_top:

    section .text
        global _start
        _start:
        mov esp, stack_top ; Stack grows in a backwards direction
        extern _kmain
        call _kmain
        cli ; If _kmain returns, we will halt the computer
    .halt:
        hlt
        jmp .halt

Makefile

    ASM=nasm
    ASM_FLAGS=-felf32
    CC=/home/alexander/opt/cross/bin/i686-elf-g++
    CFLAGS=-ffreestanding -Wall -Wextra
    LINKER=/home/alexander/opt/cross/bin/i686-elf-gcc
    LFLAGS=-ffreestanding -nostdlib -lgcc -T linker.ld

    all: boot.o kernel.o
        $(LINKER) $(LFLAGS) -o kernel.bin $^
    boot.o:
        $(ASM) $(ASM_FLAGS) boot.asm -o boot.o

    kernel.o:
        $(CC) $(CFLAGS) -c kernel.cpp -o kernel.o

    screen.o:
        $(CC) $(CFLAGS) -c screen.cpp -o screen.o

    clean:
        rm *.o

    rmbaks:
        rm *~

linker.ld

    ENTRY(_start)
    SECTIONS {
        . = 1M;
        .text BLOCK(4K) : ALIGN(4K) {
            *(.multiboot)
            *(.text)
        }
        .rdata BLOCK(4K) : ALIGN(4K) {
            *(.rdata)
        }
        .data BLOCK(4K) : ALIGN(4K) {
            *(.data)
        }
        .bss BLOCK(4K) : ALIGN(4K) {
            *(COMMON)
            *(.bss)
            *(.bootstrap_stack)
        }
    }

Output after running make :

nasm -felf32 boot.asm -o boot.o
/home/alexander/opt/cross/bin/i686-elf-g++ -ffreestanding -Wall -Wextra -c kernel.cpp -o kernel.o
/home/alexander/opt/cross/bin/i686-elf-gcc -ffreestanding -nostdlib -lgcc -T linker.ld -o kernel.bin boot.o kernel.o
boot.o: In function `_start':
boot.asm:(.text+0x6): undefined reference to `_kmain'
collect2: error: ld returned 1 exit status
make: *** [all] Ошибка 1

PS In screen.cpp all sorts of useful functions for working with the display, but it is not used.

Answer:

In fact, everything just depends on the compiler. You have g ++ (not gcc).

By default, g ++ forms function names (those that we can see with the nm kernel.o ) taking into account the function type and parameters. So, instead of the expected _kmain , in .o we get _Z6_kmainv .

If you still want to continue with your C ++ exercises, you will have to explicitly tell the compiler to use C-style function names. To do this, it is enough to write function prototypes in a special block:

extern "C" {
  void _kmain(void); // это наш случай
};

If you want the code to be compiled with gcc as well (of course, the rest of it must be compatible with both C and C ++, of course), you will have to add a few preprocessor directives (fortunately, it is the same for g ++ / gcc)

#ifdef __cplusplus
extern "C" {
#endif
  void _kmain(void);
  ...
#ifdef __cplusplus
};
#endif
Scroll to Top