Reputation: 1
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
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
Reputation: 25677
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.
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
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):
You are already using -Os, right?
Upvotes: 2
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