dannyge

I have a VC++ project B that is linked into a static library "B.lib". This library is linked into project A which creates A.exe. The constructors for the global varaiables in B do not get called.

1. If I have golbal variables in A then there is no problem - the constructors for those get called.

2. I f the solution (Project A and B) get linked on my coleagues computer then there is no problem.
3. The whole soltion is shared between us via rational clearcase so we have the same sources and the same project files.

- so it must be one of the flags of visual studio - but which
Thanks,
D.


Re: Visual C++ Language Constructors for global variables are not called???

SvenC

Hi,

as far as I remember global variables of a module are only guaranteed to be created before the first member of that module is accessed. So you could try to add an InitB function to the cpp where your global variables are defined. Call that function from your A project before using anything else from B.

If that doesn't help: how do you declare and define your global variables

--
SvenC





Re: Visual C++ Language Constructors for global variables are not called???

einaros

 dannyge wrote:
I have a VC++ project B that is linked into a static library "B.lib". This library is linked into project A which creates A.exe. The constructors for the global varaiables in B do not get called.

1. If I have golbal variables in A then there is no problem - the constructors for those get called.

2. I f the solution (Project A and B) get linked on my coleagues computer then there is no problem.
3. The whole soltion is shared between us via rational clearcase so we have the same sources and the same project files.

- so it must be one of the flags of visual studio - but which
 

Without seeing your exact project setup, it's hard to tell what's going on here. I've got a sneaking suspicion that what you're experiencing is a bug I reported a few weeks ago:

https://connect.microsoft.com/feedback/viewfeedback.aspx FeedbackID=244410&wa=wsignin1.0&siteid=210

The short sum-up is that global symbols in libraries, whether of static storage (local to a translation unit) or not, will be discarded even with the /OPT:NOREF compiler/linker flag specified. The workaround, however impractical, is to use the /SYMBOL flag to force the inclusion of specific variables. Note that you'll have to include the full decorated names to use this flag (you can use dumpbin to find these names). The use of /SYMBOL will not work if the variable has static storage, so that remains a problem.






Re: Visual C++ Language Constructors for global variables are not called???

Holger Grund

AFAICT, that's not a bug but it is the way all significant linkers work and have ever worked. Object files are selected from an archive, if and only if they are required to satisfy a strong symbol reference (via the archive symbol table). The unreferenced COMDAT elimination phase runs logically after that (even though a smarter linker will probably construct the dependency graph earlier).

While, an implementation may defer construction VC does not. Rather, the compiler injects pointers to generated initialization functions for objects with nontrivial constructors or destructors or those requiring dynamic initialization. These pointers are emitted into a special section -- typically, .crt$xcu. If the containing object file is selected the input section from that object file will contribute to the corresponding output section (It's not a COMDAT section). The CRT initialization invokes the initializers before entering the user supplied main function.

The /VERBOSE linker switch will reveal why a particular object file is selected from a library or not.

Of course, you should really be certain that the initializers are not called. For instance, the debugger can't materialize a breakpoint on a given code line if the containing translation unit was not built with debug information enabled.

-hg





Re: Visual C++ Language Constructors for global variables are not called???

einaros

Holger Grund wrote:

AFAICT, that's not a bug but it is the way all significant linkers work and have ever worked. Object files are selected from an archive, if and only if they are required to satisfy a strong symbol reference (via the archive symbol table). The unreferenced COMDAT elimination phase runs logically after that (even though a smarter linker will probably construct the dependency graph earlier).

The C++ standard, section 3.7.1, specifies:

"If an object of static storage duration has initialization or a destructor with side effects, it shall not be eliminated
even if it appears to be unused, [...]"

And IMO it should most certainly not be eliminated in presence of the /OPT:NOREF flag. The fact that /INCLUDE successfully overrides the removal strengthens my belief.






Re: Visual C++ Language Constructors for global variables are not called???

Holger Grund

I'm afraid I happen to disagree. Your first argument (BTW: how do you do these quotes, I'm apparently too dumb to use the web interface) only holds if the corresponding translation unit is part of the program. In my definition and the one of at least four major toolchain implementators, it is not.

