Bug 3315

Summary: ld --gc-sections *.o produces big(ger than possible) executables
Product: binutils Reporter: Denis Vlasenko <vda.linux>
Component: binutilsAssignee: unassigned
Status: RESOLVED INVALID    
Severity: normal CC: bug-binutils
Priority: P2    
Version: 2.17   
Target Milestone: ---   
Host: i386-pc-linux-gnu Target: i386-pc-linux-gnu
Build: i386-pc-linux-gnu Last reconfirmed:
Attachments: objdump of ld *.o result
objdump of ld main.o lib.a result

Description Denis Vlasenko 2006-10-06 15:47:14 UTC
Basically, I compile an executable from same set of source files.
First I compile .c modules into .o with -ffunction-sections
-fdata-sections, and then I can do something a-la
"gcc -o executable -Wl,--gc-sections *.o"
or I can bundle almost all .o files into lib.a using ar and do
"gcc -o executable -Wl,--gc-sections main.o lib.a"

The second method gives much better results:

# size */busybox
   text    data     bss     dec     hex filename
  14186     868      24   15078    3ae6 busybox.1.t/busybox
   2682     304      24    3010     bc2 busybox.2.t/busybox

However, there is no reason to believe that all is good now.
I tent to fear than suboptimal link-time dead code elimination
is still happening, only on lesser scale (within each individual .o file
pulled from lib.a, not over whole set of .o files).

Extended explanation is at http://busybox.net/~vda/Kbuild_new_2006/

I will attach objdump -xsdr of both executables.
comparing those, I see:
* extra EH_FRAME section
* sections .hash, .dynstr, .dynsym,.... are much longer

Unstripped executables also available for comparison at the above URL.

One thing has been tracked down to this test case:

/* Compile:
** gcc -ffunction-sections -fdata-sections -c -o test.o test.c
** gcc -o test -Wl,--gc-sections test.o
**
** First call to notexist() is optimized away by linker because
** function eliminated() is, er, eliminated by section GC.
**
** But second call to notexist() is not optimized away,
** despite the fact that getpwnam_r() is not called from anywhere.
** Then linking fails because notexist() isn't exist.
**
** Renaming getpwnam_r to any other name "fixes" this! */

void notexist(void);
int main(void) { return 0; }
void eliminated(void) { notexist(); }
void getpwnam_r(void) { notexist(); } // HERE

Rest are not researched in depth.
Comment 1 Denis Vlasenko 2006-10-06 15:48:48 UTC
Created attachment 1354 [details]
objdump of ld *.o result
Comment 2 Denis Vlasenko 2006-10-06 15:49:31 UTC
Created attachment 1355 [details]
objdump of ld main.o lib.a result
Comment 3 Alan Modra 2006-10-10 08:55:59 UTC
getpwnam_r is kept because you are linking against a shared libc that also
defines this symbol.  In this case the linker keeps your getpwnam_r, because it
is assumed you are defining a function to override the one in the shared lib. 
The linker can't know whether getpwnam_r is actually called, because there might
be a call from a dlopen'd library at runtime.