πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

Strip unused runtime functions which bloat executable (GCC)

I have build the GCC4.7.1 cross-toolchain for ARM (cortex-m3). Now I'm linking an executable from C/C++ code that surely doesn't use some certain STL classes (e.g. std::string). Furthermore exceptions and RTTI are turned off.

Though when I'm looking to the target ELF (e.g. using nm), there's a lot of symbols (apparantly from the libstdc++) linked in I wouldn't expect to find there (e.g. std::exception, std::ios_base, etc.).

Why is this there, and how can I get rid of this stuff to reduce the .text section size of my target?

A coworker gave me a tip to override some GCC specific stub function:

namespace __gnu_cxx
{
    void __verbose_terminate_handler()
    {
        for (;;)
            ;
    }
}

This alone reduced the code size about 20KB.
Are there more such stubs I can override?


UPDATE:
OK, I found one really stupid error that removed most of the stuff I was wondering about, when fixing it:
There was an #include <iostream> statement left (though nothing called from there) in one of the source files. This will of course link in the static std::cin, std::cout and std::cerr instances and all the stuff that comes along with these.
Removing the #include <iostream> statement reduced the .text segment about another > 100KB portion.


Nevertheless:
There's still the std::exception and std::basic_string stuff I'm wondering about:

Namespace summaries:
==============================================================================
Type         Size Namespace 
T             774 'std'
W             184 'std::string::_Rep'
W             268 'std'
W             472 'std::string'
Class summaries:
==============================================================================
Type         Size Class 
T              50 'std::error_category'
T              52 'std::type_info'
T              54 'std::bad_exception'
T              54 'std::exception'
T              68 'std::bad_alloc'
T              98 'std::length_error'
T             214 'std::logic_error'
W             268 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >'

There isn't really much code size used, just about several 100 bytes, so I could live with neglecting it, but would appreciate, if I can get rid of this also.

Since I'm explicitly using no exceptions I wonder why these are still instantiated when linking. Usage of exceptions or not can't be really determined at runtime?!?
The only remaining thing from the __gnu_cxx namespace I have left now is

Type         Size Class 
T              58 '__gnu_cxx::recursive_init_error'

It's another exception class.


FINALLY:
I used some additional flags to configure the GCC4.7 cross build:

--enable-gold=yes 
--enable-lto 
--enable-cxx-flags='-fno-exceptions -ffunction-sections -fno-omit-frame-pointer'

The latter flags are used to compile the libstdc++ and are essentially the same as used for building the target code (which is a reasonable action anyway). Exception references where gone afterwards (including the __gnu_cxx::recursive_init_error).

Last thing was, I found an unexpected use of std::string in our codebase. After fixing that, the reference to std::basic_string<char, std::char_traits<char>, std::allocator<char> > also disappeared.

So I'm happy with the result now, no more unnecessary, unexpected overhead from libstdc++, no reason not to use C++ in preference over C.

Upvotes: 7

Views: 4017

Answers (4)

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

Why is this there, and how can I get rid of this stuff to reduce the .text section size of my target?

There might be (static) references for stuff linked in from the libstdc++, that will be instantiated no matter if they are called or referenced in the actually executed codepathes.
Carefully search for usage of #include statements that contain such reference declarations (e.g. #include <iostream>).

When libstdc++ is build during installation of GCC with different C++ compile flags as used for the finally intended target, there might be unwanted stuff linked in or being instantiated (e.g. the __gnu_cxx::recursive_init_error exception class despite usage of the -fno-exceptions flag). Use the --enable-cxx-flags configuration option to synchronize with the intended target compile flags when building the toolchain.

Also look carefully for unwanted/unexpected usage of certain STL classes in your codebase (e.g. std::string), they might compile and being linked without errors or warnings, despite certain features (e.g. new()) aren't really supported on your bare metal platform.

Are there more such stubs I can override?

Depending on how 'newlib' was build for the toolchain (see --en/disable-newlib-supplied-syscalls configuration option), it might be necessary to override certain or all stubs provided there.

Upvotes: 2

greyfade
greyfade

Reputation: 25677

Some CFLAGS:

If you don't use dynamic_cast or type_id in your code, try adding -fno-rtti. This will remove some amount of code relating to class hierarchies, and may remove several dozen bytes for each class in your code.

If you use throw() frequently in your function specifications, try adding -fnothrow-opt. This will encourage GCC to treat throw() as the more strict noexcept specification (from C++11), which will substantially reduce code size in many areas, since it will not have to construct exception frames.

As a companion to -fnothrow-opt, add -Wnoexcept. This will warn you when it finds a place to add throw() or noexcept, and further reduce the number of places exception frames must be constructed.

Also as a companion to above, replace all instances of new in your codebase (if any) with new (nothrow) and add -fcheck-new to your CFLAGS if necessary.

If you're not using threads, you might find a small benefit from -fno-threadsafe-statics, which removes code that performs thread-safe initialization of statics.

Linker

The new (well, for the last few years) binutils linker has a plugin called gold, which performs a number of link-time optimizations, including dead code removal. I don't know off-hand whether gold is supported on ARM yet, but this would be useful in substantially reducing the footprint of the standard library you import.

Upvotes: 2

dbrank0
dbrank0

Reputation: 9476

You can never know what using one of library functions will pull in. Well, actually you probably can by creating call graph with one of the tools. So what part of c++ std library are you using?

Other than that, I have had success in removing unneeded function from executable by using two methods (none of this on ARM, but methods are not ARM specific):

  1. Compile with -ffunction-sections switch, then link with -gc-sections. This puts each function into it's own section and then tells linker to remove unused sections.
  2. Use link time optimization (-flto, see gcc manual for details). This lets compiler see "whole" program as if it was single source and will probably let it remove unused functions.

You are already using -Os, right?

Upvotes: 2

nhed
nhed

Reputation: 5999

Quick fixes

First, depending on limitations of your target - you may want to consider @Carl's recommendation above. We made the decision not to use C++ in our cortex m3 platform.

Second, Embedded development tip for reduction in size for C or C++ - kill floating point libs, in many many applications you can just use fixed point math, but some pull these in for the negotiable reasons (like desire to use "%f" in printf).

The hint your co-worker gave is probably very specific to your platform, probably it has a hardware watchdog? So it will be difficult for others who are not familiar with your hardware, code, or app to make such quick-fix recommendations.

Digging deeper

Did you actually add up the symbols of these libs and that is where you consume most of your .txt section?

The question may not be necessarily what you are not using from that library, but what you are. It is quite possible that the libraries you "demand" have their own dependencies.

It is a painful process (and maybe there are better ways) but if I were you I would try to remove the libraries you link and see what you really pull in (the STL headers may demand several of these symbols). If there are no surprises there, and your app only pulls in expected libraries you would need to dig in deeper:

Method 1: - Analyze the libstdc++ code (probably too long to jump into right away)

Methods 2: - I have never done this with C++, just with C libs, but the theory should hold - while removing linkage of libstdc++ - add in one object at a time from libstdc++, you can either build from code and use the object files explicitly or you can try and use tools such as ar (ar -t to list objects and ar -xv to extract). Note that I am not recommending that you cobble up your link command, just if you want to divide and conquer to see why these modules are demanded by the modules you actually use - at the end of the day you may be able to rebuild the library with a set of "without" flags. Reading the man pages or ar, nm, objdump would probably help in maybe automating some of this if it is taking too long, I have done stuff like that years ago so I don't have my cheat sheets handy.

Upvotes: 1

Related Questions