Again unreferenced COMDAT elimination operates on sections -- specifically on sections from object files (often called input sections in the ELF world and sometimes called section contributions in the MS/COFF world), many input sections of the same name contribute to a single output section. Standard (non-COMDAT) input sections are merged when the corresponding object file is selected. COMDAT sections allow for duplicating input sections in several contributing object files where only one is selected. The obvious uses are things with external linkage that do not violate the ODR (e.g. inline functions, vtables of polymorphic classes without a decider function). The linker determines whether a particular COMDAT section is required based on symbol references (these are almost always relocations, even though COMDAT section grouping features exist in similar ways in both MS/COFF & ELF).

The way object file selection from archives works is a bit different. Basically, archive have a global archive table which maps symbols with global/weak binding to a particular object file member. The linker initially starts with all object files given on the command line and then recursively selects object files from the static libraries until all symbol references are satifisied (or an error occurs). That's why you can override some library functions in your own code (e.g. it is how some of operator new/delete form overloads replacement works). Once an object file is selected, all of its symbols & references are added to the current state and the process continues.

Obviously, the latter was designed for C/assembler only. Also inlining and cross-module optimizations were certainly not nearly at a state where they are today. You rarely had code in a source file that was potentially not required.

COMDATs are a cheap solution for the special needs of C++, but obviously one would design a linkage system for C++ quite differently, if you didn't have the historic heritage.

I don't quite understand your /INCLUDE argument. /INCLUDE works on symbols. If the symbol is referenced, the object file is selected. That causes all non-COMDAT sections to be included. I don't see how that supports your case.

Again replacing operator new/delete wouldn't work as it works today with your suggested process.

-hg





Re: Visual C++ Language Constructors for global variables are not called???

Holger Grund

Oh BTW, others have had problems with this selection process (it's particularly painful in ELF where there are no directive sections and there is no symbol-table-level support for symbol aliases). The typical solutions is to use a proxy/sentinel/trigger symbol which effectively causes the containing object file to be selected. You can use comment pragmas to emit /INCLUDE's in all object files for translation units, which include these pragmas.

So you can add something like

#pragma comment ( linker, "/INCLUDE:xyz" )

to the header file used by clients of your static library.

-hg





Re: Visual C++ Language Constructors for global variables are not called???

einaros

First of all, this isn't as much an argument, as it is my interpretation on various observations. I am by no means proclaiming to be an expert on this matter.

 Holger Grund wrote:

I'm afraid I happen to disagree. Your first argument (BTW: how do you do these quotes, I'm apparently too dumb to use the web interface) only holds if the corresponding translation unit is part of the program. In my definition and the one of at least four major toolchain implementators, it is not.

That's probably a misconception on my behalf, then. I was of the belief that this should be upheld even when a translation unit is linked as part of a static library.

 Holger Grund wrote:

I don't quite understand your /INCLUDE argument. /INCLUDE works on symbols. If the symbol is referenced, the object file is selected. That causes all non-COMDAT sections to be included. I don't see how that supports your case.

I've interpreted the documents in ways such that unreferenced data would be linked with /OPT:NOREF flag specified, and that their failure to do so were the result of a bug since /INCLUDE successfully overrode the discarding. Obviously it was my conception of COMDATs that was flawed; specifically that a variable explicitly tagged for the COMDAT section would be included with the mentioned /OPT:NOREF present.

As somewhat of a digression (though not really): what makes an object of internal storage be kept, granted that it's type is referenced in the linking application






Re: Visual C++ Language Constructors for global variables are not called???

Holger Grund

einaros wrote:

I've interpreted the documents in ways such that unreferenced data would be linked with /OPT:NOREF flag specified, and that their failure to do so were the result of a bug since /INCLUDE successfully overrode the discarding. Obviously it was my conception of COMDATs that was flawed; specifically that a variable explicitly tagged for the COMDAT section would be included with the mentioned /OPT:NOREF present.

