Skip to content

Commit 149de8d

Browse files
committedOct 5, 2018
[LLD][COFF] Fix ordering of CRT global initializers in COMDAT sections
(patch by Benoit Rousseau) This patch fixes a bug where the global variable initializers were sometimes not invoked in the correct order when it involved a C++ template instantiation. Differential Revision: https://reviews.llvm.org/D52749 llvm-svn: 343847
1 parent 3c19674 commit 149de8d

File tree

6 files changed

+192
-6
lines changed

6 files changed

+192
-6
lines changed
 

‎lld/COFF/Chunks.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,13 @@ void SectionChunk::replace(SectionChunk *Other) {
590590
Other->Live = false;
591591
}
592592

593+
uint32_t SectionChunk::getSectionNumber() const {
594+
DataRefImpl R;
595+
R.p = reinterpret_cast<uintptr_t>(Header);
596+
SectionRef S(R, File->getCOFFObj());
597+
return S.getIndex() + 1;
598+
}
599+
593600
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
594601
// Common symbols are aligned on natural boundaries up to 32 bytes.
595602
// This is what MSVC link.exe does.

‎lld/COFF/Chunks.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,13 @@ class SectionChunk final : public Chunk {
203203
// Allow iteration over the associated child chunks for this section.
204204
ArrayRef<SectionChunk *> children() const { return AssocChildren; }
205205

206+
// The section ID this chunk belongs to in its Obj.
207+
uint32_t getSectionNumber() const;
208+
206209
// A pointer pointing to a replacement for this chunk.
207210
// Initially it points to "this" object. If this chunk is merged
208211
// with other chunk by ICF, it points to another chunk,
209-
// and this chunk is considrered as dead.
212+
// and this chunk is considered as dead.
210213
SectionChunk *Repl;
211214

212215
// The CRC of the contents as described in the COFF spec 4.5.5.

‎lld/COFF/Writer.cpp

+47-5
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ class Writer {
192192
void writeSections();
193193
void writeBuildId();
194194
void sortExceptionTable();
195+
void sortCRTSectionChunks(std::vector<Chunk *> &Chunks);
195196

196197
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
197198
size_t addEntryToStringTable(StringRef Str);
@@ -732,13 +733,18 @@ void Writer::createSections() {
732733
StringRef Name = getOutputSectionName(Pair.first.first);
733734
uint32_t OutChars = Pair.first.second;
734735

735-
// In link.exe, there is a special case for the I386 target where .CRT
736-
// sections are treated as if they have output characteristics DATA | R if
737-
// their characteristics are DATA | R | W. This implements the same special
738-
// case for all architectures.
739-
if (Name == ".CRT")
736+
if (Name == ".CRT") {
737+
// In link.exe, there is a special case for the I386 target where .CRT
738+
// sections are treated as if they have output characteristics DATA | R if
739+
// their characteristics are DATA | R | W. This implements the same
740+
// special case for all architectures.
740741
OutChars = DATA | R;
741742

743+
log("Processing section " + Pair.first.first + " -> " + Name);
744+
745+
sortCRTSectionChunks(Pair.second);
746+
}
747+
742748
OutputSection *Sec = CreateSection(Name, OutChars);
743749
std::vector<Chunk *> &Chunks = Pair.second;
744750
for (Chunk *C : Chunks)
@@ -1577,6 +1583,42 @@ void Writer::sortExceptionTable() {
15771583
errs() << "warning: don't know how to handle .pdata.\n";
15781584
}
15791585

1586+
// The CRT section contains, among other things, the array of function
1587+
// pointers that initialize every global variable that is not trivially
1588+
// constructed. The CRT calls them one after the other prior to invoking
1589+
// main().
1590+
//
1591+
// As per C++ spec, 3.6.2/2.3,
1592+
// "Variables with ordered initialization defined within a single
1593+
// translation unit shall be initialized in the order of their definitions
1594+
// in the translation unit"
1595+
//
1596+
// It is therefore critical to sort the chunks containing the function
1597+
// pointers in the order that they are listed in the object file (top to
1598+
// bottom), otherwise global objects might not be initialized in the
1599+
// correct order.
1600+
void Writer::sortCRTSectionChunks(std::vector<Chunk *> &Chunks) {
1601+
auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) {
1602+
auto SA = dyn_cast<SectionChunk>(A);
1603+
auto SB = dyn_cast<SectionChunk>(B);
1604+
assert(SA && SB && "Non-section chunks in CRT section!");
1605+
1606+
StringRef SAObj = SA->File->MB.getBufferIdentifier();
1607+
StringRef SBObj = SB->File->MB.getBufferIdentifier();
1608+
1609+
return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber();
1610+
};
1611+
std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder);
1612+
1613+
if (Config->Verbose) {
1614+
for (auto &C : Chunks) {
1615+
auto SC = dyn_cast<SectionChunk>(C);
1616+
log(" " + SC->File->MB.getBufferIdentifier().str() +
1617+
", SectionID: " + Twine(SC->getSectionNumber()));
1618+
}
1619+
}
1620+
}
1621+
15801622
OutputSection *Writer::findSection(StringRef Name) {
15811623
for (OutputSection *Sec : OutputSections)
15821624
if (Sec->Name == Name)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--- !COFF
2+
header:
3+
Machine: IMAGE_FILE_MACHINE_AMD64
4+
Characteristics: [ ]
5+
sections:
6+
- Name: '.CRT$XCU'
7+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
8+
Alignment: 1
9+
SectionData: 55
10+
- Name: '.CRT$XCU'
11+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
12+
Alignment: 1
13+
SectionData: 70
14+
symbols:
15+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--- !COFF
2+
header:
3+
Machine: IMAGE_FILE_MACHINE_AMD64
4+
Characteristics: [ ]
5+
sections:
6+
- Name: '.CRT$XCU'
7+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
8+
Alignment: 1
9+
SectionData: 10
10+
- Name: '.CRT$XCU'
11+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
12+
Alignment: 1
13+
SectionData: 11
14+
- Name: '.CRT$XCU'
15+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
16+
Alignment: 1
17+
SectionData: 12
18+
symbols:
19+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# // a.cpp
2+
# #include <iostream>
3+
# #include <vector>
4+
#
5+
# template <int Magic> struct TemplatedObject {
6+
# static std::vector<TemplatedObject<Magic> *> Instances;
7+
# TemplatedObject() { Instances.push_back(this); }
8+
# };
9+
#
10+
# using Object = TemplatedObject<0>;
11+
# template <> std::vector<Object *> Object::Instances{};
12+
# Object idle{};
13+
#
14+
# int main() {
15+
# if (Object::Instances.size() == 0)
16+
# std::cout << "It's broken" << std::endl;
17+
# else
18+
# std::cout << "It works!" << std::endl;
19+
# return 0;
20+
# }
21+
# // using `clang-cl /c a.cpp | lld-link a.obj` works
22+
# // using `cl /c a.cpp | lld-link a.obj` fails without lld/COFF/Writer.cpp/Writer::sortSectionChunks()
23+
24+
# RUN: yaml2obj %s > %t.obj
25+
# RUN: yaml2obj %S/Inputs/crt-dyn-initializer-order_1.yaml > %t1.obj
26+
# RUN: yaml2obj %S/Inputs/crt-dyn-initializer-order_2.yaml > %t2.obj
27+
28+
# CHECK: Name: .CRT
29+
# CHECK: Characteristics [
30+
# CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
31+
# CHECK-NEXT: IMAGE_SCN_MEM_READ
32+
# CHECK-NEXT: ]
33+
# CHECK-NEXT: SectionData (
34+
35+
# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj %t1.obj %t2.obj
36+
# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE1
37+
# CASE1-NEXT: 01020304 55701011 1205
38+
39+
# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t.obj %t2.obj %t1.obj
40+
# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE2
41+
# CASE2-NEXT: 01020304 10111255 7005
42+
43+
# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t1.obj %t2.obj %t.obj
44+
# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE3
45+
# CASE3-NEXT: 01557010 11120203 0405
46+
47+
# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t1.obj %t.obj %t2.obj
48+
# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE4
49+
# CASE4-NEXT: 01557002 03041011 1205
50+
51+
# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t2.obj %t1.obj %t.obj
52+
# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE5
53+
# CASE5-NEXT: 01101112 55700203 0405
54+
55+
# RUN: lld-link /out:%t.dll /entry:__ImageBase /dll %t2.obj %t.obj %t1.obj
56+
# RUN: llvm-readobj -sections -section-data %t.dll | FileCheck %s --check-prefixes CHECK,CASE6
57+
# CASE6-NEXT: 01101112 02030455 7005
58+
59+
# CHECK-NEXT: )
60+
61+
--- !COFF
62+
header:
63+
Machine: IMAGE_FILE_MACHINE_AMD64
64+
Characteristics: [ ]
65+
sections:
66+
- Name: '.CRT$XCA'
67+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
68+
Alignment: 1
69+
SectionData: 01
70+
- Name: '.CRT$XCU'
71+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
72+
Alignment: 1
73+
SectionData: 02
74+
- Name: '.CRT$XCU'
75+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_LNK_COMDAT ]
76+
Alignment: 1
77+
SectionData: 03
78+
- Name: '.CRT$XCU'
79+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
80+
Alignment: 1
81+
SectionData: 04
82+
- Name: '.CRT$XCZ'
83+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
84+
Alignment: 1
85+
SectionData: 05
86+
symbols:
87+
- Name: '.CRT$XCU'
88+
Value: 0
89+
SectionNumber: 3
90+
SimpleType: IMAGE_SYM_TYPE_NULL
91+
ComplexType: IMAGE_SYM_DTYPE_NULL
92+
StorageClass: IMAGE_SYM_CLASS_STATIC
93+
SectionDefinition:
94+
Length: 1
95+
NumberOfRelocations: 0
96+
NumberOfLinenumbers: 0
97+
CheckSum: 1
98+
Number: 2
99+
Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
100+
...

0 commit comments

Comments
 (0)
Please sign in to comment.