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 polymorphicoperator<<(ostream&, ...)
method for convenient, type-agnostic output. - Exceptions cannot be used (i.e.
try
,catch
andthrow
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 usegcc
here instead ofg++
becauseg++
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
You can safely get rid of the 0 check around the call to free(). free() already checks for a NULL argument.
ReplyDeleteIs it really impossible to throw exceptions? even if you throw your own types that don't depend on std::exception? Why?
ReplyDelete@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.
ReplyDeleteI just tried the method cross-compiling to an embedded MIPS system running Linux and it works :)
ReplyDeleteThanks,
Soyeb
This is awesome, exactly what I was looking for in trying to use a small amount of C++ code in a C project for an embedded micro (where I don't have anywhere near enough room to fit libstdc++).
ReplyDelete