Skip to content

Commit d581dd5

Browse files
committed
[LLD] [COFF] Implement MinGW default manifest handling
In mingw environments, resources are normally compiled to resource object files directly, instead of letting the linker convert them to COFF format. Since some time, GCC supports the notion of a default manifest object. When invoking the linker, GCC looks for the default manifest object file, and if found in the expected path, it is added to linker commands. The default manifest is one that indicates support for the latest known versions of windows, to implicitly unlock the modern behaviours of certain APIs. Not all mingw/gcc distributions include this file, but e.g. in msys2, the default manifest object is distributed in a separate package (which can be but might not always be installed). This means that even if user projects only use one single resource object file, the linker can end up with two resource object files, and thus needs to support merging them. The default manifest has a language id of zero, and GNU ld has got logic for dropping a manifest with a zero language id, if there's another manifest present with a nonzero language id. If there are multiple manifests with a nonzero language id, the merging process errors out. Differential Revision: https://reviews.llvm.org/D66825 llvm-svn: 370974
1 parent a6e8b68 commit d581dd5

10 files changed

+229
-7
lines changed

lld/COFF/DriverUtils.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
702702
// Does what cvtres.exe does, but in-process and cross-platform.
703703
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
704704
ArrayRef<ObjFile *> objs) {
705-
object::WindowsResourceParser parser;
705+
object::WindowsResourceParser parser(/* MinGW */ config->mingw);
706706

707707
std::vector<std::string> duplicates;
708708
for (MemoryBufferRef mb : mbs) {
@@ -727,6 +727,8 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
727727
fatal(toString(std::move(ec)));
728728
}
729729

730+
if (config->mingw)
731+
parser.cleanUpManifests(duplicates);
730732

731733
for (const auto &dupeDiag : duplicates)
732734
if (config->forceMultipleRes)
80 Bytes
Binary file not shown.
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--- !COFF
2+
header:
3+
Machine: IMAGE_FILE_MACHINE_AMD64
4+
Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED ]
5+
sections:
6+
- Name: .rsrc
7+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
8+
Alignment: 4
9+
SectionData: 000000000000000000000000000001001800000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000580000000E00000000000000000000006D616E69666573742D6C616E67300000
10+
Relocations:
11+
- VirtualAddress: 72
12+
SymbolName: .rsrc
13+
Type: IMAGE_REL_AMD64_ADDR32NB
14+
symbols:
15+
- Name: .rsrc
16+
Value: 0
17+
SectionNumber: 1
18+
SimpleType: IMAGE_SYM_TYPE_NULL
19+
ComplexType: IMAGE_SYM_DTYPE_NULL
20+
StorageClass: IMAGE_SYM_CLASS_STATIC
21+
...
80 Bytes
Binary file not shown.
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--- !COFF
2+
header:
3+
Machine: IMAGE_FILE_MACHINE_AMD64
4+
Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED ]
5+
sections:
6+
- Name: .rsrc
7+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
8+
Alignment: 4
9+
SectionData: 000000000000000000000000000001001800000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000100000048000000580000000E00000000000000000000006D616E69666573742D6C616E67310000
10+
Relocations:
11+
- VirtualAddress: 72
12+
SymbolName: .rsrc
13+
Type: IMAGE_REL_AMD64_ADDR32NB
14+
symbols:
15+
- Name: .rsrc
16+
Value: 0
17+
SectionNumber: 1
18+
SimpleType: IMAGE_SYM_TYPE_NULL
19+
ComplexType: IMAGE_SYM_DTYPE_NULL
20+
StorageClass: IMAGE_SYM_CLASS_STATIC
21+
...
80 Bytes
Binary file not shown.
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--- !COFF
2+
header:
3+
Machine: IMAGE_FILE_MACHINE_AMD64
4+
Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED ]
5+
sections:
6+
- Name: .rsrc
7+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
8+
Alignment: 4
9+
SectionData: 000000000000000000000000000001001800000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000200000048000000580000000E00000000000000000000006D616E69666573742D6C616E67320000
10+
Relocations:
11+
- VirtualAddress: 72
12+
SymbolName: .rsrc
13+
Type: IMAGE_REL_AMD64_ADDR32NB
14+
symbols:
15+
- Name: .rsrc
16+
Value: 0
17+
SectionNumber: 1
18+
SimpleType: IMAGE_SYM_TYPE_NULL
19+
ComplexType: IMAGE_SYM_DTYPE_NULL
20+
StorageClass: IMAGE_SYM_CLASS_STATIC
21+
...
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.o
2+
3+
# RUN: yaml2obj < %p/Inputs/manifest-lang0.yaml > %t-manifest-lang0.o
4+
# RUN: yaml2obj < %p/Inputs/manifest-lang1.yaml > %t-manifest-lang1.o
5+
# RUN: yaml2obj < %p/Inputs/manifest-lang2.yaml > %t-manifest-lang2.o
6+
7+
# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %p/Inputs/manifest-lang0.res %p/Inputs/manifest-lang1.res
8+
# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
9+
10+
# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang0.o %t-manifest-lang1.o
11+
# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
12+
13+
# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %p/Inputs/manifest-lang1.res %p/Inputs/manifest-lang0.res
14+
# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
15+
16+
# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang1.o %t-manifest-lang0.o
17+
# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
18+
19+
# RUN: cp %t-manifest-lang0.o %t-manifest-lang0-copy.o
20+
# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang0.o %t-manifest-lang0-copy.o %t-manifest-lang1.o
21+
# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
22+
23+
# RUN: not lld-link /lldmingw /out:%t.exe /entry:main %t.o %p/Inputs/manifest-lang1.res %p/Inputs/manifest-lang2.res 2>&1 | FileCheck --check-prefix=ERROR %s
24+
25+
# RUN: not lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang1.o %t-manifest-lang2.o 2>&1 | FileCheck --check-prefix=ERROR %s
26+
27+
# CHECK: Resources [
28+
# CHECK-NEXT: Total Number of Resources: 1
29+
# CHECK-NEXT: Base Table Address: 0x
30+
# CHECK-NEXT: {{ }}
31+
# CHECK-NEXT: Number of String Entries: 0
32+
# CHECK-NEXT: Number of ID Entries: 1
33+
# CHECK-NEXT: Type: MANIFEST (ID 24) [
34+
# CHECK-NEXT: Table Offset: 0x18
35+
# CHECK-NEXT: Number of String Entries: 0
36+
# CHECK-NEXT: Number of ID Entries: 1
37+
# CHECK-NEXT: Name: (ID 1) [
38+
# CHECK-NEXT: Table Offset: 0x30
39+
# CHECK-NEXT: Number of String Entries: 0
40+
# CHECK-NEXT: Number of ID Entries: 1
41+
# CHECK-NEXT: Language: (ID 1) [
42+
# CHECK-NEXT: Entry Offset: 0x48
43+
# CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
44+
# CHECK-NEXT: Major Version: 0
45+
# CHECK-NEXT: Minor Version: 0
46+
# CHECK-NEXT: Characteristics: 0
47+
# CHECK-NEXT: Data [
48+
# CHECK-NEXT: DataRVA:
49+
# CHECK-NEXT: DataSize: 14
50+
# CHECK-NEXT: Codepage: 0
51+
# CHECK-NEXT: Reserved: 0
52+
# CHECK-NEXT: Data (
53+
# CHECK-NEXT: 0000: 6D616E69 66657374 2D6C616E 6731 |manifest-lang1|
54+
# CHECK-NEXT: )
55+
# CHECK-NEXT: ]
56+
# CHECK-NEXT: ]
57+
# CHECK-NEXT: ]
58+
# CHECK-NEXT: ]
59+
# CHECK-NEXT: ]
60+
61+
# ERROR: error: duplicate non-default manifests with languages 1 in {{.*}}manifest-lang1.{{res|o}} and 2 in {{.*}}manifest-lang2.{{res|o}}

