Skip to content
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

[-cxx-abi microsoft] dynamic initializers for linkonce_odr data run twice when linking clang and MSVC code #17333

Closed
rnk opened this issue Aug 21, 2013 · 10 comments
Assignees
Labels
bugzilla Issues migrated from bugzilla llvm:codegen

Comments

@rnk
Copy link
Collaborator

rnk commented Aug 21, 2013

Bugzilla Link 16959
Resolution FIXED
Resolved on May 23, 2014 16:35
Version trunk
OS Windows NT
Depends On #17262
Blocks #12849
Attachments double initialization test case
CC @nico,@zygoloid,@timurrrr

Extended Description

The attached test case fails when built with and without -DCONFIG_1 while mixing compilers.

The Microsoft ABI does not use guard variables to avoid double initialization of linkonce_odr data, but my fix for #17262 does.

What's supposed to happen is something like the following:


.CRT$XCU is a section similar to .init_array on Linux / ELF, an array of void function pointers.

Each TU with a weak initializer emits two symbols:
void ("?__E" )(void)
void (* "$initializer$")(void)

The __E symbol is the initializer code stub goes into the usual COMDAT selectany text section, the way we would emit any inline function.

The $initializer$ symbol goes into a .CRT$XCU COMDAT section with the IMAGE_COMDAT_SELECT_ASSOCIATIVE flag, where it is associated with the __E symbol.

This way, the linker picks one __E symbol, and then throws away all the associated function pointers that would have referenced it, thereby leaving only one call to the initializer.


Currently clang orders initializers within a TU by creating one giant function (__GLOBAL_I_a etc) that calls each initializer stub in turn. This means that we can't perform COMDAT elimination on the function pointers in .CRT$XCU.

The LLVM LangRef says that initializers in llvm.global_ctors with the same priority are not ordered, but I wonder if we could loosen that initializers with the same priority in a single TU are called in the order defined by the global_ctors array.

With that change, I don't think clang would need to emit _GLOBAL_I* functions. Are there any known object file formats or platforms where the linker would violate that guarantee?

@rnk
Copy link
Collaborator Author

rnk commented Aug 21, 2013

assigned to @rnk

@rnk
Copy link
Collaborator Author

rnk commented Aug 21, 2013

We can implement priorities by appending the priority to the section name, similar to how ELF does it.

The COFF scheme is supposed to look like this:

.CRT$XCA // marker for array start
.CRT$XCU // default ctors
.CRT$XCZ // marker for array end

It's explained by http://msdn.microsoft.com/en-us/library/bb918180.aspx .

Everything after the $ is sorted and concatenated by the linker.

We should be able to add sections like:
.CRT$XCT000001
.CRT$XCT065535

These will sort prior to the default ctor priority which will go into .CRT$XCU.

@rnk
Copy link
Collaborator Author

rnk commented Aug 21, 2013

After discussing this with Richard, it seems we don't want to guarantee order of execution of global_ctors because GlobalOpt will attempt to symbolically execute each initializer in turn, out of order. By batching up all the initializers in a TU into a GLOBAL__I stub, clang ensures that they either all execute in order, or none at all.

Instead, perhaps I'm looking into this:

  • Split out initializers for static data members of class template specializations into their own global_ctors entries. The language does not guarantee the order of initialization of these. This will allow GlobalOpt to fire more often for Itanium.

  • Steal a priority bit to indicate if an initializer is "unique", meaning should only run once, and then fix LTO global_ctors merging to dedupe unique initializers.

  • LLVM CodeGen sees the unique bit and emits a IMAGE_COMDAT_SELECT_ASSOCIATIVE .CRT$XCU section associated with the initializer.

  • GlobalOpt may have to skip symbolic execution of unique initializers, check what cl.exe does

  • GlobalOpt shouldn't be allowed to modify data unless it knows it has the only definition. Check that this is true.

@rnk
Copy link
Collaborator Author

rnk commented Aug 23, 2013

It turns out that the function pointer going into .CRT$XCU is actually associated with the data, not the initializer (__E).

We don't put the data in global_ctors, so I'll have to widen the struct to be 2 or 3 elements where the third is optional. The third is a pointer to an associated GlobalValue.

@rnk
Copy link
Collaborator Author

rnk commented Apr 23, 2014

I hit this recently, so I'm going to dust off my old patch for it.

@nico
Copy link
Contributor

nico commented May 23, 2014

(fixed in r209555?)

@rnk
Copy link
Collaborator Author

rnk commented May 23, 2014

Yep! r209555. :)

@rnk
Copy link
Collaborator Author

rnk commented Nov 26, 2021

mentioned in issue llvm/llvm-bugzilla-archive#18951

1 similar comment
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 26, 2021

mentioned in issue llvm/llvm-bugzilla-archive#18951

@zmodem
Copy link
Collaborator

zmodem commented Nov 26, 2021

mentioned in issue llvm/llvm-bugzilla-archive#20889

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 9, 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 llvm:codegen
Projects
None yet
Development

No branches or pull requests

4 participants