Index: lld/test/COFF/merge-resource-manifest.test =================================================================== --- /dev/null +++ lld/test/COFF/merge-resource-manifest.test @@ -0,0 +1,53 @@ +# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj + +# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.obj %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.obj %p/Inputs/manifest-lang0.o %p/Inputs/manifest-lang1.o +# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s + +# RUN: lld-link /lldmingw /out:%t.exe /entry:main %t.obj %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.obj %p/Inputs/manifest-lang1.o %p/Inputs/manifest-lang0.o +# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s + +# RUN: not lld-link /lldmingw /out:%t.exe /entry:main %t.obj %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.obj %p/Inputs/manifest-lang1.o %p/Inputs/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/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/include/llvm/Object/WindowsResource.h +++ llvm/include/llvm/Object/WindowsResource.h @@ -242,6 +242,8 @@ const coff_resource_dir_table &Table, uint32_t Origin, std::vector &Context, std::vector &Duplicates); + bool checkManifestChildren(TreeNode &Node, uint32_t Lang, uint32_t &DataIndex, + std::vector &Duplicates); TreeNode Root; std::vector> Data; Index: llvm/lib/Object/WindowsResource.cpp =================================================================== --- llvm/lib/Object/WindowsResource.cpp +++ llvm/lib/Object/WindowsResource.cpp @@ -251,6 +251,34 @@ return OS.str(); } +bool WindowsResourceParser::checkManifestChildren( + TreeNode &Node, uint32_t Lang, uint32_t &DataIndex, + std::vector &Duplicates) { + if (Node.IDChildren.size() == 0) + return true; + // Already have one manifest, check what to do. + auto PrevIt = Node.IDChildren.begin(); + uint32_t PrevLang = PrevIt->first; + TreeNode *PrevNode = PrevIt->second.get(); + if (Lang == 0) { + // Ignore lang=0 manifests if we already have another manifest + return false; + } else if (PrevLang == 0) { + // Already have a lang=0 manifest, replace it with the new one, + // and overwrite the old Data slot. + DataIndex = PrevNode->getDataIndex(); + Node.IDChildren.erase(PrevIt); + return true; + } else { + Duplicates.push_back(("duplicate non-default manifests with languages " + + Twine(PrevLang) + " in " + + InputFilenames[PrevNode->Origin] + " and " + + Twine(Lang) + " in " + InputFilenames.back()) + .str()); + return false; + } +} + Error WindowsResourceParser::parse(WindowsResource *WR, std::vector &Duplicates) { auto EntryOrErr = WR->getHeadEntry(); @@ -275,11 +303,32 @@ bool End = false; while (!End) { - TreeNode *Node; uint32_t DataIndex = Data.size(); + if (!Entry.checkTypeString() && Entry.getTypeID() == /* RT_MANIFEST */ 24 && + !Entry.checkNameString() && + Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1) { + auto TypeIt = Root.IDChildren.find(24); + if (TypeIt != Root.IDChildren.end()) { + TreeNode *TypeNode = TypeIt->second.get(); + auto NameIt = TypeNode->IDChildren.find(1); + if (NameIt != TypeNode->IDChildren.end()) { + TreeNode *NameNode = NameIt->second.get(); + if (!checkManifestChildren(*NameNode, Entry.getLanguage(), DataIndex, + Duplicates)) { + RETURN_IF_ERROR(Entry.moveNext(End)); + continue; + } + } + } + } + + TreeNode *Node; bool IsNewNode = Root.addEntry(Entry, Origin, DataIndex, StringTable, Node); if (IsNewNode) { - Data.push_back(Entry.getData()); + if (DataIndex == Data.size()) + Data.push_back(Entry.getData()); + else + Data[DataIndex] = Entry.getData(); } else { Duplicates.push_back(makeDuplicateResourceError( Entry, InputFilenames[Node->Origin], WR->getFileName())); @@ -344,16 +393,28 @@ return createStringError(object_error::parse_failed, "unexpected string key for data object"); UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); + uint32_t DataIndex = Data.size(); + if (Context.size() == 2 && !Context[0].IsString && + Context[0].ID == 24 /* RT_MANIFEST */ && !Context[1].IsString && + Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1) { + if (!checkManifestChildren(Node, Entry.Identifier.ID, DataIndex, + Duplicates)) + continue; + } TreeNode *Child; Context.push_back(StringOrID(Entry.Identifier.ID)); bool Added = Node.addDataChild(Entry.Identifier.ID, Table.MajorVersion, Table.MinorVersion, Table.Characteristics, - Origin, Data.size(), Child); + Origin, DataIndex, Child); if (Added) { UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); - Data.push_back(ArrayRef( + ArrayRef ContentsUint8 = ArrayRef( reinterpret_cast(Contents.data()), - Contents.size())); + Contents.size()); + if (DataIndex == Data.size()) + Data.push_back(ContentsUint8); + else + Data[DataIndex] = ContentsUint8; } else { Duplicates.push_back(makeDuplicateResourceError( Context, InputFilenames[Child->Origin], InputFilenames.back()));