Smaller binary size with C++ on baremetal (g++)

I’ve recently begun moving a large portion of the isostick code from C to C++, since much of it would benefit from object orientation.

In the case of isostick I have lots of strings, vectors, and file handles being tossed around. Making them objects reduces the amount code and simplifies memory management, which can be moved to constructors and destructors for example.

In my opinion, if you find yourself writing a lot of boilerplate just to do what C++ would do implicitly then perhaps you should consider C++, but it’s important to understand what exactly C++ is doing behind the scenes, as with any language or tool.

Anyway, this is just going to be a brief overview of some things I’ve done in order to reduce code size when using C++ on baremetal.

Disclaimer: I’m by no means an expert on the topic. These tips may only apply to g++, I do not use any other compilers so I can’t be sure. Please feel free to point out errors by dropping a note in the comments section.

Don’t use exceptions (-fno-exceptions)

Exceptions add a lot of bulk to your code. If you don’t use them, get rid of them with the -fno-exceptions compiler flag.

Eliminate Run-Time Type Information (-fno-rtti)

Run-Time Type Information is handy, it lets you introspect your code at runtime and do safer casts. Before disabling it, do be aware that RTTI is what makes dynamic_cast<>() and typeid() work! Anecdotally, the size added by RTTI is minimal, so in general I wouldn’t worry too much about this one.

Overload the new and delete operators

The default new and delete operators throw std::bad_alloc when there’s no memory left on the heap. That means, even if you use -fno-exceptions, you may still see your binary size balloon when call new!

Luckily, overriding new and delete is exceedingly easy, and you probably wanted to anyway so you can catch the errors with your own debug/trace code. Here’s some sample code for it.

Provide a __cxa_pure_virtual() implementation

If you use pure virtual functions anywhere but have disabled exceptions, you may notice your code suddenly inflate again.

This happened to me, and it took a while to track down, whoops!
Inspecting assembly listing of the final binary (from `objdump -h -C -S'), it looked like exceptions were coming back!

One thing I tried was linking with -nostdlib, completely pulling libstdc++ out of the picture. I provided dummy implementations of malloc, realloc, free, and a few other stdlib functions I used, but then avr32-g++ complained about something I hadn’t seen before: I was missing __cxa_pure_virtual().

Aha,” I thought, “this has to be it!” In the source of that particular function, found in libstdc++, is a call to std::terminate(), seen here. That call threw a lovely party all over my poor AVR32’s flash memory, trampling on -fno-exceptions on their way in.

Anyway, __cxa_pure_virtual() is what actually gets called when you call a pure virtual function. Like new and delete, this is probably something you want to override anyway so your own debug/trace code can give you useful feedback. The implementation is straightforward, just be sure to make it extern "C" so the name doesn’t get mangled:
extern "C" void __cxa_pure_virtual() { while(1); }

If you do want exceptions…

…you may want to override the verbose terminate handler, details in tip #4 here. As explained on that page, the default __gnu_cxx::__verbose_terminate_handler() (which handles uncaught exceptions) prints the exception name. The routines to demangle the exception name, however, are apparently quite beefy.

You may also be able to just use std::set_terminate() to set your own handler and get the same reduction in code size. I haven’t tried either of these personally, so if anyone knows for sure please leave me a note in the comments!

7 thoughts on “Smaller binary size with C++ on baremetal (g++)

    • That is a similar project to isostick, yes.
      All the isostick code will be open sourced later this year under MIT license. That includes the firmware update utilities, making it easy to apply homebrew firmware. 🙂

  1. Thanks for the answer.

    One more question. Obviously it is too late to “back” the project on Kickstarter. When will it become available to us mere mortals – i.e. those who found out too late about the project?

  2. Pingback: Smaller binary size with C++ on baremetal (g++) – Part 2: Templates | Elegant Invention

Leave a Reply

Your email address will not be published. Required fields are marked *