llvm/include/llvm/Object/WindowsResource.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,11 @@ class WindowsResource : public Binary {
153153
class WindowsResourceParser {
154154
public:
155155
class TreeNode;
156-
WindowsResourceParser();
156+
WindowsResourceParser(bool MinGW = false);
157157
Error parse(WindowsResource *WR, std::vector<std::string> &Duplicates);
158158
Error parse(ResourceSectionRef &RSR, StringRef Filename,
159159
std::vector<std::string> &Duplicates);
160+
void cleanUpManifests(std::vector<std::string> &Duplicates);
160161
void printTree(raw_ostream &OS) const;
161162
const TreeNode &getTree() const { return Root; }
162163
const ArrayRef<std::vector<uint8_t>> getData() const { return Data; }
@@ -216,6 +217,7 @@ class WindowsResourceParser {
216217
TreeNode &addIDChild(uint32_t ID);
217218
TreeNode &addNameChild(ArrayRef<UTF16> NameRef,
218219
std::vector<std::vector<UTF16>> &StringTable);
220+
void shiftDataIndexDown(uint32_t Index);
219221

220222
bool IsDataNode = false;
221223
uint32_t StringIndex;
@@ -245,12 +247,16 @@ class WindowsResourceParser {
245247
const coff_resource_dir_table &Table, uint32_t Origin,
246248
std::vector<StringOrID> &Context,
247249
std::vector<std::string> &Duplicates);
250+
bool shouldIgnoreDuplicate(const ResourceEntryRef &Entry) const;
251+
bool shouldIgnoreDuplicate(const std::vector<StringOrID> &Context) const;
248252

249253
TreeNode Root;
250254
std::vector<std::vector<uint8_t>> Data;
251255
std::vector<std::vector<UTF16>> StringTable;
252256

253257
std::vector<std::string> InputFilenames;
258+
259+
bool MinGW;
254260
};
255261

256262
Expected<std::unique_ptr<MemoryBuffer>>

llvm/lib/Object/WindowsResource.cpp

+95-5
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ Error ResourceEntryRef::loadNext() {
137137
return Error::success();
138138
}
139139

140-
WindowsResourceParser::WindowsResourceParser() : Root(false) {}
140+
WindowsResourceParser::WindowsResourceParser(bool MinGW)
141+
: Root(false), MinGW(MinGW) {}
141142

142143
void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {
143144
switch (TypeID) {
@@ -251,6 +252,80 @@ static std::string makeDuplicateResourceError(
251252
return OS.str();
252253
}
253254

255+
// MinGW specific. Remove default manifests (with language zero) if there are
256+
// other manifests present, and report an error if there are more than one
257+
// manifest with a non-zero language code.
258+
// GCC has the concept of a default manifest resource object, which gets
259+
// linked in implicitly if present. This default manifest has got language
260+
// id zero, and should be dropped silently if there's another manifest present.
261+
// If the user resources surprisignly had a manifest with language id zero,
262+
// we should also ignore the duplicate default manifest.
263+
void WindowsResourceParser::cleanUpManifests(
264+
std::vector<std::string> &Duplicates) {
265+
auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);
266+
if (TypeIt == Root.IDChildren.end())
267+
return;
268+
269+
TreeNode *TypeNode = TypeIt->second.get();
270+
auto NameIt =
271+
TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
272+
if (NameIt == TypeNode->IDChildren.end())
273+
return;
274+
275+
TreeNode *NameNode = NameIt->second.get();
276+
if (NameNode->IDChildren.size() <= 1)
277+
return; // None or one manifest present, all good.
278+
279+
// If we have more than one manifest, drop the language zero one if present,
280+
// and check again.
281+
auto LangZeroIt = NameNode->IDChildren.find(0);
282+
if (LangZeroIt != NameNode->IDChildren.end() &&
283+
LangZeroIt->second->IsDataNode) {
284+
uint32_t RemovedIndex = LangZeroIt->second->DataIndex;
285+
NameNode->IDChildren.erase(LangZeroIt);
286+
Data.erase(Data.begin() + RemovedIndex);
287+
Root.shiftDataIndexDown(RemovedIndex);
288+
289+
// If we're now down to one manifest, all is good.
290+
if (NameNode->IDChildren.size() <= 1)
291+
return;
292+
}
293+
294+
// More than one non-language-zero manifest
295+
auto FirstIt = NameNode->IDChildren.begin();
296+
uint32_t FirstLang = FirstIt->first;
297+
TreeNode *FirstNode = FirstIt->second.get();
298+
auto LastIt = NameNode->IDChildren.rbegin();
299+
uint32_t LastLang = LastIt->first;
300+
TreeNode *LastNode = LastIt->second.get();
301+
Duplicates.push_back(
302+
("duplicate non-default manifests with languages " + Twine(FirstLang) +
303+
" in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +
304+
" in " + InputFilenames[LastNode->Origin])
305+
.str());
306+
}
307+
308+
// Ignore duplicates of manifests with language zero (the default manifest),
309+
// in case the user has provided a manifest with that language id. See
310+
// the function comment above for context. Only returns true if MinGW is set
311+
// to true.
312+
bool WindowsResourceParser::shouldIgnoreDuplicate(
313+
const ResourceEntryRef &Entry) const {
314+
return MinGW && !Entry.checkTypeString() &&
315+
Entry.getTypeID() == /* RT_MANIFEST */ 24 &&
316+
!Entry.checkNameString() &&
317+
Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
318+
Entry.getLanguage() == 0;
319+
}
320+
321+
bool WindowsResourceParser::shouldIgnoreDuplicate(
322+
const std::vector<StringOrID> &Context) const {
323+
return MinGW && Context.size() == 3 && !Context[0].IsString &&
324+
Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&
325+
Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
326+
!Context[2].IsString && Context[2].ID == 0;
327+
}
328+
254329
Error WindowsResourceParser::parse(WindowsResource *WR,
255330
std::vector<std::string> &Duplicates) {
256331
auto EntryOrErr = WR->getHeadEntry();
@@ -278,8 +353,9 @@ Error WindowsResourceParser::parse(WindowsResource *WR,
278353
TreeNode *Node;
279354
bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);
280355
if (!IsNewNode) {
281-
Duplicates.push_back(makeDuplicateResourceError(
282-
Entry, InputFilenames[Node->Origin], WR->getFileName()));
356+
if (!shouldIgnoreDuplicate(Entry))
357+
Duplicates.push_back(makeDuplicateResourceError(
358+
Entry, InputFilenames[Node->Origin], WR->getFileName()));
283359
}
284360

285361
RETURN_IF_ERROR(Entry.moveNext(End));
@@ -362,8 +438,9 @@ Error WindowsResourceParser::addChildren(TreeNode &Node,
362438
reinterpret_cast<const uint8_t *>(Contents.data()),
363439
Contents.size()));
364440
} else {
365-
Duplicates.push_back(makeDuplicateResourceError(
366-
Context, InputFilenames[Child->Origin], InputFilenames.back()));
441+
if (!shouldIgnoreDuplicate(Context))
442+
Duplicates.push_back(makeDuplicateResourceError(
443+
Context, InputFilenames[Child->Origin], InputFilenames.back()));
367444
}
368445
Context.pop_back();
369446

@@ -508,6 +585,19 @@ uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
508585
return Size;
509586
}
510587

588+
// Shift DataIndex of all data children with an Index greater or equal to the
589+
// given one, to fill a gap from removing an entry from the Data vector.
590+
void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {
591+
if (IsDataNode && DataIndex >= Index) {
592+
DataIndex--;
593+
} else {
594+
for (auto &Child : IDChildren)
595+
Child.second->shiftDataIndexDown(Index);
596+
for (auto &Child : StringChildren)
597+
Child.second->shiftDataIndexDown(Index);
598+
}
599+
}
600+
511601
class WindowsResourceCOFFWriter {
512602
public:
513603
WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,

0 commit comments

Comments
 (0)