There are times when you do not want to use the standard C++ library and also want to remove the dependency of your binary on the standard C++ library. Some of the binary dependencies are automatically created by the compiler when you compile your code without using any library explicitly. For example write this code-
int main() { return 0; }
Ideally you can compile only with the nostdlib option. You will need to implement exception handling ABI. But we will not go through that route. The technique here described is with all three compiler options.
The 1st issue can be solved by calling the constructors of all global variables yourself. You have to do what stdlib does for calling constructors of global and static variables. Here is code to do that:
void callConstructors() { void (**constructor)() = &__CTOR_LIST__ ; int total = *(int *)constructor ; constructor++ ; while(total){ (*constructor)() ; total-- ; constructor++ ; } } void callDestructors() { void (**deconstructor)() = &__DTOR_LIST__ ; int total = *(int *)deconstructor ; deconstructor++ ; while(total){ (*deconstructor)() ; total-- ; deconstructor++ ; } }
The __CTOR_LIST__ is the list which contains the function pointers of functions which call the constructors of global and static variables. The first entry of the list contains the number of such pointers then all function pointers.
Similarly the __DTOR_LIST__ contains the function pointers of function which calls the destructors of static and global variables.
The functions which call constructors and destructors are generated by the compiler and their address are stored in sections called .ctors and .dtors respectively. The __CTOR_LIST__ and __DTOR_LIST__ is created using these sections and with the help of a linker script.
ENTRY(entry) LOAD_VIR = 0x100000; LOAD_PHYS = 0x100000; SECTIONS { .text LOAD_VIR : AT(LOAD_PHYS) { _linker_code = .; *(.text) *(.text.*) *(.rodata*) *(.gnu.linkonce.t*) *(.gnu.linkonce.r*) . = ALIGN(4096); } .data : { _linker_data_start = .; __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)* (.ctors) LONG(0) __CTOR_END__ = .; __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)* (.dtors) LONG(0) __DTOR_END__ = .; _linker_data = .; *(.data) *(.data.*) *(.gnu.linkonce.d*) . = ALIGN(4096); } .bss : { _linker_bss = .; *(.bss) *(.bss.*) *(.gnu.linkonce.b.*) *(.COMMON) . = ALIGN(4096); } _linker_end = .; }
extern "C" { extern void (*__CTOR_LIST__)() ; extern void (*__DTOR_LIST__)() ; } void callConstructors() { void (**constructor)() = &__CTOR_LIST__ ; int total = *(int *)constructor ; constructor++ ; while(total){ (*constructor)() ; total-- ; constructor++ ; } } void callDestructors() { void (**deconstructor)() = &__DTOR_LIST__ ; int total = *(int *)deconstructor ; deconstructor++ ; while(total){ (*deconstructor)() ; total-- ; deconstructor++ ; } } int main() { callConstructors(); // Call you code callDestructors(); } void* operator new (unsigned size) { // call your memory allocation routine } void * operator new[] (unsigned size) { // call your memory allocation routine } void operator delete (void*) { // call your memory free routine } void operator delete[] (void*) { // call your memory free routine } extern "C" void __cxa_pure_virtual (){} void * __dso_handle=0; extern "C" void __cxa_atexit(){}
These commands will produce a.out binary from the file a.cpp. If you have more then one file, then compile each file separately and use the linker to link all object files into an executable file.