Bug 22762 - missing static variable constructor calls
Summary: missing static variable constructor calls
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.30
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 22989 (view as bug list)
Depends on:
Blocks:
 
Reported: 2018-01-30 17:00 UTC by Hannes Domani
Modified: 2018-07-09 21:05 UTC (History)
5 users (show)

See Also:
Host:
Target: *mingw*
Build:
Last reconfirmed:


Attachments
compiled testcase (48.07 KB, application/octet-stream)
2018-02-01 13:27 UTC, Hannes Domani
Details
_ctors.o (4.16 KB, application/octet-stream)
2018-02-01 16:17 UTC, Hannes Domani
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Hannes Domani 2018-01-30 17:00:43 UTC
When trying a program as simple as that:
> #include <stdio.h>
> 
> class Printer
> {
>   public:
>     Printer()
>     {
>       printf("Printer()\n");
>     }
> };
> 
> static Printer printer;
> 
> int main()
> {
>   return 0;
> }
No output is visible, because the static constructors are no longer called.

My investigation got me to this commit: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=ca6f2be7f6bc638fd4fad48def1fae4ae4d7906e

This added PROVIDE() to both __CTOR_LIST__ and __DTOR_LIST__, and the docu (https://sourceware.org/binutils/docs/ld/PROVIDE.html) states:
> The PROVIDE keyword may be used to define a symbol, such as ‘etext’, only if it is referenced but not defined.

But since it's already defined, it's no longer added by ld:
> $ g++ -ostatic-var.exe static-var.cpp -Wl,-T,static-var-ld.txt -Wl,-y,___CTOR_LIST__
> c:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/7.2.0/../../../../i686-w64-mingw32/lib/../lib/libmingw32.a(lib32_libmingw32_a-gccmain.o): reference to ___CTOR_LIST__
> c:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/7.2.0/libgcc.a(_ctors.o): definition of ___CTOR_LIST__

I didn't find _ctors.c, but I think the definition is from here:
https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libgcc/libgcc2.c;h=f418f3a354de4b74d88ba9697b0ac5b8aa71ae03;hb=HEAD#l2358
Comment 1 Nick Clifton 2018-02-01 11:50:07 UTC
Hi Domani,

  Are you saying that that commit broke static constructors ?

  This would imply that the placement of the __CTOR_LIST__ symbol in
  libgcc(_ctors.o) is incorrect, so that when the start up code runs,
  it looks at the data pointed to by __CTOR_LIST__, finds nothing, and
  so no constructors are run.  Please could you have a look at the
  executable to see if this is the case ?  (Or upload it to this PR
  so that I can have a look at it).

Cheers
  Nick
Comment 2 Hannes Domani 2018-02-01 13:05:52 UTC
>   Are you saying that that commit broke static constructors ?
Yes, exactly.

>   This would imply that the placement of the __CTOR_LIST__ symbol in
>   libgcc(_ctors.o) is incorrect, so that when the start up code runs,
>   it looks at the data pointed to by __CTOR_LIST__, finds nothing, and
>   so no constructors are run.  Please could you have a look at the
>   executable to see if this is the case ?  (Or upload it to this PR
>   so that I can have a look at it).
__CTOR_LIST__ is empty in this case, I checked in the debugger already.

And once I remove those PROVIDE()'s again, everything works again.

PS: Originally I had opened this gdb bug: https://sourceware.org/bugzilla/show_bug.cgi?id=22757
Someone mentioned there that I'm not the only one with this problem with the new binutils version.
Comment 3 Nick Clifton 2018-02-01 13:10:57 UTC
Hi Domani,

> And once I remove those PROVIDE()'s again, everything works again.
> 
> PS: Originally I had opened this gdb bug:
> https://sourceware.org/bugzilla/show_bug.cgi?id=22757
> Someone mentioned there that I'm not the only one with this problem with the
> new binutils version.

The thing that concerns me is that this might not be a binutils bug.  It
might be a bug in gcc's libgcc.c file that is used to create ctors.o.
That is why I wanted to have a look at the failing executable, so that I 
can see if the __CTOR_LIST__ symbol really is in the wrong place.

So - please could you upload the compiled test case ?

Cheers
  Nick
Comment 4 Hannes Domani 2018-02-01 13:27:57 UTC
Created attachment 10766 [details]
compiled testcase

used options:
> g++ -g -ostatic-var.exe static-var.cpp
Comment 5 Nick Clifton 2018-02-01 16:14:03 UTC
Hi Domani,

  Thanks.  So the __CTOR_LIST__ symbol is indeed wrong - it is pointing to somewhere in the middle of the .bss section.  It looks to me like the culprit is the _ctors.o file.  Would you mind uploading that too, so that I can take a look at it ?

Cheers
  Nick
Comment 6 Hannes Domani 2018-02-01 16:17:29 UTC
Created attachment 10769 [details]
_ctors.o
Comment 7 Nick Clifton 2018-02-01 16:33:43 UTC
Hi Domani,

  Thanks for the _ctor.o file.  I think that I understand the problem
  now.  The _ctor.o file defines the __CTOR_LIST__ symbol as a common
  symbol (ie uninitialized).  These symbols can be overriden by a 
  definition that does define a value, which is why the linker scripts
  used to work in the past.  But the addition of the PROVIDE directive
  changed that, because PROVIDE will not override a common symbol.

  Hmm, I need to think about how to fix this.

  In the meantime please could you see if adding:

     -Wl,--defsym,__CTOR_LIST__=.ctors

  to your command line will act a workaround for the problem ?

Cheers
  Nick
Comment 8 Hannes Domani 2018-02-01 16:50:51 UTC
(In reply to Nick Clifton from comment #7)
>   In the meantime please could you see if adding:
> 
>      -Wl,--defsym,__CTOR_LIST__=.ctors
> 
>   to your command line will act a workaround for the problem ?
Then I get this result:
> $ g++ -ostatic-var.exe static-var.cpp -Wl,--defsym,__CTOR_LIST__=.ctors
> c:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/7.3.0/../../../../i686-w64-mingw32/lib/../lib/libmingw32.a(lib32_libmingw32_a-gccmain.o): reference to ___CTOR_LIST__
> c:/msys64/mingw32/bin/../lib/gcc/i686-w64-mingw32/7.3.0/libgcc.a(_ctors.o): definition of ___CTOR_LIST__
> --defsym:1: undefined symbol `.ctors' referenced in expression
> collect2.exe: error: ld returned 1 exit status

But I've just removed the PROVIDE()'s to make it work again, that's good enough as a workaround for me.
Comment 9 Nick Clifton 2018-02-03 13:00:08 UTC
Hi Hannes,

> But I've just removed the PROVIDE()'s to make it work again, that's good
> enough as a workaround for me.

Well I have been wracking my brains for a couple of days now, and I cannot think of a better solution either.

I will revert the addition of the PROVIDE()s, but leave the rest of the patch in place, and add a comment about why PROVIDE cannot be used.

If someone does want to provide their own __CTOR_LIST__ and __DTOR_LIST__ symbols, and not just common ones, then they will have to use a custom linker script to achieve this.  Not ideal, but I do not see a better way.

Cheers
  Nick
Comment 10 Sourceware Commits 2018-02-03 13:12:59 UTC
The master branch has been updated by Nick Clifton <nickc@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b0daac83d78c82f7998287bb717063c4d01d4f97

commit b0daac83d78c82f7998287bb717063c4d01d4f97
Author: Nick Clifton <nickc@redhat.com>
Date:   Sat Feb 3 13:11:35 2018 +0000

    Remove PROVIDE() qualifiers from definition of __CTOR_LIST__ and __DTOR_LIST__ symbols in PE linker scripts.
    
    	PR 22762
    	* scripttempl/pe.sc: Remove PROVIDE()s from __CTOR_LIST__ and
    	__DTOR_LIST__ symbols.  Add a comment explaining why this is
    	necessary.
    	* scripttemp/pep.sc: Likewise.
    	* ld.texinfo (PROVIDE): Add a note about the effect of common
    	symbols.
Comment 11 Nick Clifton 2018-02-03 13:13:32 UTC
PROVIDE()s reverted.
Comment 12 Hannes Domani 2018-02-03 13:34:41 UTC
(In reply to Nick Clifton from comment #9)
> Well I have been wracking my brains for a couple of days now, and I cannot
> think of a better solution either.
You first suggested this:
> This would imply that the placement of the __CTOR_LIST__ symbol in
> libgcc(_ctors.o) is incorrect,
So is the definition in libgcc2.c even necessary, isn't it always provided by the linker?

On the other hand, I don't know why anyone would want to override __CTOR_LIST__ anyways.
Comment 13 Sourceware Commits 2018-02-05 09:14:15 UTC
The binutils-2_30-branch branch has been updated by Nick Clifton <nickc@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a985e9b9deabd81e16754584f4397a638e9d3f36

commit a985e9b9deabd81e16754584f4397a638e9d3f36
Author: Nick Clifton <nickc@redhat.com>
Date:   Mon Feb 5 09:12:42 2018 +0000

    Import patch from mainline to remove PROVODE qualifiers around definitions of __CTOR_LIST__ and __DTOR_LIST__ in PE linker scripts.
    
    	PR 22762
    	* scripttempl/pe.sc: Remove PROVIDE()s from __CTOR_LIST__ and
    	__DTOR_LIST__ symbols.  Add a comment explaining why this is
    	necessary.
    	* scripttemp/pep.sc: Likewise.
    	* ld.texinfo (PROVIDE): Add a note about the effect of common
    	symbols.
Comment 14 Nicolas Vigier 2018-03-21 16:50:24 UTC
*** Bug 22989 has been marked as a duplicate of this bug. ***
Comment 15 Martin Storsjö 2018-07-09 21:05:06 UTC
(In reply to Hannes Domani from comment #12)
> (In reply to Nick Clifton from comment #9)
> > Well I have been wracking my brains for a couple of days now, and I cannot
> > think of a better solution either.
> You first suggested this:
> > This would imply that the placement of the __CTOR_LIST__ symbol in
> > libgcc(_ctors.o) is incorrect,
> So is the definition in libgcc2.c even necessary, isn't it always provided
> by the linker?

It's not normally needed, no. As far as I understand it, I think libgcc just provides the symbol in case nothing else provides it (maybe for other targets than mingw).

> On the other hand, I don't know why anyone would want to override
> __CTOR_LIST__ anyways.

This issue originated from trying to use mingw-w64 with lld, llvm's linker (which basically works like msvc's link.exe with a few additions), and doesn't provide __CTOR_LIST__ and doesn't use linker scripts at all.

When intending to use that linker, we provide __CTOR_LIST__ within mingw-w64's crt startup object files. If binutils ld would be able to handle this case (which the original patch achieves), it would (at a later stage when one could start requiring binutils >= 2.30) reduce the amount of conditionals and the fact that we need to know what linker we're going to use, when building mingw-w64.

If we'd enable this codepath for all cases, we'd fix this issue for binutils 2.30 (but at the same time break all earlier versions).

Other potential ways of handling it would be to extend libgcc's fallback __CTOR_LIST__ to something like this:

__attribute__ (( __section__ (".ctors"), __used__ , aligned(sizeof(void *)))) const void * __CTOR_LIST__ = (void *) -1;  
__attribute__ (( __section__ (".dtors"), __used__ , aligned(sizeof(void *)))) const void * __DTOR_LIST__ = (void *) -1;  
__attribute__ (( __section__ (".ctors.99999"), __used__ , aligned(sizeof(void *)))) const void * __CTOR_END__ = (void *) 0;  
__attribute__ (( __section__ (".dtors.99999"), __used__ , aligned(sizeof(void *)))) const void * __DTOR_END__ = (void *) 0;  

However I think that only works if the object file that provides it (in the case of mingw-w64 for lld, in crt2.o) is linked first, before everything else, placing __CTOR_LIST__ at the head of the .ctors section. And I don't think that's fit for libgcc since I guess it's too target specific, and would still cause breakage for any current libgcc version that doesn't have it.

So given that, I guess reverting the patch is the most sensible thing to do, and we'll have to start over with other approaches, if we want to unify matters.