I've been playing around with a tool to answer the more generic question: why is my binary (written in C, C++, Rust, etc) so large?
We use CPU profilers to tell us why our programs are slow. My tool is intended to be a size profiler: why is my program big?
It turns out there are some really interesting analyses and visualizations you can perform to answer this question.
A few direct comments on the article:
- I do not recommend stripping your binaries completely! Use "strip --strip-debug" instead (or just don't compile with -g). You should realize that debug information and the symbol table are two separate things. The debug information is much larger (4x or more) and much less essential. If you strip debug information but keep the symbol table, you can still get backtraces.
- I don't believe -Wl,--gc-sections has any effect unless you also compile with -ffunction-sections and/or -fdata-sections, which these examples for C/C++ do not.
If you care about binary size in C or C++ you should probably be compiling with -ffunction-sections/-fdata-sections/-Wl,--gc-sections these days. Sadly, these are not the default.
- It is fine to strip both debug information and symbol table if you want the distributable (I mentioned the ISP at the beginning for one reason) and the program does not rely on them in any way. Actually, I wonder why separate debug files [1] are not a norm in Unixes.
- `-Wl,--gc-sections` does have an effect even at the absence of `-ffunction-sections` (and so on) because libc and libstdc++ is already compiled in the way allowing for GC---perhaps necessarily. The example had a single function anyway and I was lazy enough to exploit that `-ffunction-sections` wouldn't have a difference...
Without symbols you can't get backtraces, profile the program, use function-based DTrace probes, readably disassemble it, etc. I'm not saying it's impossible to distribute stripped binaries, I'm just saying I don't recommend it. Compared with the debug information, I think the symbol table is much bigger bang for the buck, considering how much smaller it is.
It's strange -- I can verify with -Wl,--print-gc-sections that this is indeed discarding some sections from a static glibc link, so I was wrong about that. On the other hand I can also see plenty of sections in libc.a that have more than one function in them -- not sure why this would happen if libc was indeed compiled with -ffunction-sections.
I agree that separate debug files are nice, and OS X's implementation of them is especially nice, since it does a very good job of finding the right debug information for an executable.
Yeah, I don't doubt the usefulness of the symbol table. I'm probably thinking of the distributable in Windows, where you... don't really do such things.
IIRC the coding convention of glibc is that most functions (and probably all public functions) are contained in their own files. So it effectively results in the similar effect, even when `-ffunction-sections` is missing.
Actually stripping debug information (or compiling without -g) makes it very difficult to troubleshoot a problem in production, where the binary might be deployed at a customer's site, and where the source code might not be available (firewall, no network connection, ...) The additional information is encoded into the ELF header, but ignored by the runtime linker, so it does not hurt the performance of the program.
The space savings come nowhere close to the benefit of having the additional information available when debugging.
Some operating systems, for example SmartOS, and any other OS based off of the illumos's source code, even inject the entire source code into the ELF binary and library in a special compact format during the build with the ctfconvert(1ONBLD) / ctfmerge(1ONBLD) tools[1][2].
If you are ever considering stripping the binary just to save some disk space but do not have a good reason for it (like building for a space-constrained appliance), please abstain from doing so; every developer and engineer trying to debug your program will be thankful to you if you do not remove the debugging information.
Are you sure those flags has any effect? In the C++ project I'm trying it on --ffunction-sections/-fdata-sections/--gc-sections causes the object files to grow a bit. But the resulting binary is to the byte exactly as big as it was without the flags.
We use CPU profilers to tell us why our programs are slow. My tool is intended to be a size profiler: why is my program big?
It turns out there are some really interesting analyses and visualizations you can perform to answer this question.
A few direct comments on the article:
- I do not recommend stripping your binaries completely! Use "strip --strip-debug" instead (or just don't compile with -g). You should realize that debug information and the symbol table are two separate things. The debug information is much larger (4x or more) and much less essential. If you strip debug information but keep the symbol table, you can still get backtraces.
- I don't believe -Wl,--gc-sections has any effect unless you also compile with -ffunction-sections and/or -fdata-sections, which these examples for C/C++ do not.
If you care about binary size in C or C++ you should probably be compiling with -ffunction-sections/-fdata-sections/-Wl,--gc-sections these days. Sadly, these are not the default.