Skip to content

Static initializers (C++) are not called with Microsoft Visual Studio Linker #9585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
llvmbot opened this issue Feb 14, 2011 · 7 comments
Closed
Labels
bugzilla Issues migrated from bugzilla

Comments

@llvmbot
Copy link
Member

llvmbot commented Feb 14, 2011

Bugzilla Link 9213
Resolution FIXED
Resolved on May 06, 2012 21:50
Version trunk
OS Windows NT
Blocks llvm/llvm-bugzilla-archive#12477
Attachments A simple C++ initialization example
Reporter LLVM Bugzilla Contributor
CC @asl,@jckarter,@tritao

Extended Description

Static initializers in C++, defined by @​llvm.global_ctors are not called when object files are linked by MSVC linker. I've attached a simple test file. Other tool chains (MinGW gcc) are okay in Windows.

The attached file should print out:
"foo_initializer"
"11"

I'm unclear which component is culprit for this bug. Please move this bug reporting if better component section fits.

[1] MSVC C++ - Okay

cl ctor.cpp (Calling MSVC C++ compiler and linker)
ctor.exe
foo_initializer
11

[2] MinGW-gcc - Okay

gcc ctor.cpp -o ctor.exe (MinGW gcc)
ctor.exe
foo_initializer
11

[3] MinGW-llvm-gcc - Okay

llvm-gcc ctor.cpp -o ctor.exe (MinGW gcc)
Z:\dev\MinGW\bin/ld.exe: Warning: type of symbol `_main' changed from 32 to 512 in R:\temp/ccMNSXGt.o

ctor.exe
foo_initializer
11

[4] Clang - NOT okay (Clang will call MSVC's "link.exe")

clang ctor.cpp -o ctor.exe
ctor.exe
1
===> Static initializer wasn't called.

clang ctor.cpp -c -o ctor.o
gcc -o ctor.exe ctor.o
ctor.exe
foo_initializer
11

===> However, linking with gcc's is okay.

@llvmbot
Copy link
Member Author

llvmbot commented Apr 27, 2011

If anyone could provide some information on where the code generation for all of this happens, I'd be quite happy to look into adding support for msvc...

@asl
Copy link
Collaborator

asl commented Apr 28, 2011

Static ctors are normally run from the hidden routine which is executed before main(). So, you need to figure out how vcpp handles this, what does it call, etc.

@llvmbot
Copy link
Member Author

llvmbot commented Apr 29, 2011

Static ctors are normally run from the hidden routine which is executed before
main(). So, you need to figure out how vcpp handles this, what does it call,
etc.

Yes, I'm already aware of how msvc handles static initializers - I was more interested about where in llvm/clang the static initializer information is put into the object file.

@llvmbot
Copy link
Member Author

llvmbot commented Jun 18, 2011

I have a working fix for this, although it isn't widely tested yet.

To properly get the MS CRT to call static constructors, they need to be put into a specially named section that gets picked up by the MSVC linker.

In TargetLoweringObjectFileCOFF::Initialize(...) when LLVM sets up StaticCtorSection, one could do something like:

Triple T(((LLVMTargetMachine&)TM).getTargetTriple());
bool useMSVCLinkerCompatibleGlobalCtorDtor = (T.getOS() == llvm::Triple::Win32);

..

getContext().getCOFFSection(useMSVCLinkerCompatibleGlobalCtorDtor ? ".CRT$XCU" : ".ctors",

The .CRT$XC* section/group is read by the MSVC linker and function pointers are placed into an array (ordered by the last letter in the group name) that is called during __tmainCRTStartup. (http://msdn.microsoft.com/en-us/library/bb918180.aspx)

I've tested this with a previously-broken project and it fixes it perfectly, along with producing the right looking stuff in DUMPBIN.

The other trick is to also get static destructors working. The static constructor wrappers (__cxx_global_var_init et al) for objects that have dtors contain a call to __cxa_atexit(the_dtor_fn) which needs to be implemented on MSVC targets. Forward the call directly to atexit, thus:

extern "C" void
__cxa_atexit(void (arg1)(), void, void*)
{
atexit(arg1);
}

and the CRT will similarly unwind the construction order on exit.

All that in place, things seem to work, at least in the test case(s) I've got here. Further testing required. I don't claim the above is the correct/perfect answer, but perhaps it will help focus in on a final solution.

@llvmbot
Copy link
Member Author

llvmbot commented Jun 18, 2011

A transparent fix for dtors is to just use the "atexit" symbol instead of "__cxa_atexit" during CodeGenFunction::EmitCXXGlobalDtorRegistration(...) when targeting the MSVC linker.

This then resolves to the MSVC equivalent directly, no code changes required on the user end.

I tried patching the tail end of the above function as below (obviously these kind of specifications all need to live somewhere in the Targets configuration stuff)

llvm::Constant *AtExitFn = CGM.CreateRuntimeFunction(AtExitFnTy,
"atexit");
if (llvm::Function *Fn = dyn_castllvm::Function(AtExitFn))
Fn->setDoesNotThrow();

llvm::Value *Args[3] = { llvm::ConstantExpr::getBitCast(DtorFn, DtorFnTy),
llvm::ConstantExpr::getBitCast(DeclPtr, Int8PtrTy),
llvm::ConstantPointerNull::get(VoidPtrTy) };
Builder.CreateCall(AtExitFn, &Args[0], llvm::array_endof(Args));

I have a working fix for this, although it isn't widely tested yet.

To properly get the MS CRT to call static constructors, they need to be put
into a specially named section that gets picked up by the MSVC linker.

In TargetLoweringObjectFileCOFF::Initialize(...) when LLVM sets up
StaticCtorSection, one could do something like:

Triple T(((LLVMTargetMachine&)TM).getTargetTriple());
bool useMSVCLinkerCompatibleGlobalCtorDtor = (T.getOS() ==
llvm::Triple::Win32);

..

getContext().getCOFFSection(useMSVCLinkerCompatibleGlobalCtorDtor ? ".CRT$XCU"
: ".ctors",

The .CRT$XC* section/group is read by the MSVC linker and function pointers are
placed into an array (ordered by the last letter in the group name) that is
called during __tmainCRTStartup.
(http://msdn.microsoft.com/en-us/library/bb918180.aspx)

I've tested this with a previously-broken project and it fixes it perfectly,
along with producing the right looking stuff in DUMPBIN.

The other trick is to also get static destructors working. The static
constructor wrappers (__cxx_global_var_init et al) for objects that have dtors
contain a call to __cxa_atexit(the_dtor_fn) which needs to be implemented on
MSVC targets. Forward the call directly to atexit, thus:

extern "C" void
__cxa_atexit(void (arg1)(), void, void*)
{
atexit(arg1);
}

and the CRT will similarly unwind the construction order on exit.

All that in place, things seem to work, at least in the test case(s) I've got
here. Further testing required. I don't claim the above is the correct/perfect
answer, but perhaps it will help focus in on a final solution.

@jckarter
Copy link
Contributor

I just got through manually working around this bug in my compiler. The workarounds described by Harry Denholm worked, with the caveat that the symbols generated into the .CRT$XCU section must also be protected against internalization. libcmt.lib's mainCRTStartup appears to be unable to run initializers with internal linkage.

@jckarter
Copy link
Contributor

jckarter commented Apr 8, 2012

This should be fixed by r151289.

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 3, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla
Projects
None yet
Development

No branches or pull requests

3 participants