C++ Bare Bones

From OSDev.wiki
Jump to navigation Jump to search
This tutorial sets up only the most basic bootable C++ kernel. For more information on C++ kernels see C++.

If you have a GRUB-booting C kernel (please read and follow through that document first!), extending it to C++ isn't difficult. All that is needed are a couple of lines to cater for C++ specifics.

linker.ld

Using C++ adds two more sections of interest to your kernel binaries: The constructors and destructors of static objects. You have to modify the linker script to include them.

.data ALIGN (0x1000) : {
   start_ctors = .;
   *(.ctor*)
   end_ctors = .;
   start_dtors = .;
   *(.dtor*)
   end_dtors = .;
   *(.data)
}

loader.s

The static constructors have to be called before you enter the main kernel function, and the destructors have to be called after that function returns. This is done by modifying loader.s as follows:

NASM

extern start_ctors, end_ctors, start_dtors, end_dtors

_loader:
   mov esp, stack+STACKSIZE        ; set up the stack
   push eax                        ; Multiboot magic number
   push ebx                        ; Multiboot info structure

static_ctors_loop:
   mov ebx, start_ctors
   jmp .test
.body:
   call [ebx]
   add ebx,4
.test:
   cmp ebx, end_ctors
   jb .body

   call _main                      ; call kernel proper

static_dtors_loop:
   mov ebx, start_dtors
   jmp .test
.body:
   call [ebx]
   add ebx,4
.test:
   cmp ebx, end_dtors
   jb .body

   hlt                             ; halt machine should kernel return

GAS

_loader:
   mov  $(stack + STACKSIZE), %esp # set up the stack
   push %eax                       # Multiboot magic number
   push %ebx                       # Multiboot data structure

   # calling static constructors
   mov  $start_ctors, %ebx
   jmp  2f
1:
   call *(%ebx)
   add  $4, %ebx
2:
   cmp  $end_ctors, %ebx
   jb   1b

   call _main                      # call kernel proper

   # calling static destructors
   mov  $start_dtors, %ebx
   jmp  4f
3:
   call *(%ebx)
   add  $4, %ebx
4:
   cmp  $end_dtors, %ebx
   jb   3b

   hlt                             # halt machine should kernel return

C/C++

typedef unsigned long vintp; //- integer type to store a pointer.

extern start_ctors, end_ctors, start_dtors, end_dtors;

void _loader(void) {
    //- call all the static constructors in the list.
    for(vintp * call = &start_ctors; call < &end_ctors; call++) {
        ((void (*)(void))*call)();
    }

    //- call kernel proper
    main();

    //- call all the static destructors in the list.
    for(vintp * call = &start_dtors; call < &end_dtors; call++) {
        ((void (*)(void))*call)();
    }
}

main.cpp

Now, all that is needed is to declare C style linkage for the kernel entry function, so that its name will not get mangled to C++ linkage style:

extern "C" void _main(multiboot_data* mbd, unsigned int magic);

void _main( void* mbd, unsigned int magic )
{
   // write your kernel here
}

Questions

Does anyone know if those .ctors and .dtors are specific to some compiler or if there's an ABI stating them ?
It's defined in the ELF ABI for System V platforms, but it's used by most unices. The concept of using a constructor/destructor list for bootup is not so much specified by any C++ ABI, but it is used in pretty much all implementations (not checked, but probably all).
Does anyone know how to control the order of the static ctor's ?
It is assumed that the compiler puts them into the correct order. However, it is probably best to keep interdependencies at an absolute minimum.

See Also

Articles