Working Without Standard Library


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;
}

Now compile this code with g++ compiler as:
g++ a.cpp
This will generate a.out Then to see what libraries on which there is a dependency created, run this command:
ldd a.out

It will show you a bunch of libraries on which the a.out depends. To remove this dependency, compile the code as:
g++ -nostdlib -fno-exceptions -fno-rtti a.cpp
This will generate a.out which will have no dependency on any other library.

Here are the meanings of the compiler options used above:
nostdlib: do not use standard libraries.
fno-exceptions: disable the exception handling.
fno-rtti: disable runtime type identification.

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.


When do you need to compile without the standard library?
There are many cases. Here are a few:-
Things to take care if there is no support for C++ in your system

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.

Here is the 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 = .;
}

This linker script does these things:
Now lets see the code, compiling and linking steps:
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(){}

Save this code a.cpp. Then use these commands:
g++ -c -nostdlib -fno-exceptions -fno-rtti a.cpp
ld -T ldscriptFile a.o -o a.out

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.


up

Want to learn (C/C++ internals) directly from the author using video call? Learn more

Do you collaborate using whiteboard? Please try Lekh Board - An Intelligent Collaborate Whiteboard App