Why doesn't nano provide a slimmer __cxa_pure_virtual?

Asked by George Hawkins

I wrote a tiny demo program that deliberately pulls in __cxa_pure_virtual.

If I replace the "= 0;" of the single pure virtual method in this demo with "{ }" then the size of the .text section (as reported by "size -A") drops really dramatically.

I'm definitely using nano - if I remove "--specs=nano.specs" from my linker arguments then I see a noticeable size increase.

Why doesn't nano provide a slimmer __cxa_pure_virtual by default?

At the moment I have to add the following to every program to avoid this cost:

extern "C" void __cxa_pure_virtual() { abort(); }

I suspect I'm missing something here, is there a flag I should be using or such like to avoid this?

I've put together a small demo on GitHub if you want to experiment with this:

https://github.com/george-hawkins/cxa_pure_virtual

The file there called demo.cpp contains a class Foo that has a pure virtual method if the macro PURE_VIRTUAL is defined.

I've provided a little bash script called BUILD that will build everything and output the size of the .text section.

The following shows cloning the repo and building first with PURE_VIRTUAL unset and then with it set:

$ git clone <email address hidden>:george-hawkins/cxa_pure_virtual.git
$ cd cxa_pure_virtual
$ ./BUILD
668
$ ./BUILD --pure
80792

As you can see the size output changes from 668 bytes to 80792.

Note: demo.cpp just demos this issue, it doesn't make any real sense, some of the code is there just to make sure things don't get optimized away.

The other files are:
* reset.c - this provides the main entry point and most importantly calls __libc_init_array.
* syscalls.c - this provides the functions like _kill that are called by _kill_r etc. in libc.
* RBL_nRF51822.ld - this is the short linker script for the system I'm using (a RBL BLE Nano).

I cut everything down to a minimum but don't know enough about linker scripts - so it's the unchanged original.

If I use the 4.8.3 arm-none-eabi-g++ that comes with the 1.6.4 Arduino IDE the .text size is 652 when no pure virtual is used and 69524 when one is.
If I use 4.9.3 from Terry Guo's PPA then the corresponding sizes are 668 and 80792 respectively.

Thanks,

/George

Question information

Language:
English Edit question
Status:
Answered
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Tony Liu (mrtoniliu) said :
#1

Hi George,

When I added --specs=nano.specs into the commands in your script, the output becomes to

$ ./BUILD
360
$ ./BUILD --pure
47132

So it does provide a relatively smaller __cxa_pure_virtual() compares to the normal one. Maybe you still think it is large comparing with the one has no pure virtual function declared, is that right? Use custom defined __cxa_pure_virtual() should one of the solutions, or do you have any idea to this?

BR,
Tony

Revision history for this message
George Hawkins (ubuntu-8n) said :
#2

Hi Tony --

Thanks for the reply :)

Isn't that typical - I try to look smart in my first post here and of course after commenting things in and out all day I check in a version of my test that has "--specs=nano.specs" removed.

I've pushed an update to GitHub, you can see the change here:

https://github.com/george-hawkins/cxa_pure_virtual/commit/838b6b5

So with newlib nano enabled my .text section is 360 bytes if I use "{ }" instead of "= 0;" for the method I want to be pure virtual.

It grows to 47132 bytes if I make the method pure virtual (i.e. my numbers match yours).

It shrinks to 3096 bytes if I provide this __cxa_pure_virtual implementation:

extern "C" void __cxa_pure_virtual() {
    puts("__cxa_pure_virtual() called - see https://goo.gl/YJXrK3");
    abort();
}

And shrinks further to just 524 if I remove the "puts" line.

46772 seems a massive default penalty for something that will only be useful to you if:

* You've made a coding mistake that would kill your program anyway, i.e. called a pure virtual before the relevant instance is fully constructed.
* You've provided a sensible implementation for _write so that you'll actually see the output from __cxa_pure_virtual.
* You've got a serial console open and connected to your board to display the resulting output.

Wouldn't a lighter weight version like the one shown above be a more reasonable default?

The shortened URL could point to an informative page that explains what the probable issue is and how, if required, a heavy weight __cxa_pure_virtual implementation can be enabled that provides full-fat name demangling etc. (it seems that "d_print_comp" and related functionality are the real underlying memory hogs).

You might argue the 47132 bytes only looks large in relation to my toy example.

But in my actual code base I do all kinds of neat stuff with BLE that all fits into around 15KB and then I suffer a 45KB penalty because one of the libraries I use perfectly reasonably uses some pure virtuals.

I.e. about 75% of the size of my real world program is down to __cxa_pure_virtual.

So shouldn't the default implementation be a little more light weight? That would seem better than everyone having to provide their own separate light weight alternatives (if they get as far as working out that this is the issue behind their large binaries). The current situation looks more desktop-like than embedded.

Regards,

/George

Revision history for this message
Tony Liu (mrtoniliu) said :
#3

Hi George,

Here is a quick update.
I've tried add an implementation of __cxa_pure_virtual() function by:

extern "C" void __cxa_pure_virtual() {
std::fputs("__cxa_pure_virtual() called - see https://goo.gl/YJXrK3", stderr);
std::terminate();
}

and the output:

$ ./BUILD --pure
47132
$ ./BUILD --pure-stubbed
47104

Thus I guess the large size of code is introduced by std::terminate().
I'll keep looking into the terminate() function to see whether it is possible to shrink it or use abort() instead of it.

BR,
Tony

Revision history for this message
Tony Liu (mrtoniliu) said :
#4

Hi George,

There is a configuration option: --disable-libstdcxx-verbose, which should be useful. More detail information can be found here: https://gcc.gnu.org/ml/libstdc++/2012-08/msg00106.html

For your test case, the results will be when I rebuild the libstdc++_nano in the toolchain.

$ ./BUILD --pure
556

You can add this option (--disable-libstdcxx-verbose) into the Task III-5 in the build-toolchain.sh, then try to build entire toolchain again. However, currently we have not got enough time to fully test this option whether it will introduce other problems or not. If you tried build toolchain by yourself and use it, could you please give us feedback if problems exist? Hope this will be helpful.

Best Regards,
Tony

Can you help with this problem?

Provide an answer of your own, or ask George Hawkins for more information if necessary.

To post a message you must log in.