How to remove unused C/C++ symbols with GCC and ld

2018-12-31 15:44发布

I need to optimize the size of my executable severely (ARM development) and I noticed that in my current build scheme (gcc + ld) unused symbols are not getting stripped.

The usage of the arm-strip --strip-unneeded for the resulting executables / libraries doesn't change the output size of the executable (I have no idea why, maybe it simply can't).

What would be the way (if it exists) to modify my building pipeline, so that the unused symbols are stripped from the resulting file?


I wouldn't even think of this, but my current embedded environment isn't very "powerful" and saving even 500K out of 2M results in a very nice loading performance boost.

Update:

Unfortunately the current gcc version I use doesn't have the -dead-strip option and the -ffunction-sections... + --gc-sections for ld doesn't give any significant difference for the resulting output.

I'm shocked that this even became a problem, because I was sure that gcc + ld should automatically strip unused symbols (why do they even have to keep them?).

标签: c++ c gcc ld strip
11条回答
余生无你
2楼-- · 2018-12-31 16:04

You'll want to check your docs for your version of gcc & ld:

However for me (OS X gcc 4.0.1) I find these for ld

-dead_strip

Remove functions and data that are unreachable by the entry point or exported symbols.

-dead_strip_dylibs

Remove dylibs that are unreachable by the entry point or exported symbols. That is, suppresses the generation of load command commands for dylibs which supplied no symbols during the link. This option should not be used when linking against a dylib which is required at runtime for some indirect reason such as the dylib has an important initializer.

And this helpful option

-why_live symbol_name

Logs a chain of references to symbol_name. Only applicable with -dead_strip. It can help debug why something that you think should be dead strip removed is not removed.

There's also a note in the gcc/g++ man that certain kinds of dead code elimination are only performed if optimization is enabled when compiling.

While these options/conditions may not hold for your compiler, I suggest you look for something similar in your docs.

查看更多
琉璃瓶的回忆
3楼-- · 2018-12-31 16:04

It seems to me that the answer provided by Nemo is the correct one. If those instructions do not work, the issue may be related to the version of gcc/ld you're using, as an exercise I compiled an example program using instructions detailed here

#include <stdio.h>
void deadcode() { printf("This is d dead codez\n"); }
int main(void) { printf("This is main\n"); return 0 ; }

Then I compiled the code using progressively more aggressive dead-code removal switches:

gcc -Os test.c -o test.elf
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections -Wl,--strip-all

These compilation and linking parameters produced executables of size 8457, 8164 and 6160 bytes, respectively, the most substantial contribution coming from the 'strip-all' declaration. If you cannot produce similar reductions on your platform,then maybe your version of gcc does not support this functionality. I'm using gcc(4.5.2-8ubuntu4), ld(2.21.0.20110327) on Linux Mint 2.6.38-8-generic x86_64

查看更多
旧时光的记忆
4楼-- · 2018-12-31 16:13

From the GCC 4.2.1 manual, section -fwhole-program:

Assume that the current compilation unit represents whole program being compiled. All public functions and variables with the exception of main and those merged by attribute externally_visible become static functions and in a affect gets more aggressively optimized by interprocedural optimizers. While this option is equivalent to proper use of static keyword for programs consisting of single file, in combination with option --combine this flag can be used to compile most of smaller scale C programs since the functions and variables become local for the whole combined compilation unit, not for the single source file itself.

查看更多
皆成旧梦
5楼-- · 2018-12-31 16:15

Programming habits could help too; e.g. add static to functions that are not accessed outside a specific file; use shorter names for symbols (can help a bit, likely not too much); use const char x[] where possible; ... this paper, though it talks about dynamic shared objects, can contain suggestions that, if followed, can help to make your final binary output size smaller (if your target is ELF).

查看更多
素衣白纱
6楼-- · 2018-12-31 16:16

For GCC, this is accomplished in two stages:

First compile the data but tell the compiler to separate the code into separate sections within the translation unit. This will be done for functions, classes, and external variables by using the following two compiler flags:

-fdata-sections -ffunction-sections

Link the translation units together using the linker optimization flag (this causes the linker to discard unreferenced sections):

-Wl,--gc-sections

So if you had one file called test.cpp that had two functions declared in it, but one of them was unused, you could omit the unused one with the following command to gcc(g++):

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test -Wl,--gc-sections

(Note that -Os is an additional compiler flag that tells GCC to optimize for size)

查看更多
登录 后发表回答