As somewhat of a digression (though not really): what makes an object of internal storage be kept, granted that it's type is referenced in the linking application

The linker pretty much operates on symbol and sections only. It's the compiler's job to map the requirements of C++ standard to the linker concepts.

A COMDAT section can be discarded if it is not referenced -- a standard input section is always included. None of the things in the original example are emitted in COMDAT sections. So, if an object file is selected all of its standard input sections will be included in the final image. Unfortunately, almost everything goes into standard sections, specifically:

  • symbols with internal or no linkage that can be referenced from other sections. Unfortunately, that includes things like string literals, static functions and variables. This is probably due to the limitations of COMDAT sections. Object format specifications require that symbols with local bindings not to be referenced from outside a COMDAT group.
  • Global variables with external linkage that are not declared with __declspec(selectany)
  • Global functions with external linkage unless you build with /Gy
  • Vtables for classes with a decider function (not sure however, how the VC ABI implements vtable emitting)
  • Non-templated static data member definitions
  • A gazillion of other things I can't think of now

Some of the things could certainly be emitted in COMDAT sections and that would probably yield measurable image size savings.I asked some time back for an equivalent of /Gy for data items, but the MS folks weren't exactly enthusiastic about it. Doing the same things for elements with internal/no linkage, shouldn't be that hard either as it is probably already required for some PGOs such as indirect/vcall speculation.

-hg





Re: Visual C++ Language Constructors for global variables are not called???

dannyge

Hi Thanks for the input. Yes NOREF does not work. Heres what I am trying to do in a nutshell:

I have an abstract class and other people develop derived classes to it. My main application is oblivious to the derived classes - it uses the abstract interface. The way I connect the concrete derived classes to the application is via declaring a global object which registers the new class in some registery (rest assured I know how to overcome the unknown initialization order). I am looking for a workaround that will enable me to:

1. Not add new code to the main application.

2. Add the new class in somehow.

One possible way is to explicitly link the derived classes as objects and not as part of a library. Any othe suggestions





Re: Visual C++ Language Constructors for global variables are not called???

einaros

dannyge wrote:

1. Not add new code to the main application.

The only way would be to use the /INCLUDE linker flag, either through a pragma as Holger proposes, or in your project settings.






Re: Visual C++ Language Constructors for global variables are not called???

einaros

Holger Grund wrote:

[...] So, if an object file is selected all of its standard input sections will be included in the final image. Unfortunately, almost everything goes into standard sections, specifically:

  • symbols with internal or no linkage that can be referenced from other sections. Unfortunately, that includes things like string literals, static functions and variables. This is probably due to the limitations of COMDAT sections. Object format specifications require that symbols with local bindings not to be referenced from outside a COMDAT group.
  • Global variables with external linkage that are not declared with __declspec(selectany)
  • Global functions with external linkage unless you build with /Gy
  • Vtables for classes with a decider function (not sure however, how the VC ABI implements vtable emitting)
  • Non-templated static data member definitions
  • A gazillion of other things I can't think of now

Thanks for clearing that up While I cannot say that I'm entirely pleased with the behavior, I do see the implications of doing it otherwise.

In retrospect I do see that I missed a few subtle paragraphs in the MSDN docs for both selectany and OPT:NOREF. They could easily have been written less ambiguous, but that's nothing new.






Re: Visual C++ Language Constructors for global variables are not called???

dannyge

On unix (I recently migrated to windows) you dont usually create libraries in a team project. Rather you own a local makefile which lets you control your build process and yiu usually leave yor code compiled as a bunch of objects. Any way to imitate this convniently in VC++:

1. give each team member a personal "make" or equivalent file so that he can add files to it easily in his sandbox (and not have to check it out/in all the time).

2. Not link as a library to the main project but rather as a bunch of objects.

I couldnt find anything in the project file settings that lets you do that.

Thanks,

Danny