C++ has two kinds of implementations, hosted and freestanding. Hosted implementations do assume that malloc
is present and often do use it for internal purposes. Freestanding implementations assume that only the new
function is present, because it supports the C++ keyword new
, but it is easy to ensure that this function doesn't get called.
The difference between the two is that in a freestanding implementation, you can control program startup and the set of required headers and libraries is limited. Program startup is controlled by setting the entry point.
g++ -ffreestanding -e _entry program.cpp
program.cpp
might be:
extern "C" int entry()
{
return 0;
}
The extern "C"
is necessary to prevent C++ name mangling, which might make it difficult to figure out what the name of entry
is during linking. Then, don't use new
, std::string
, stream I/O, STL, or the like, and avoid at_exit
.
This should give you control over the startup and cleanup code and limit what the compiler can implicitly rely on being available from the standard library. Note, however, that this can be a challenging environment. Not only will you prevent initialization of heaps, I/O streams, and the like, but you will also prevent setup of exceptions, RTTI, and the calling of static storage object constructors, among other things. You will have to write code or use libraries to manually opt into several features of C++ you might want to use. If you go this route, you may want to peruse this wiki http://wiki.osdev.org/C%2B%2B. You may want to at least call global constructors, which is pretty easy to do.
Explanation
The standard
C++ has the notion of a "freestanding implementation", in which fewer headers are available.
3.6.1 Main function [basic.start.main]
1 [snip] It is implementation-defined whether a program in a freestanding environment is required to define a main function.
17.6.1.3 Freestanding implementations [compliance]
1 Two kinds of implementations are defined: hosted and freestanding (1.4). For a hosted implementation, this
International Standard describes the set of available headers.
2 A freestanding implementation has an implementation-defined set of headers. This set shall include at least
the headers shown in Table 16.
3 The supplied version of the header <cstdlib> shall declare at least the functions abort, atexit, at_quick_exit, exit, and quick_exit (18.5). [snip]
Table 16 lists ciso646
, cstddef
, cfloat
, limits
, climits
, cstdint
, cstdlib
, new
, typeinfo
, exception
, initializer_list
, cstdalign
, cstdarg
, cstdbool
, type_traits
, and atomic
.
Most of the above headers contain simple definitions of types, constants, and templates. The only ones that may seem problematic are typeinfo
, exception
, cstdlib
, and new
. The first two support RTTI and exceptions, respectively, which you can ensure are disabled with additional compiler flags. cstdlib
and new
you can simply ignore by not calling exit
/at_exit
and not using new
expressions. The reason for avoiding at_exit
is it might call new
internally. Nothing else in the freestanding fragment should call call anything in cstdlib
or new
.
The options
The most important option above is -e _entry
, which gives you control over what runs when your program starts.
-ffreestanding
tells the compiler and the standard library (rather, its freestanding fragment) not to assume that the entire standard library is present (even if it still is). It may prevent the generation of surprising code. Note that this option doesn't actually restrict which headers are available to you. You can still use iostream
, for instance, though it may be a bad idea if you also changed the entry point. What it does is it prevents the code supporting the freestanding headers from calling anything outside the freestanding headers, and prevents the compiler from generating any such calls implicitly.