Index: lld/trunk/COFF/Chunks.h =================================================================== --- lld/trunk/COFF/Chunks.h +++ lld/trunk/COFF/Chunks.h @@ -203,10 +203,13 @@ // Allow iteration over the associated child chunks for this section. ArrayRef children() const { return AssocChildren; } + // The section ID this chunk belongs to in its Obj. + uint32_t getSectionNumber() const; + // A pointer pointing to a replacement for this chunk. // Initially it points to "this" object. If this chunk is merged // with other chunk by ICF, it points to another chunk, - // and this chunk is considrered as dead. + // and this chunk is considered as dead. SectionChunk *Repl; // The CRC of the contents as described in the COFF spec 4.5.5. Index: lld/trunk/COFF/Chunks.cpp =================================================================== --- lld/trunk/COFF/Chunks.cpp +++ lld/trunk/COFF/Chunks.cpp @@ -590,6 +590,13 @@ Other->Live = false; } +uint32_t SectionChunk::getSectionNumber() const { + DataRefImpl R; + R.p = reinterpret_cast(Header); + SectionRef S(R, File->getCOFFObj()); + return S.getIndex() + 1; +} + CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { // Common symbols are aligned on natural boundaries up to 32 bytes. // This is what MSVC link.exe does. Index: lld/trunk/COFF/Writer.cpp =================================================================== --- lld/trunk/COFF/Writer.cpp +++ lld/trunk/COFF/Writer.cpp @@ -192,6 +192,7 @@ void writeSections(); void writeBuildId(); void sortExceptionTable(); + void sortCRTSectionChunks(std::vector &Chunks); llvm::Optional createSymbol(Defined *D); size_t addEntryToStringTable(StringRef Str); @@ -732,13 +733,18 @@ StringRef Name = getOutputSectionName(Pair.first.first); uint32_t OutChars = Pair.first.second; - // In link.exe, there is a special case for the I386 target where .CRT - // sections are treated as if they have output characteristics DATA | R if - // their characteristics are DATA | R | W. This implements the same special - // case for all architectures. - if (Name == ".CRT") + if (Name == ".CRT") { + // In link.exe, there is a special case for the I386 target where .CRT + // sections are treated as if they have output characteristics DATA | R if + // their characteristics are DATA | R | W. This implements the same + // special case for all architectures. OutChars = DATA | R; + log("Processing section " + Pair.first.first + " -> " + Name); + + sortCRTSectionChunks(Pair.second); + } + OutputSection *Sec = CreateSection(Name, OutChars); std::vector &Chunks = Pair.second; for (Chunk *C : Chunks) @@ -1577,6 +1583,42 @@ errs() << "warning: don't know how to handle .pdata.\n"; } +// The CRT section contains, among other things, the array of function +// pointers that initialize every global variable that is not trivially +// constructed. The CRT calls them one after the other prior to invoking +// main(). +// +// As per C++ spec, 3.6.2/2.3, +// "Variables with ordered initialization defined within a single +// translation unit shall be initialized in the order of their definitions +// in the translation unit" +// +// It is therefore critical to sort the chunks containing the function +// pointers in the order that they are listed in the object file (top to +// bottom), otherwise global objects might not be initialized in the +// correct order. +void Writer::sortCRTSectionChunks(std::vector &Chunks) { + auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) { + auto SA = dyn_cast(A); + auto SB = dyn_cast(B); + assert(SA && SB && "Non-section chunks in CRT section!"); + + StringRef SAObj = SA->File->MB.getBufferIdentifier(); + StringRef SBObj = SB->File->MB.getBufferIdentifier(); + + return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber(); + }; + std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder); + + if (Config->Verbose) { + for (auto &C : Chunks) { + auto SC = dyn_cast(C); + log(" " + SC->File->MB.getBufferIdentifier().str() + + ", SectionID: " + Twine(SC->getSectionNumber())); + } + } +} + OutputSection *Writer::findSection(StringRef Name) { for (OutputSection *Sec : OutputSections) if (Sec->Name == Name) Index: lld/trunk/test/COFF/Inputs/crt-dyn-initializer-order_1.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/crt-dyn-initializer-order_1.yaml +++ lld/trunk/test/COFF/Inputs/crt-dyn-initializer-order_1.yaml @@ -0,0 +1,15 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 55 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 70 +symbols: +... Index: lld/trunk/test/COFF/Inputs/crt-dyn-initializer-order_2.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/crt-dyn-initializer-order_2.yaml +++ lld/trunk/test/COFF/Inputs/crt-dyn-initializer-order_2.yaml @@ -0,0 +1,19 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 10 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 11 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 12 +symbols: +... Index: lld/trunk/test/COFF/crt-dyn-initializer-order.test =================================================================== --- lld/trunk/test/COFF/crt-dyn-initializer-order.test +++ lld/trunk/test/COFF/crt-dyn-initializer-order.test @@ -0,0 +1,100 @@ +# // a.cpp +# #include +# #include +# +# template struct TemplatedObject { +# static std::vector *> Instances; +# TemplatedObject() { Instances.push_back(this); } +# }; +# +# using Object = TemplatedObject<0>; +# template <> std::vector Object::Instances{}; +# Object idle{}; +# +# int main() { +# if (Object::Instances.size() == 0) +# std::cout << "It's broken" << std::endl; +# else +# std::cout << "It works!" << std::endl; +# return 0; +# } +# // using `clang-cl /c a.cpp | lld-link a.obj` works +# // using `cl /c a.cpp | lld-link a.obj` fails without lld/COFF/Writer.cpp/Writer::sortSectionChunks() + +# RUN: yaml2obj %s > %t.obj +# RUN: yaml2obj %S/Inputs/crt-dyn-initializer-order_1.yaml > %t1.obj +# RUN: yaml2obj %S/Inputs/crt-dyn-initializer-order_2.yaml > %t2.obj + +# CHECK: Name: .CRT +# CHECK: Characteristics [ +# CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA +# CHECK-NEXT: IMAGE_SCN_MEM_READ +# CHECK-NEXT: ] +# CHECK-NEXT: SectionData ( + +# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj %t1.obj %t2.obj +# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE1 +# CASE1-NEXT: 01020304 55701011 1205 + +# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj %t2.obj %t1.obj +# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE2 +# CASE2-NEXT: 01020304 10111255 7005 + +# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t1.obj %t2.obj %t.obj +# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE3 +# CASE3-NEXT: 01557010 11120203 0405 + +# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t1.obj %t.obj %t2.obj +# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE4 +# CASE4-NEXT: 01557002 03041011 1205 + +# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t2.obj %t1.obj %t.obj +# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE5 +# CASE5-NEXT: 01101112 55700203 0405 + +# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t2.obj %t.obj %t1.obj +# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE6 +# CASE6-NEXT: 01101112 02030455 7005 + +# CHECK-NEXT: ) + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: '.CRT$XCA' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 01 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 02 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_LNK_COMDAT ] + Alignment: 1 + SectionData: 03 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 04 + - Name: '.CRT$XCZ' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 05 +symbols: + - Name: '.CRT$XCU' + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE +...