Index: lld/trunk/COFF/DriverUtils.cpp =================================================================== --- lld/trunk/COFF/DriverUtils.cpp +++ lld/trunk/COFF/DriverUtils.cpp @@ -702,7 +702,7 @@ // Does what cvtres.exe does, but in-process and cross-platform. MemoryBufferRef convertResToCOFF(ArrayRef mbs, ArrayRef objs) { - object::WindowsResourceParser parser; + object::WindowsResourceParser parser(/* MinGW */ config->mingw); std::vector duplicates; for (MemoryBufferRef mb : mbs) { @@ -727,6 +727,8 @@ fatal(toString(std::move(ec))); } + if (config->mingw) + parser.cleanUpManifests(duplicates); for (const auto &dupeDiag : duplicates) if (config->forceMultipleRes) Index: lld/trunk/test/COFF/Inputs/manifest-lang0.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/manifest-lang0.yaml +++ lld/trunk/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 +... Index: lld/trunk/test/COFF/Inputs/manifest-lang1.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/manifest-lang1.yaml +++ lld/trunk/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 +... Index: lld/trunk/test/COFF/Inputs/manifest-lang2.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/manifest-lang2.yaml +++ lld/trunk/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 +... Index: lld/trunk/test/COFF/merge-resource-manifest.test =================================================================== --- lld/trunk/test/COFF/merge-resource-manifest.test +++ lld/trunk/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}} Index: llvm/trunk/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/trunk/include/llvm/Object/WindowsResource.h +++ llvm/trunk/include/llvm/Object/WindowsResource.h @@ -153,10 +153,11 @@ class WindowsResourceParser { public: class TreeNode; - WindowsResourceParser(); + WindowsResourceParser(bool MinGW = false); Error parse(WindowsResource *WR, std::vector &Duplicates); Error parse(ResourceSectionRef &RSR, StringRef Filename, std::vector &Duplicates); + void cleanUpManifests(std::vector &Duplicates); void printTree(raw_ostream &OS) const; const TreeNode &getTree() const { return Root; } const ArrayRef> getData() const { return Data; } @@ -216,6 +217,7 @@ TreeNode &addIDChild(uint32_t ID); TreeNode &addNameChild(ArrayRef NameRef, std::vector> &StringTable); + void shiftDataIndexDown(uint32_t Index); bool IsDataNode = false; uint32_t StringIndex; @@ -245,12 +247,16 @@ const coff_resource_dir_table &Table, uint32_t Origin, std::vector &Context, std::vector &Duplicates); + bool shouldIgnoreDuplicate(const ResourceEntryRef &Entry) const; + bool shouldIgnoreDuplicate(const std::vector &Context) const; TreeNode Root; std::vector> Data; std::vector> StringTable; std::vector InputFilenames; + + bool MinGW; }; Expected> Index: llvm/trunk/lib/Object/WindowsResource.cpp =================================================================== --- llvm/trunk/lib/Object/WindowsResource.cpp +++ llvm/trunk/lib/Object/WindowsResource.cpp @@ -137,7 +137,8 @@ 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 @@ 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 &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 &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 &Duplicates) { auto EntryOrErr = WR->getHeadEntry(); @@ -278,8 +353,9 @@ 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 @@ reinterpret_cast(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 @@ 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,