2010-12-19

How to write a C++ program without libstdc++

This blog post explains how to write a C++ program and compile with GCC (g++) so that the resulting binary doesn't depend on libstdc++. The reason for avoiding libstdc++ can be purity (don't depend on libraries you don't really need) and statically linking small tools (e.g. for Unix or Win32 console applications with MinGW).

The limitations are:

  • The standard C++ STL (e.g. #include <string> and #include <vector>) cannot be used. This functionality has to be reimplemented in the program.
  • The standard C++ streams (e.g. #include <iostream>) cannot be used. The standard C I/O library (e.g. #include <stdio.h>) is a smaller and faster replacement. A disadvantage: there is no polymorphic operator<<(ostream&, ...) method for convenient, type-agnostic output.
  • Exceptions cannot be used (i.e. try, catch and throw are disallowed).
  • RTTI (run-time type information) cannot be used.
  • dynamic_cast<...>(...) cannot be used (because it requires RTTI).

Here is how to do it:

  • Add the following C++ code to your program (can be in a separate source file):
    #include <stdlib.h>
    #include <unistd.h>  /* for write(), also available on Windows */
    extern "C" void* emulate_cc_new(unsigned len) { \
      void *p = malloc(len);
      if (p == 0) {
        /* Don't use stdio (e.g. fputs), because that may want to allocate more
         * memory.
         */
        (void)!write(2, "out of memory\n", 14);
        abort();
      }
      return p;
    }
    extern "C" void emulate_cc_delete(void* p) {
      if (p != 0)
        free(p);
    }
    void* operator new  (unsigned len) __attribute__((alias("emulate_cc_new")));
    void* operator new[](unsigned len) __attribute__((alias("emulate_cc_new")));   
    void  operator delete  (void* p)   __attribute__((alias("emulate_cc_delete")));
    void  operator delete[](void* p)   __attribute__((alias("emulate_cc_delete")));
    void* __cxa_pure_virtual = 0;
  • Compile your program with g++ -c to create individual .o files. Add flags -fno-rtti -fno-exceptions to get compile errors for disabled features (exceptions and RTTI).
  • Link your executable with gcc -o prog code1.o code2.o ... It's important that you use gcc here instead of g++ because g++ would link against libstdc++.

This method has been tested and found working with GCC 3.2, GCC 4.2.1 and GCC 4.4.1 (both native Linux compilation and MinGW compilation), and it probably works with other GCC versions as well.

If you need dynamic_cast, add void* __gxx_personality_v0 = 0;, don't use -fno-rtti, and add dyncast.a to the gcc -o ... command-line. Here is how to create dyncast.a (about 55kB) for GCC 4.4.1:

$ ar x /usr/lib/gcc/i486-linux-gnu/4.4/libstdc++.a \
  dyncast.o class_type_info.o si_class_type_info.o pointer_type_info.o \
  pbase_type_info.o tinfo.o fundamental_type_info.o
$ ar crs dyncast.a \
  dyncast.o class_type_info.o si_class_type_info.o pointer_type_info.o \
  pbase_type_info.o tinfo.o fundamental_type_info.o

4 comments:

landtuna said...

You can safely get rid of the 0 check around the call to free(). free() already checks for a NULL argument.

Alvaro said...

Is it really impossible to throw exceptions? even if you throw your own types that don't depend on std::exception? Why?

pts said...

@Alvaro: Exceptions need quite a few extra symbols to be linked in, which gcc doesn't have, so linking with gcc fails. Maybe it's possible to provide a reasonable emulation (just like for operator new), but implementing that doesn't look obvious for me.

Unknown said...

I just tried the method cross-compiling to an embedded MIPS system running Linux and it works :)

Thanks,

Soyeb