C++ global variable not initialized when linked through static libraries, but OK when compiled with source

被刻印的时光 ゝ 提交于 2020-01-09 12:52:11

问题


I have created a system that automatically registers function objects (functors) into a map based on the constructor of an global instance.

In each cpp file that defines the functor, there's a global instance of the registrar class instance to register the functor to a singleton std::map<int, std::function<...> > object.

This is the definition of registrar class:

template
<
    typename map_type,
    typename handler_type
>
struct registrar
{
    registrar
        (
             map_type& map_object,
             boost::uint16_t cmd_code,
             const handler_type& handler
        )
        {
          map_object.insert(std::pair<boost::uint16_t, handler_type>(cmd_code, handler));
        }
};

in each .cpp file. The global instance is defined like this:

namespace one_way
{
    static registrar <in_out_map_type, handler>
        post_receiver(in_out_map_type::instance(), command, handlers());
}

All works fine if I compile all the cpp with the main.cpp together. But If I compile the cpp file into a static library and link it to main.cpp, the registration does not work.

I tested with VC10 and GCC4.61 both on Windows & and Ubuntu 11.10. Both fail.

I found a thread with the same problem but OP did not say whether he solved it or not.

Am I missing anything?


Edit


Thanks for all responses including the comments.

Every response indeed helped me to think more and investigate deep into this method. After all the study and trials, I finally gave up the idea of relying on global/static variable for self-registration across binary boundaries, because there's no portable way to guarantee it will work.

My final way is to keep the registration within one binary.


回答1:


I believe your object file from the library is not getting linked. Look how Microsoft handles the acrtused symbol (search case insensitive, I do not remember the case and there's no MSVC on this machine).

Once you know how they handle acrtused, do the same thing with your global variable to force it to get linked.

Will update if I find the answer.

Here are a couple of possibilities to force things to link and initialize in a somewhat forced order.

Look here for a GCC answer.

Look here for MSVC10.




回答2:


Short answer for android NDK work, any static libs that are affected by this problem should be added to the LOCAL_WHOLE_STATIC_LIBRARIES variable -- they'll then be referenced using the -Wl,--whole-archive flag and won't be subject to stripping.

Longer answer for MSVC:

Static variables in a translation unit are initialized before any regular code in the translation unit executes. In practice the initialization happens when the containing executable or dynamic library is loaded. When your \c main() is called, or your call to LoadLibrary()/dlopen() completes, any static variables will have been initialized.

The Problem, as described by MSDN:

Constructors and assignment by global function or static methods in the declaration do not create a reference and will not prevent /OPT:REF elimination. Side effects from such code should not be depended on when no other references to the data exist.

It can be convenient to place the object code from multiple translation units in a single file, a static library conventionally named with a \c .lib or \c .a suffix. The MSVC linker does dependency analysis on static libraries and will not include code that is not referenced by the including entity.

The common pattern of using a static variable to declare and cause the registration of a factory object can fail in this circumstance -- the MSVC linker deems the static as being unreachable and strips it from the result.

Solutions

A useful google search: http://www.google.com/search?q=msvc+factory+static+library

One solution is to set the /OPT:NOREF linker flag on the including entity. However, this is an all or nothing setting, and will require that all included libraries be fully linkable.

If something in the file containing the static is referenced (directly or indirectly) by the including entity, then by the language rules the static itself must be preserved.

The most basic approach is to put a dummy function in the file, and reference that from somewhere known to be considered reachable.

Another approach is to use the /INCLUDE linker flag to reference an entity in the problem file. Assuming an entity named DummyForLinkProblem, this can be done in the including entity's source:

#pragma comment(linker, "/include:DummyForLinkProblem")

ZooLib's Solution

ZooLib entities currently affected by this problem are those in ZFile_Win.cpp, ZGRgnRep_HRGN.cpp, ZNet_Internet_WinSock.cpp, ZStreamRWCon_SSL_Win.cpp, ZTextCoder_Win.cpp and ZUnicode_Normalize_Win.cpp.

We #include ZCompat_MSVCStaticLib.h in the corresponding header files, and put in each a ZMACRO_MSVCStaticLib_Reference(ModifiedFileName). In the cpp files we put a ZMACRO_MSVCStaticLib_cpp(ModifiedFileName). The ModifiedFileName is generally the filename with the leading Z and file extension removed, the same style as used in ZCONFIG_API_XXX macros.

To ensure that your executable or library does not strip these entities, simply #include the appropriate header file from known referenced code in your including entity. This will cause a non-executing reference to occur, and things will work as expected.



来源:https://stackoverflow.com/questions/9459980/c-global-variable-not-initialized-when-linked-through-static-libraries-but-ok

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!