OSD Home

C++ calling conventions

C++ uses the same calling conventions as C.

For C++ nonstatic member functions (methods), the first slot in the stack frame is reserved for the hidden pointer this.

C++ name-mangling

With C++ polymorphism, functions in different classes or functions with different argument lists may have the same name. To avoid confusing the linker, the function names in the object code are altered depending on their class or argument list. Example:
        hello.o:     file format coff-go32
	Disassembly of section .text:
	00000000 <.text>:
	00000008 <_greet__Fv>:
	int greet(void) { cout << "hello" << endl; return 0; }
	   8:   55              push   %ebp
	   9:   89 e5           mov    %esp,%ebp
	   b:   83 ec 08        sub    $0x8,%esp
           e:   83 c4 f8        add    $0xfffffff8,%esp
greet has become _greet__Fv

Note that there is no standard for name-mangling -- different compilers and different versions of the same compiler do it differently.

Name-mangling can be turned off for external C or asm functions by declaring prototypes for those functions with C linkage:

extern "C" void foo(void);

extern "C" {
extern int errno;
void exit(int status);
int putchar(int c);
};
The GCC binutils for C++ contain a utility called c++filt (or, for DJGPP, cxxfilt) which demangles C++ symbol names:
	C:\tmp>nm test.cof
	00000000 a .absolut
		...
	00001130 T __ZdlPv
	00001140 T __ZdaPv
	00001150 T _main
		...
	00005ad0 N .comment

	C:\tmp>echo __ZdaPv | cxxfilt
	operator delete[](void*)
nm -C ... will also display de-mangled symbol names.

Built-in functions required by C++

void *operator new(size_t size); /* wrapper around calloc() */
void *operator new[](size_t size); /* wrapper around calloc() */
void operator delete(void *arg); /* wrapper around free() */
void operator delete[](void *arg); /* wrapper around free() */

These must be compiled as C++. Do not bracket them with extern "C" { ... };. Maybe compile with "-fno-builtin" too, so GCC doesn't try to use it's own versions of these functions. Defining these functions like this:
void *__builtin_new(unsigned long a);
void __builtin_delete(void *p);
etc.
works only with GCC 2.x

Static constructors

For each global and static local C++ object, GCC creates a small subroutine to construct the object. Pointers to these routines (not the routines themselves) are stored as simple, linear arrays in the .ctors section of the object file. Similarly, pointers to global/static destructor routines are stored in .dtors.

The kernel linker script can simply copy the .ctors and .dtors sections to the output (executable) file, or it can combine these sections with .data, .rodata, or even .text. Labels should be defined in the linker script to mark the start and end of these sections.

In older (2.x) versions of GCC, the section names ".ctor" and ".dtor" were used instead of ".ctors" and ".dtors". Maybe use a wildcard for these names in your linker script.

The constructors should be called from the kernel startup code. They can also be called from main() before accessing global or static local objects (see example linker script below).

Other C++ issues

The linkonce sections are related to C++, but I don't know how, exactly (maybe used for vtables and templates...?). These must be combined into .text, .rodata, .data, or .bss by the linker script.

If you don't use C++ structured exception handling (try, catch, throw), compile with "gcc -fno-exceptions ...". Otherwise, you will get undefined symbols at link time.

Unless you need it, turn off run-time type information by compiling with "gcc -fno-rtti ...". Otherwise, you will get undefined symbols at link time.

Code snippets

Linker script for a C++ kernel:
ENTRY(entry)
SECTIONS {
/* use ld -Ttext=nnn ... to set starting virtual address */
    .text : {
        g_code = .; _g_code = .;    /* symbols to mark start of section */
/* code */
        *(.text*)                   /* wildcard for "gcc -ffunction-sections" */
        *(.gnu.linkonce.t.*)        /* C++ templates? */
        *(.rodata*)                 /* read-only data (ELF only) */
	*(.gnu.linkonce.r.*)
/* C++ static constructors and destructors */
        start_ctors = .;
	*(SORT(.ctor*))
	end_ctors = .;              /* accessed from asm code only... */
	start_dtors = .;            /* ...so no leading underscore */
	*(SORT(.dtor*))
	end_dtors = .;
	. = ALIGN(4096);
    }
    .data : {
        g_data = .; _g_data = .;
/* data */
        *(.data*)                   /* wildcard for "gcc -fdata-sections" */
	*(.gnu.linkonce.d.*)
	. = ALIGN(4096);
    }
    .bss : {
        g_bss = .; _g_bss = .;
/* BSS */
        *(.bss*)                    /* wildcard for "gcc -fdata-sections" */
	*(.gnu.linkonce.b.*)
        *(COMMON)                   /* "common" variables */
	. = ALIGN(4096);
    }
    g_end = .; _g_end = .;
/* MinGW debug sections
Omit these and you may get an invalid executable file */
    .stab : {
	*(.stab)
    }
    .stabstr : {
	*(.stabstr)
    }
}
Assembly-language startup code to call the static constructors before calling main() (assuming the linker script shown above):
        mov $start_ctors,%ebx
        jmp 2f
.1:
        call *(%ebx)
        add $4,%ebx
.2:
        cmp $end_ctors,%ebx
        jb 1b
Constructors can also be called from main(), but before accessing global or static local objects.

Links

TO DO

- how to declare pure_virtual(void) ?
- say "ld --verbose" and check built-in linker script
  for details...varies between DJGPP, MinGW, and Linux
- Strange sections seen in MinGW linker script:
        .text$*  .data$* .glue_7t  .glue_7
- RTTI?
- C++ exceptions?
- making static constructor code discardable?

REPORT BUGS OR ERRORS IN THIS DOCUMENT