Bug 20152

Summary: constructors for c++ objects are sometimes discarded when linked via a .a archive
Product: binutils Reporter: Dave Malcolm <dmalcolm>
Component: ldAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED INVALID    
Severity: normal CC: amodra, nickc
Priority: P2    
Version: 2.27   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Dave Malcolm 2016-05-26 20:50:37 UTC
I noticed when developing a patch for GCC that C++ objects with global constructors are sometimes discarded if linked via a .a archive.

I've created a minimal reproducer for this here:
  https://github.com/davidmalcolm/test-ctor

I can turn that into an archive and attach it if that's preferably.

In the example, test-ctor.C contains objects with constructors that should be run before main.

If linked as a .o file into the final executable, the constructors are run. If converted to a .a archive first, the constructors are only run if there's another symbol in the file that's consumed by something in another file.

Note how in test-1 and test-2a, four ctors are run, but in test-2b, only two ctors are run; the ctors in test-ctors.C appear to be being discarded:

$ ./test-1
foo:foo(test-ctor.C, static foo in emptyish file)
foo:foo(test-ctor.C, foo in anon ns in emptyish file)
foo:foo(test-ctor-main.C, static foo in main file)
foo:foo(test-ctor-main.C, foo in anon ns in main file)
main()

$ ./test-2a
foo:foo(test-ctor-main.C, static foo in main file)
foo:foo(test-ctor-main.C, foo in anon ns in main file)
foo:foo(test-ctor.C, static foo in emptyish file)
foo:foo(test-ctor.C, foo in anon ns in emptyish file)
main()

$ ./test-2b
foo:foo(test-ctor-main.C, static foo in main file)
foo:foo(test-ctor-main.C, foo in anon ns in main file)
main()

Seen with binutils-2.23.88.0.1-13.fc20.x86_64, reproduced with trunk (f3ad76370f8c79e4ae74ca6826e23bf417d5283a, 2016-05-25).
Comment 1 Nick Clifton 2016-06-02 12:31:22 UTC
Hi Dave,

> I noticed when developing a patch for GCC that C++ objects with global
> constructors are sometimes discarded if linked via a .a archive.

Why is this surprising ?  The point of an archive is that contents are only
extracted from it if they are needed.  If nothing in the main program
references the global object in the library then it is not included in the
link and its constructor is never run.

Cheers
  Nick
Comment 2 Dave Malcolm 2016-06-02 19:11:05 UTC
(In reply to Nick Clifton from comment #1)
> Hi Dave,
> 
> > I noticed when developing a patch for GCC that C++ objects with global
> > constructors are sometimes discarded if linked via a .a archive.
> 
> Why is this surprising ?  The point of an archive is that contents are only
> extracted from it if they are needed.  If nothing in the main program
> references the global object in the library then it is not included in the
> link and its constructor is never run.

The word "object" is rather overloaded in this discussion.

If foo.o contains a C++ object "f" with a global constructor and "f" is not used, then:

  * if foo.o is linked directly into an executable, f's ctor is run before "main".
  * if foo.o is linked into a .a and then that .a is linked into an executable, f's ctor may or may not be run before "main".  The constructor is only run if there's at least one symbol in foo.o that's referenced by the main program.

Note that although the C++ object f isn't used, f's ctor may have interesting side-effects that need to happen; the parameters may reference things that need to be linked (in my case I was using it to register unit testing functions with a test runner).

I don't know if this behavior is a bug, but this is sufficiently surprising to me that I wanted to capture it somewhere with a URL; it seems like a "gotcha" to me  (it took me a couple of days of stepping through the link of gcc to track down).

Hope the above makes sense.
Comment 3 Alan Modra 2016-06-03 03:51:14 UTC
Not a bug.  ld and archives are behaving as designed,