Index: llvm/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/include/llvm/Object/WindowsResource.h +++ llvm/include/llvm/Object/WindowsResource.h @@ -184,19 +184,23 @@ static std::unique_ptr createIDNode(); static std::unique_ptr createDataNode(uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics); + uint32_t Characteristics, + uint32_t Origin); explicit TreeNode(bool IsStringNode); TreeNode(uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics); + uint32_t Characteristics, uint32_t Origin); - void addEntry(const ResourceEntryRef &Entry, bool &IsNewTypeString, - bool &IsNewNameString); + bool addEntry(const ResourceEntryRef &Entry, uint32_t Origin, + bool &IsNewTypeString, bool &IsNewNameString, + TreeNode *&Result); TreeNode &addTypeNode(const ResourceEntryRef &Entry, bool &IsNewTypeString); TreeNode &addNameNode(const ResourceEntryRef &Entry, bool &IsNewNameString); - TreeNode &addLanguageNode(const ResourceEntryRef &Entry); - TreeNode &addDataChild(uint32_t ID, uint16_t MajorVersion, - uint16_t MinorVersion, uint32_t Characteristics); + bool addLanguageNode(const ResourceEntryRef &Entry, uint32_t Origin, + TreeNode *&Result); + bool addDataChild(uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, + uint32_t Characteristics, uint32_t Origin, + TreeNode *&Result); TreeNode &addIDChild(uint32_t ID); TreeNode &addNameChild(ArrayRef NameRef, bool &IsNewString); @@ -208,12 +212,18 @@ uint16_t MajorVersion = 0; uint16_t MinorVersion = 0; uint32_t Characteristics = 0; + + // The .res file that defined this TreeNode, for diagnostics. + // Index into InputFilenames. + uint32_t Origin; }; private: TreeNode Root; std::vector> Data; std::vector> StringTable; + + std::vector InputFilenames; }; Expected> Index: llvm/lib/Object/WindowsResource.cpp =================================================================== --- llvm/lib/Object/WindowsResource.cpp +++ llvm/lib/Object/WindowsResource.cpp @@ -127,6 +127,39 @@ WindowsResourceParser::WindowsResourceParser() : Root(false) {} +static Error duplicateResourceError(const ResourceEntryRef& Entry, + StringRef File1, StringRef File2) { + std::string Ret; + raw_string_ostream OS(Ret); + + OS << "duplicate resource:"; + + OS << " type "; + if (Entry.checkTypeString()) { + std::string UTF8; + if (!convertUTF16ToUTF8String(Entry.getTypeString(), UTF8)) + UTF8 = "(failed conversion from UTF16)"; + OS << '\"' << UTF8 << '\"'; + } else { + OS << "ID " << Entry.getTypeID(); + } + + OS << "/name "; + if (Entry.checkNameString()) { + std::string UTF8; + if (!convertUTF16ToUTF8String(Entry.getNameString(), UTF8)) + UTF8 = "(failed conversion from UTF16)"; + OS << '\"' << UTF8 << '\"'; + } else { + OS << "ID " << Entry.getNameID(); + } + + OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " + << File2; + + return make_error(OS.str(), object_error::parse_failed); +} + Error WindowsResourceParser::parse(WindowsResource *WR) { auto EntryOrErr = WR->getHeadEntry(); if (!EntryOrErr) { @@ -152,7 +185,13 @@ bool IsNewTypeString = false; bool IsNewNameString = false; - Root.addEntry(Entry, IsNewTypeString, IsNewNameString); + TreeNode* Node; + bool IsNewNode = Root.addEntry(Entry, InputFilenames.size(), + IsNewTypeString, IsNewNameString, Node); + InputFilenames.push_back(WR->getFileName()); + if (!IsNewNode) + return duplicateResourceError(Entry, InputFilenames[Node->Origin], + WR->getFileName()); if (IsNewTypeString) StringTable.push_back(Entry.getTypeString()); @@ -171,12 +210,14 @@ Root.print(Writer, "Resource Tree"); } -void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, +bool WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, + uint32_t Origin, bool &IsNewTypeString, - bool &IsNewNameString) { + bool &IsNewNameString, + TreeNode *&Result) { TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString); TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString); - NameNode.addLanguageNode(Entry); + return NameNode.addLanguageNode(Entry, Origin, Result); } WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { @@ -186,10 +227,11 @@ WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics) + uint32_t Characteristics, + uint32_t Origin) : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion), - Characteristics(Characteristics) { - DataIndex = DataCount++; + Characteristics(Characteristics), Origin(Origin) { + DataIndex = DataCount++; } std::unique_ptr @@ -205,9 +247,10 @@ std::unique_ptr WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics) { + uint32_t Characteristics, + uint32_t Origin) { return std::unique_ptr( - new TreeNode(MajorVersion, MinorVersion, Characteristics)); + new TreeNode(MajorVersion, MinorVersion, Characteristics, Origin)); } WindowsResourceParser::TreeNode & @@ -228,24 +271,21 @@ return addIDChild(Entry.getNameID()); } -WindowsResourceParser::TreeNode & -WindowsResourceParser::TreeNode::addLanguageNode( - const ResourceEntryRef &Entry) { +bool WindowsResourceParser::TreeNode::addLanguageNode( + const ResourceEntryRef &Entry, uint32_t Origin, TreeNode *&Result) { return addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), - Entry.getMinorVersion(), Entry.getCharacteristics()); + Entry.getMinorVersion(), Entry.getCharacteristics(), + Origin, Result); } -WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addDataChild( +bool WindowsResourceParser::TreeNode::addDataChild( uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics) { - auto Child = IDChildren.find(ID); - if (Child == IDChildren.end()) { - auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics); - WindowsResourceParser::TreeNode &Node = *NewChild; - IDChildren.emplace(ID, std::move(NewChild)); - return Node; - } else - return *(Child->second); + uint32_t Characteristics, uint32_t Origin, TreeNode *&Result) { + auto NewChild = + createDataNode(MajorVersion, MinorVersion, Characteristics, Origin); + auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); + Result = ElementInserted.first->second.get(); + return ElementInserted.second; } WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( Index: llvm/test/tools/llvm-cvtres/Inputs/id.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cvtres/Inputs/id.rc @@ -0,0 +1,3 @@ +stringtable begin + 42, "hi" +end Index: llvm/test/tools/llvm-cvtres/Inputs/name.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cvtres/Inputs/name.rc @@ -0,0 +1 @@ +namebar typefoo "test2.rc" Index: llvm/test/tools/llvm-cvtres/duplicate.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cvtres/duplicate.test @@ -0,0 +1,19 @@ +// Check that cvtres rejects duplicate resources. +// The input was generated with the following command, using the original Windows +// rc.exe: +// > rc /fo id.res /nologo id.rc +// > rc /fo name.res /nologo name.rc + +RUN: rm -rf %t.dir +RUN: mkdir %t.dir +RUN: cp %S/Inputs/id.res %t.dir/id1.res +RUN: cp %S/Inputs/id.res %t.dir/id2.res +RUN: not llvm-cvtres /machine:X86 %t.dir/id1.res %t.dir/id2.res 2>&1 | \ +RUN: FileCheck -check-prefix=ID %s +ID: duplicate resource: type ID 6/name ID 3/language 1033, in {{.*}}id1.res and in {{.*}}id2.res + +RUN: cp %S/Inputs/name.res %t.dir/name1.res +RUN: cp %S/Inputs/name.res %t.dir/name2.res +RUN: not llvm-cvtres /machine:X86 %t.dir/name1.res %t.dir/name2.res 2>&1 | \ +RUN: FileCheck -check-prefix=NAME %s +NAME: duplicate resource: type "TYPEFOO"/name "NAMEBAR"/language 1033, in {{.*}}name1.res and in {{.*}}name2.res Index: llvm/tools/llvm-cvtres/llvm-cvtres.cpp =================================================================== --- llvm/tools/llvm-cvtres/llvm-cvtres.cpp +++ llvm/tools/llvm-cvtres/llvm-cvtres.cpp @@ -102,7 +102,7 @@ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); if (InputArgs.hasArg(OPT_HELP)) { - T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter", false); + T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter"); return 0; }