// 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) {
fatal(toString(std::move(ec)));
}
+ if (config->mingw)
+ parser.cleanUpManifests(duplicates);
for (const auto &dupeDiag : duplicates)
if (config->forceMultipleRes)
--- /dev/null
+--- !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
+...
--- /dev/null
+--- !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
+...
--- /dev/null
+--- !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
+...
--- /dev/null
+# 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}}
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; }
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;
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>>
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) {
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();
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));
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();
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,