summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Storsjo <martin@martin.st>2019-09-04 20:34:00 +0000
committerMartin Storsjo <martin@martin.st>2019-09-04 20:34:00 +0000
commitd581dd50138186fe2c29eff617e7ca5897e3c69e (patch)
tree8c2400b2af40312f8bcde615019c793305802778
parenta6e8b685e13492abfb2e58dc49007deda165a00d (diff)
downloadbcm5719-llvm-d581dd50138186fe2c29eff617e7ca5897e3c69e.tar.gz
bcm5719-llvm-d581dd50138186fe2c29eff617e7ca5897e3c69e.zip
[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
-rw-r--r--lld/COFF/DriverUtils.cpp4
-rw-r--r--lld/test/COFF/Inputs/manifest-lang0.resbin0 -> 80 bytes
-rw-r--r--lld/test/COFF/Inputs/manifest-lang0.yaml21
-rw-r--r--lld/test/COFF/Inputs/manifest-lang1.resbin0 -> 80 bytes
-rw-r--r--lld/test/COFF/Inputs/manifest-lang1.yaml21
-rw-r--r--lld/test/COFF/Inputs/manifest-lang2.resbin0 -> 80 bytes
-rw-r--r--lld/test/COFF/Inputs/manifest-lang2.yaml21
-rw-r--r--lld/test/COFF/merge-resource-manifest.test61
-rw-r--r--llvm/include/llvm/Object/WindowsResource.h8
-rw-r--r--llvm/lib/Object/WindowsResource.cpp100
10 files changed, 229 insertions, 7 deletions
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index ab96e227a3c..b525ffdae9c 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -702,7 +702,7 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
// Does what cvtres.exe does, but in-process and cross-platform.
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
ArrayRef<ObjFile *> objs) {
- object::WindowsResourceParser parser;
+ object::WindowsResourceParser parser(/* MinGW */ config->mingw);
std::vector<std::string> duplicates;
for (MemoryBufferRef mb : mbs) {
@@ -727,6 +727,8 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
fatal(toString(std::move(ec)));
}
+ if (config->mingw)
+ parser.cleanUpManifests(duplicates);
for (const auto &dupeDiag : duplicates)
if (config->forceMultipleRes)
diff --git a/lld/test/COFF/Inputs/manifest-lang0.res b/lld/test/COFF/Inputs/manifest-lang0.res
new file mode 100644
index 00000000000..555139018d3
--- /dev/null
+++ b/lld/test/COFF/Inputs/manifest-lang0.res
Binary files differ
diff --git a/lld/test/COFF/Inputs/manifest-lang0.yaml b/lld/test/COFF/Inputs/manifest-lang0.yaml
new file mode 100644
index 00000000000..a3f6eba5261
--- /dev/null
+++ b/lld/test/COFF/Inputs/manifest-lang0.yaml
@@ -0,0 +1,21 @@
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED ]
+sections:
+ - Name: .rsrc
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: 000000000000000000000000000001001800000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000580000000E00000000000000000000006D616E69666573742D6C616E67300000
+ Relocations:
+ - VirtualAddress: 72
+ SymbolName: .rsrc
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .rsrc
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/lld/test/COFF/Inputs/manifest-lang1.res b/lld/test/COFF/Inputs/manifest-lang1.res
new file mode 100644
index 00000000000..dce7902a6d2
--- /dev/null
+++ b/lld/test/COFF/Inputs/manifest-lang1.res
Binary files differ
diff --git a/lld/test/COFF/Inputs/manifest-lang1.yaml b/lld/test/COFF/Inputs/manifest-lang1.yaml
new file mode 100644
index 00000000000..6c466991a62
--- /dev/null
+++ b/lld/test/COFF/Inputs/manifest-lang1.yaml
@@ -0,0 +1,21 @@
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED ]
+sections:
+ - Name: .rsrc
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: 000000000000000000000000000001001800000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000100000048000000580000000E00000000000000000000006D616E69666573742D6C616E67310000
+ Relocations:
+ - VirtualAddress: 72
+ SymbolName: .rsrc
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .rsrc
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/lld/test/COFF/Inputs/manifest-lang2.res b/lld/test/COFF/Inputs/manifest-lang2.res
new file mode 100644
index 00000000000..a37c4fcc20f
--- /dev/null
+++ b/lld/test/COFF/Inputs/manifest-lang2.res
Binary files differ
diff --git a/lld/test/COFF/Inputs/manifest-lang2.yaml b/lld/test/COFF/Inputs/manifest-lang2.yaml
new file mode 100644
index 00000000000..3331b9c0a1a
--- /dev/null
+++ b/lld/test/COFF/Inputs/manifest-lang2.yaml
@@ -0,0 +1,21 @@
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED ]
+sections:
+ - Name: .rsrc
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: 000000000000000000000000000001001800000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000200000048000000580000000E00000000000000000000006D616E69666573742D6C616E67320000
+ Relocations:
+ - VirtualAddress: 72
+ SymbolName: .rsrc
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .rsrc
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/lld/test/COFF/merge-resource-manifest.test b/lld/test/COFF/merge-resource-manifest.test
new file mode 100644
index 00000000000..f15a9dedcd0
--- /dev/null
+++ b/lld/test/COFF/merge-resource-manifest.test
@@ -0,0 +1,61 @@
+# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.o
+
+# RUN: yaml2obj < %p/Inputs/manifest-lang0.yaml > %t-manifest-lang0.o
+# RUN: yaml2obj < %p/Inputs/manifest-lang1.yaml > %t-manifest-lang1.o
+# RUN: yaml2obj < %p/Inputs/manifest-lang2.yaml > %t-manifest-lang2.o
+
+# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %p/Inputs/manifest-lang0.res %p/Inputs/manifest-lang1.res
+# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
+
+# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang0.o %t-manifest-lang1.o
+# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
+
+# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %p/Inputs/manifest-lang1.res %p/Inputs/manifest-lang0.res
+# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
+
+# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang1.o %t-manifest-lang0.o
+# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
+
+# RUN: cp %t-manifest-lang0.o %t-manifest-lang0-copy.o
+# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.o %t-manifest-lang0.o %t-manifest-lang0-copy.o %t-manifest-lang1.o
+# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s
+
+# 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
+
+# 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
+
+# CHECK: Resources [
+# CHECK-NEXT: Total Number of Resources: 1
+# CHECK-NEXT: Base Table Address: 0x
+# CHECK-NEXT: {{ }}
+# CHECK-NEXT: Number of String Entries: 0
+# CHECK-NEXT: Number of ID Entries: 1
+# CHECK-NEXT: Type: MANIFEST (ID 24) [
+# CHECK-NEXT: Table Offset: 0x18
+# CHECK-NEXT: Number of String Entries: 0
+# CHECK-NEXT: Number of ID Entries: 1
+# CHECK-NEXT: Name: (ID 1) [
+# CHECK-NEXT: Table Offset: 0x30
+# CHECK-NEXT: Number of String Entries: 0
+# CHECK-NEXT: Number of ID Entries: 1
+# CHECK-NEXT: Language: (ID 1) [
+# CHECK-NEXT: Entry Offset: 0x48
+# CHECK-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
+# CHECK-NEXT: Major Version: 0
+# CHECK-NEXT: Minor Version: 0
+# CHECK-NEXT: Characteristics: 0
+# CHECK-NEXT: Data [
+# CHECK-NEXT: DataRVA:
+# CHECK-NEXT: DataSize: 14
+# CHECK-NEXT: Codepage: 0
+# CHECK-NEXT: Reserved: 0
+# CHECK-NEXT: Data (
+# CHECK-NEXT: 0000: 6D616E69 66657374 2D6C616E 6731 |manifest-lang1|
+# CHECK-NEXT: )
+# CHECK-NEXT: ]
+# CHECK-NEXT: ]
+# CHECK-NEXT: ]
+# CHECK-NEXT: ]
+# CHECK-NEXT: ]
+
+# ERROR: error: duplicate non-default manifests with languages 1 in {{.*}}manifest-lang1.{{res|o}} and 2 in {{.*}}manifest-lang2.{{res|o}}
diff --git a/llvm/include/llvm/Object/WindowsResource.h b/llvm/include/llvm/Object/WindowsResource.h
index 5ddccab292b..a0d658491cb 100644
--- a/llvm/include/llvm/Object/WindowsResource.h
+++ b/llvm/include/llvm/Object/WindowsResource.h
@@ -153,10 +153,11 @@ private:
class WindowsResourceParser {
public:
class TreeNode;
- WindowsResourceParser();
+ WindowsResourceParser(bool MinGW = false);
Error parse(WindowsResource *WR, std::vector<std::string> &Duplicates);
Error parse(ResourceSectionRef &RSR, StringRef Filename,
std::vector<std::string> &Duplicates);
+ void cleanUpManifests(std::vector<std::string> &Duplicates);
void printTree(raw_ostream &OS) const;
const TreeNode &getTree() const { return Root; }
const ArrayRef<std::vector<uint8_t>> getData() const { return Data; }
@@ -216,6 +217,7 @@ public:
TreeNode &addIDChild(uint32_t ID);
TreeNode &addNameChild(ArrayRef<UTF16> NameRef,
std::vector<std::vector<UTF16>> &StringTable);
+ void shiftDataIndexDown(uint32_t Index);
bool IsDataNode = false;
uint32_t StringIndex;
@@ -245,12 +247,16 @@ private:
const coff_resource_dir_table &Table, uint32_t Origin,
std::vector<StringOrID> &Context,
std::vector<std::string> &Duplicates);
+ bool shouldIgnoreDuplicate(const ResourceEntryRef &Entry) const;
+ bool shouldIgnoreDuplicate(const std::vector<StringOrID> &Context) const;
TreeNode Root;
std::vector<std::vector<uint8_t>> Data;
std::vector<std::vector<UTF16>> StringTable;
std::vector<std::string> InputFilenames;
+
+ bool MinGW;
};
Expected<std::unique_ptr<MemoryBuffer>>
diff --git a/llvm/lib/Object/WindowsResource.cpp b/llvm/lib/Object/WindowsResource.cpp
index 726308bb783..10717718b20 100644
--- a/llvm/lib/Object/WindowsResource.cpp
+++ b/llvm/lib/Object/WindowsResource.cpp
@@ -137,7 +137,8 @@ Error ResourceEntryRef::loadNext() {
return Error::success();
}
-WindowsResourceParser::WindowsResourceParser() : Root(false) {}
+WindowsResourceParser::WindowsResourceParser(bool MinGW)
+ : Root(false), MinGW(MinGW) {}
void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) {
switch (TypeID) {
@@ -251,6 +252,80 @@ static std::string makeDuplicateResourceError(
return OS.str();
}
+// MinGW specific. Remove default manifests (with language zero) if there are
+// other manifests present, and report an error if there are more than one
+// manifest with a non-zero language code.
+// GCC has the concept of a default manifest resource object, which gets
+// linked in implicitly if present. This default manifest has got language
+// id zero, and should be dropped silently if there's another manifest present.
+// If the user resources surprisignly had a manifest with language id zero,
+// we should also ignore the duplicate default manifest.
+void WindowsResourceParser::cleanUpManifests(
+ std::vector<std::string> &Duplicates) {
+ auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ 24);
+ if (TypeIt == Root.IDChildren.end())
+ return;
+
+ TreeNode *TypeNode = TypeIt->second.get();
+ auto NameIt =
+ TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1);
+ if (NameIt == TypeNode->IDChildren.end())
+ return;
+
+ TreeNode *NameNode = NameIt->second.get();
+ if (NameNode->IDChildren.size() <= 1)
+ return; // None or one manifest present, all good.
+
+ // If we have more than one manifest, drop the language zero one if present,
+ // and check again.
+ auto LangZeroIt = NameNode->IDChildren.find(0);
+ if (LangZeroIt != NameNode->IDChildren.end() &&
+ LangZeroIt->second->IsDataNode) {
+ uint32_t RemovedIndex = LangZeroIt->second->DataIndex;
+ NameNode->IDChildren.erase(LangZeroIt);
+ Data.erase(Data.begin() + RemovedIndex);
+ Root.shiftDataIndexDown(RemovedIndex);
+
+ // If we're now down to one manifest, all is good.
+ if (NameNode->IDChildren.size() <= 1)
+ return;
+ }
+
+ // More than one non-language-zero manifest
+ auto FirstIt = NameNode->IDChildren.begin();
+ uint32_t FirstLang = FirstIt->first;
+ TreeNode *FirstNode = FirstIt->second.get();
+ auto LastIt = NameNode->IDChildren.rbegin();
+ uint32_t LastLang = LastIt->first;
+ TreeNode *LastNode = LastIt->second.get();
+ Duplicates.push_back(
+ ("duplicate non-default manifests with languages " + Twine(FirstLang) +
+ " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) +
+ " in " + InputFilenames[LastNode->Origin])
+ .str());
+}
+
+// Ignore duplicates of manifests with language zero (the default manifest),
+// in case the user has provided a manifest with that language id. See
+// the function comment above for context. Only returns true if MinGW is set
+// to true.
+bool WindowsResourceParser::shouldIgnoreDuplicate(
+ const ResourceEntryRef &Entry) const {
+ return MinGW && !Entry.checkTypeString() &&
+ Entry.getTypeID() == /* RT_MANIFEST */ 24 &&
+ !Entry.checkNameString() &&
+ Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
+ Entry.getLanguage() == 0;
+}
+
+bool WindowsResourceParser::shouldIgnoreDuplicate(
+ const std::vector<StringOrID> &Context) const {
+ return MinGW && Context.size() == 3 && !Context[0].IsString &&
+ Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString &&
+ Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 &&
+ !Context[2].IsString && Context[2].ID == 0;
+}
+
Error WindowsResourceParser::parse(WindowsResource *WR,
std::vector<std::string> &Duplicates) {
auto EntryOrErr = WR->getHeadEntry();
@@ -278,8 +353,9 @@ Error WindowsResourceParser::parse(WindowsResource *WR,
TreeNode *Node;
bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Node);
if (!IsNewNode) {
- Duplicates.push_back(makeDuplicateResourceError(
- Entry, InputFilenames[Node->Origin], WR->getFileName()));
+ if (!shouldIgnoreDuplicate(Entry))
+ Duplicates.push_back(makeDuplicateResourceError(
+ Entry, InputFilenames[Node->Origin], WR->getFileName()));
}
RETURN_IF_ERROR(Entry.moveNext(End));
@@ -362,8 +438,9 @@ Error WindowsResourceParser::addChildren(TreeNode &Node,
reinterpret_cast<const uint8_t *>(Contents.data()),
Contents.size()));
} else {
- Duplicates.push_back(makeDuplicateResourceError(
- Context, InputFilenames[Child->Origin], InputFilenames.back()));
+ if (!shouldIgnoreDuplicate(Context))
+ Duplicates.push_back(makeDuplicateResourceError(
+ Context, InputFilenames[Child->Origin], InputFilenames.back()));
}
Context.pop_back();
@@ -508,6 +585,19 @@ uint32_t WindowsResourceParser::TreeNode::getTreeSize() const {
return Size;
}
+// Shift DataIndex of all data children with an Index greater or equal to the
+// given one, to fill a gap from removing an entry from the Data vector.
+void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) {
+ if (IsDataNode && DataIndex >= Index) {
+ DataIndex--;
+ } else {
+ for (auto &Child : IDChildren)
+ Child.second->shiftDataIndexDown(Index);
+ for (auto &Child : StringChildren)
+ Child.second->shiftDataIndexDown(Index);
+ }
+}
+
class WindowsResourceCOFFWriter {
public:
WindowsResourceCOFFWriter(COFF::MachineTypes MachineType,
OpenPOWER on IntegriCloud