C++ Bare Bones: Difference between revisions

m
[unchecked revision][unchecked revision]
(Added source tags)
 
(36 intermediate revisions by 14 users not shown)
Line 1:
#REDIRECT [[Bare Bones]]
{{Rating|2}}
:''This tutorial sets up the most basic bootable C++ kernel. The theory can be found in [[C++]].''
 
{{Redirect page}}
If you have a [[Bare bones|GRUB-booting C kernel]] (please read and follow through that document first!), extending it to C++ isn't that difficult. All that is needed are a couple of lines to cater for C++ specifics.
 
==Linker Script==
Using C++ requires you to take a few extra sections into account:
 
{| {{wikitable}}
|-
! Section
! Name
! Description
|-
! ctor
| C++ static/global constructors
| Constructors for static objects used in C++ (must be explicitely called, see below)
|-
! dtor
| C++ static/global destructors
| Destructors for static objects used in C++ (ditto, though the use is open for discussion)
|-
! gnu.linkonce
| GCC vague linkages
| Sections dedicated for GCC's vague linking (see [http://gcc.gnu.org/onlinedocs/gcc/Vague-Linkage.html#Vague-Linkage the documentation] for more information)
|}
 
Because there is no environment executing your kernel (you can't expect the bootloader to do this), you have to execute your own constructors (and possibly destructors). Both are described below. There is however discussion about whether executing the global destructors in your kernel makes any sense when you're shutting down (seeing as you're shutting the computer or OS down). It would probably be best to call these destructors only if you have specific object destructors that need to clean something up before the kernel is shut down (and you can't or don't want to explicitely call these). Note also that you should call the destructors in '''reverse''' order! This to make sure objects depending on other objects to exist may result in problems if not done correctly.
 
The vague linking sections should be split accross multiple sections: you should put them in text, rodata, data and bss. Here is an example linker script:
 
<pre>
SECTIONS
{
. = 0x100000;
 
.text ALIGN(4096) :
{
*(.text*)
*(.gnu.linkonce.t*)
}
 
.rodata ALIGN(4096) :
{
*(.rodata*)
*(.gnu.linkonce.r*)
}
.data ALIGN(4096) :
{
start_ctors = .;
*(.ctor*)
end_ctors = .;
 
start_dtors = .;
*(.dtor*)
end_dtors = .;
*(.data*)
*(.gnu.linkonce.d*)
}
 
.bss ALIGN(4096) :
{
*(.COMMON*)
*(.bss*)
*(.gnu.linkonce.b*)
}
/DISCARD/ :
{
*(.comment)
*(.eh_frame) /* You should discard this unless you're implementing runtime support for C++ exceptions. */
}
}
</pre>
 
The script might require modification to suit your kernel's needs. Note that forgetting to add the vague linking sections might result in GRUB randomly not being able to load your kernel anymore (e.g. after modifying the most trivial code). It might also explain sudden rises in executable size by 50 kB and other issues.
 
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.
 
==Calling Constructors & Destructors==
The constructors have to be executed as quickly as possible after booting your kernel (just make sure you execute them before you actually use any of the objects that require them). Destructors should preferably be called somewhere before your kernel shuts the computer down or simply returns from the main function.
 
Below you'll find a couple of examples on how to do this.
 
===NASM===
<source lang="asm">
extern start_ctors, end_ctors, start_dtors, end_dtors
 
loader:
; Possibly set up a stack here: mov esp, stack + STACKSIZE
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
</source>
 
===GAS===
<source lang="asm">
loader:
# Possibly set up a stack here: mov $(stack + STACKSIZE), %esp
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
</source>
 
===C/C++===
<source lang="cpp">
extern start_ctors, end_ctors, start_dtors, end_dtors;
 
void loader(void)
{
//- call all the static constructors in the list.
for(unsigned long *constructor(&start_ctors); constructor < &end_ctors; ++constructor)
((void (*) (void)) (*constructor)) ();
 
//- call kernel proper
main();
 
//- call all the static destructors in the list.
for(unsigned long *destructor(&start_dtors); destructor < &end_dtors; ++destructor)
((void (*) (void)) (*destructor)) ();
}
</source>
 
==Entry.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 and you can call it from your multiboot header Assembly file:
 
<source lang="cpp">
extern "C" void main(struct mb_header *header, unsigned magic)
{
// write your kernel here
}
</source>
 
==Notes==
In this code '''loader''' is the entry point to the binary. Make sure that your linker script has something like
 
<pre>
ENTRY(loader)
</pre>
 
'''loader''' should in its turn call "main" (or something like "_main" if your linker requires you to add a leading underscore).
 
==Compiler Options==
The options for g++ are slightly different than when building a C kernel.
 
<source lang="bash">
g++ -o main.o -c kernel.cpp -Wall -Wextra -Werror -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector
</source>
 
The reasons why most of these are disabled is because they usually require runtime support (which you in a basic kernel don't have). For a more thorough explanation, see the [[C++]] article. There is also an article about the [[GCC_Stack_Smashing_Protector|stack smashing protector]] if you want to enable --stack-protector.
 
==Questions==
;Does anyone know if those '''.ctor'''s and '''.dtor'''s 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 most implementations.
 
;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 inter-dependencies at an absolute minimum.
 
==See Also==
===Articles===
*[[C++]]
*[[Bare bones]]
 
[[Category:Bare bones tutorials]]
[[Category:C++]]
409

edits