Programming Pointers
Preventing dynamic allocation
Dan Saks
3/9/2010 1:29 PM EST
Last summer, I wrote a column entitled "Poor reasons for rejecting C++" in which I sought to dispel some misconceptions about C++.1 Among the many reader comments posted online were some valid concerns that merit further discussion. I'll address one of those concerns this month.
Some readers took exception to my statement that, "I know of no place where the C++ language performs dynamic allocation or recursion behind the scenes." As I explained a few months ago, I think of the C and C++ programming languages as distinct from their accompanying standard libraries.2 Although the various components in the Standard C++ library perform dynamic allocation behind the scenes, the language itself does not.
Nonetheless, you may be legitimately concerned that your C++ code will invoke a function that uses dynamic allocation against your wishes. In that case, you can choose among various techniques that will trigger a compile or link error to alert you that your code is using dynamic allocation.
Probably the simplest such technique is to replace the global operator new with a version that causes a link error. As I explained in an earlier column, a C++ new-expression allocates memory by calling a function named operator new.3 Each C++ environment provides a default implementation for a global operator new, declared as:
void *operator new(std::size_t n) throw (std::bad_alloc);
However, the C++ Standard lets you define a function with the same name and signature (parameter types) to replace the default implementation.4
To prevent using dynamic allocation, simply define a replacement version of operator new as follows:
// declare this function, but don't define it
void *operator_new_blocker();
void *operator new(std::size_t) throw (std::bad_alloc)
{
return operator_new_blocker();
}
Hence, a new-expression as in:
int *p = new int;
will compile, as always, to call operator new. When the linker drags the definition for operator new into the executable, it will also try to drag in operator_new_blocker as well. However, the link will fail because operator_new_blocker isn't defined. Any call to operator new will provoke a link error, whether the call occurs directly in your code or indirectly in library code that your code uses.
Compile errors are generally preferable to link errors because compile errors tend to be more accurate in pinpointing the location and describing the nature of translation errors. However, you can't use compile-time techniques to intercept calls to operator new in previously-compiled components that you're linking into your code. You have to rely on the linker.
You must place the replacement definition for operator new so that the linker will bind the definition into the executable program if and only if at least one call to operator new occurs somewhere in the program. That is, you must ensure that the linker won't incorporate the replacement definition unless the program actually calls operator new. If you inadvertently link operator new into your program, the linker will hunt for a definition for operator_new_blocker that it won't find, and you'll get spurious link errors.
Where you need to place your definition for operator new depends on your development tools. Many modern compilers and linkers employ some form of smart linking that will link into the executable only those external functions and data that the program actually uses. For example, if a single source file contains external definitions for functions f, g, and h, yet the program calls only f and h, a smart linker will link f and h into the program, but omit g. If you're using a compiler with a smart linker, you can place the definition for operator new in any source module that's compiled and linked as part of the build. A program that never calls operator new should still link without complaint.



KarlS
3/10/2010 1:33 PM EST
Hi Dan -- good info. Seems like there are many things unique to embedded C programming, and so far it looks like first embedded programmers are ridiculed because of poor practices, etc. But your articles are helpful.
What I have in mind is a parser(tool) that would scan the code and generate warnings for "bad" coding practice and rules/hints. I think a tool would let the designer/coder be able to focus more on the design.
I have a parser to offer as open source as a starting point, although it is a straight forward task in C# and probably C++.
What do you think? Thanks, Karl.
Sign in to Reply
willc2010
3/11/2010 12:09 AM EST
Programming by linker error (but only if you understand the particular linker). Welcome to the world of embedded C/C++ programming.
Just for comparison, here's the Ada way to do it:
pragma Restrictions(No_Implicit_Heap_Allocation)
Sign in to Reply
jtdavies
3/11/2010 11:39 AM EST
I worked on an embedded project that didn't allow heap operations after startup. The thought was that if the heap was too small the device just wouldn't run at power up (and log an error).
I'd be interested in a solution that checks for heap allocations after a certain point in the code has passed. It shouldn't be too hard to modify this code to first allow then disallow new.
Sign in to Reply
KarlS
3/13/2010 5:07 PM EST
One thing comes to mind and it goes back to class instantiation. OOP creates objects at anytime, but if objects were only created at startup the encapsulation by the class would be preserved and the app would still be constructed of reusable classes. Dynamic memory allocation and garbage collection would not be needed. because the objects are the app. How should buffer allocation and array access be done? How about a replacement for malloc? Maybe a class that would manage buffers and also manage indexers in a safe way to solve the pointer arithmetic issues? This begs the question, does the C standard allow custom library functions. or is it a don't care. This is starting to sound like building the app from static classes at startup without going all the way to C++. I would like to see more discussion.
Sign in to Reply
Dan at ECS
4/3/2010 1:41 PM EDT
jtdavies - I've done this before... what I did was supply my own malloc(), which during init just called into the library malloc()... once init was done (the application sets a flag, releases a semaphore, whatever...), my own malloc would no longer call into the library, it would call an error routine.
Granted, there is a small amount of coupling between the application & the homebrew malloc (via the flag) but the main motivation was to ensure that new engineers coming onto the project wouldn't be able to sneak in a call to malloc().
Sign in to Reply
KarlS
4/3/2010 5:13 PM EDT
Hi Dan. For C. malloc() is the only call to be concerned about, but have you thought about C++ where class instantiation creates similar problems? It seems that all ctors could check the "init flag", assuming that all classes are initiated during init. Of course that removes some of the perceived advantages of OOP practices. I wonder if a Memory Manager in the OS should handle all instantiation and if it could also manage the deletes.
Sign in to Reply
rajesh284
12/18/2011 12:30 PM EST
I've done this before... what I did was supply my own malloc(), which during init just called into the library malloc()http://clinuxpro.com/static-and-dynamic-memory-allocation
http://clinuxpro.com/what-are-the-differences-between-malloc-and-calloc
Sign in to Reply