Index: lld/trunk/COFF/Driver.h =================================================================== --- lld/trunk/COFF/Driver.h +++ lld/trunk/COFF/Driver.h @@ -98,6 +98,10 @@ // Library search path. The first element is always "" (current directory). std::vector searchPaths; + // Convert resource files and potentially merge input resource object + // trees into one resource tree. + void convertResources(); + void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args); // We don't want to add the same file more than once. @@ -184,7 +188,8 @@ void checkFailIfMismatch(StringRef arg, InputFile *source); // Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef mbs); +MemoryBufferRef convertResToCOFF(ArrayRef mbs, + ArrayRef objs); void runMSVCLinker(std::string rsp, ArrayRef objects); Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -991,30 +991,37 @@ config->pdbAltPath = buf; } -/// Check that at most one resource obj file was used. +/// Convert resource files and potentially merge input resource object +/// trees into one resource tree. /// Call after ObjFile::Instances is complete. -static void diagnoseMultipleResourceObjFiles() { - // The .rsrc$01 section in a resource obj file contains a tree description - // of resources. Merging multiple resource obj files would require merging - // the trees instead of using usual linker section merging semantics. - // Since link.exe disallows linking more than one resource obj file with - // LNK4078, mirror that. The normal use of resource files is to give the - // linker many .res files, which are then converted to a single resource obj - // file internally, so this is not a big restriction in practice. - ObjFile *resourceObjFile = nullptr; - for (ObjFile *f : ObjFile::instances) { - if (!f->isResourceObjFile) - continue; +void LinkerDriver::convertResources() { + std::vector resourceObjFiles; - if (!resourceObjFile) { - resourceObjFile = f; - continue; - } + for (ObjFile *f : ObjFile::instances) { + if (f->isResourceObjFile()) + resourceObjFiles.push_back(f); + } - error(toString(f) + + if (!config->mingw && + (resourceObjFiles.size() > 1 || + (resourceObjFiles.size() == 1 && !resources.empty()))) { + error((!resources.empty() ? "internal .obj file created from .res files" + : toString(resourceObjFiles[1])) + ": more than one resource obj file not allowed, already got " + - toString(resourceObjFile)); + toString(resourceObjFiles.front())); + return; } + + if (resources.empty() && resourceObjFiles.size() <= 1) { + // No resources to convert, and max one resource object file in + // the input. Keep that preconverted resource section as is. + for (ObjFile *f : resourceObjFiles) + f->includeResourceChunks(); + return; + } + ObjFile *f = make(convertResToCOFF(resources, resourceObjFiles)); + symtab->addFile(f); + f->includeResourceChunks(); } // In MinGW, if no symbols are chosen to be exported, then all symbols are @@ -1583,12 +1590,6 @@ for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg, config->machine); - // Input files can be Windows resource files (.res files). We use - // WindowsResource to convert resource files to a regular COFF file, - // then link the resulting file normally. - if (!resources.empty()) - symtab->addFile(make(convertResToCOFF(resources))); - if (tar) tar->append("response.txt", createResponseFile(args, filePaths, @@ -1906,7 +1907,7 @@ markLive(symtab->getChunks()); // Needs to happen after the last call to addFile(). - diagnoseMultipleResourceObjFiles(); + convertResources(); // Identify identical COMDAT sections to merge them. if (config->doICF) { Index: lld/trunk/COFF/DriverUtils.cpp =================================================================== --- lld/trunk/COFF/DriverUtils.cpp +++ lld/trunk/COFF/DriverUtils.cpp @@ -700,26 +700,40 @@ // Convert Windows resource files (.res files) to a .obj file. // Does what cvtres.exe does, but in-process and cross-platform. -MemoryBufferRef convertResToCOFF(ArrayRef mbs) { +MemoryBufferRef convertResToCOFF(ArrayRef mbs, + ArrayRef objs) { object::WindowsResourceParser parser; + std::vector duplicates; for (MemoryBufferRef mb : mbs) { std::unique_ptr bin = check(object::createBinary(mb)); object::WindowsResource *rf = dyn_cast(bin.get()); if (!rf) fatal("cannot compile non-resource file as resource"); - std::vector duplicates; if (auto ec = parser.parse(rf, duplicates)) fatal(toString(std::move(ec))); + } + + // Note: This processes all .res files before all objs. Ideally they'd be + // handled in the same order they were linked (to keep the right one, if + // there are duplicates that are tolerated due to forceMultipleRes). + for (ObjFile *f : objs) { + object::ResourceSectionRef rsf; + if (auto ec = rsf.load(f->getCOFFObj())) + fatal(toString(f) + ": " + toString(std::move(ec))); - for (const auto &dupeDiag : duplicates) - if (config->forceMultipleRes) - warn(dupeDiag); - else - error(dupeDiag); + if (auto ec = parser.parse(rsf, f->getName(), duplicates)) + fatal(toString(std::move(ec))); } + + for (const auto &dupeDiag : duplicates) + if (config->forceMultipleRes) + warn(dupeDiag); + else + error(dupeDiag); + Expected> e = llvm::object::writeWindowsResourceCOFF(config->machine, parser, config->timestamp); Index: lld/trunk/COFF/InputFiles.h =================================================================== --- lld/trunk/COFF/InputFiles.h +++ lld/trunk/COFF/InputFiles.h @@ -134,6 +134,10 @@ return symbols.size() - 1; } + void includeResourceChunks(); + + bool isResourceObjFile() const { return !resourceChunks.empty(); } + static std::vector instances; // Flags in the absolute @feat.00 symbol if it is present. These usually @@ -161,9 +165,6 @@ // precompiled object. Any difference indicates out-of-date objects. llvm::Optional pchSignature; - // Whether this is an object file created from .res files. - bool isResourceObjFile = false; - // Whether this file was compiled with /hotpatch. bool hotPatchable = false; @@ -233,6 +234,8 @@ // chunks and non-section chunks for common symbols. std::vector chunks; + std::vector resourceChunks; + // CodeView debug info sections. std::vector debugChunks; Index: lld/trunk/COFF/InputFiles.cpp =================================================================== --- lld/trunk/COFF/InputFiles.cpp +++ lld/trunk/COFF/InputFiles.cpp @@ -206,10 +206,6 @@ if (def) c->checksum = def->CheckSum; - // link.exe uses the presence of .rsrc$01 for LNK4078, so match that. - if (name == ".rsrc$01") - isResourceObjFile = true; - // CodeView sections are stored to a different vector because they are not // linked in the regular manner. if (c->isCodeView()) @@ -226,12 +222,18 @@ // relocations, in .rdata, leader symbol name matches the MSVC name mangling // for string literals) are subject to string tail merging. MergeChunk::addSection(c); + else if (name == ".rsrc" || name.startswith(".rsrc$")) + resourceChunks.push_back(c); else chunks.push_back(c); return c; } +void ObjFile::includeResourceChunks() { + chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end()); +} + void ObjFile::readAssociativeDefinition( COFFSymbolRef sym, const coff_aux_section_definition *def) { readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); Index: lld/trunk/test/COFF/Inputs/combined-resources-2.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/combined-resources-2.yaml +++ lld/trunk/test/COFF/Inputs/combined-resources-2.yaml @@ -0,0 +1,137 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ IMAGE_FILE_32BIT_MACHINE ] +sections: + - Name: '.debug$S' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 04000000F30000004E000000005A3A5C686F6D655C6D617274696E5C636F64655C6C6C766D5C746F6F6C735C6C6C645C746573745C434F46465C496E707574735C636F6D62696E65642D7265736F75726365732D322E726573000000F400000018000000010000001001C20ABB57742312AC65B588238495D1AC0000F10000001101000051000111000000005A3A5C686F6D655C6D617274696E5C636F64655C6C6C766D5C746F6F6C735C6C6C645C746573745C434F46465C496E707574735C636F6D62696E65642D7265736F75726365732D322E6F002D003C1108020000D00000000000000000000E001400746B01004D6963726F736F66742028522920435654524553008D003D1100637764005A3A5C686F6D655C6D617274696E5C636F64655C6C6C766D5C746F6F6C735C6C6C645C746573745C434F46465C496E7075747300657865005A3A5C686F6D655C6D617274696E5C6D737663323031395C76635C746F6F6C735C6D7376635C31342E32302E32373530385C62696E5C486F73747838365C7836345C6376747265732E6578650000000000 + Subsections: + - !StringTable + Strings: + - 'Z:\home\martin\code\llvm\tools\lld\test\COFF\Inputs\combined-resources-2.res' + - !FileChecksums + Checksums: + - FileName: 'Z:\home\martin\code\llvm\tools\lld\test\COFF\Inputs\combined-resources-2.res' + Kind: MD5 + Checksum: C20ABB57742312AC65B588238495D1AC + - !Symbols + Records: + - Kind: S_OBJNAME + ObjNameSym: + Signature: 0 + ObjectName: 'Z:\home\martin\code\llvm\tools\lld\test\COFF\Inputs\combined-resources-2.o' + - Kind: S_COMPILE3 + Compile3Sym: + Flags: [ NoDbgInfo ] + Machine: X64 + FrontendMajor: 0 + FrontendMinor: 0 + FrontendBuild: 0 + FrontendQFE: 0 + BackendMajor: 14 + BackendMinor: 20 + BackendBuild: 27508 + BackendQFE: 1 + Version: 'Microsoft (R) CVTRES' + - Kind: S_ENVBLOCK + EnvBlockSym: + Entries: + - cwd + - 'Z:\home\martin\code\llvm\tools\lld\test\COFF\Inputs' + - exe + - 'Z:\home\martin\msvc2019\vc\tools\msvc\14.20.27508\bin\Hostx86\x64\cvtres.exe' + - Name: '.rsrc$01' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + SectionData: 0000000000000000000000000000020009000000200000800A0000003800008000000000000000000000000001000000E40000805000008000000000000000000000000001000000D0000080680000800000000000000000000000000000010004080000900000000000000000000000000000000000030009040000A000000004080000B000000007100000C0000000000000001800000000000000000000000000000036000000000000000000000000000000430000000000000000000000000000004200000000000000000000000900520041004E0044004F004D004400410054000E004D00590041004300430045004C0045005200410054004F00520053000000 + Relocations: + - VirtualAddress: 160 + SymbolName: '$R000000' + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 176 + SymbolName: '$R000038' + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 192 + SymbolName: '$R000080' + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 144 + SymbolName: '$R0000C8' + Type: IMAGE_REL_AMD64_ADDR32NB + - Name: '.rsrc$02' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + SectionData: 7468697320697320612072616E646F6D20626974206F6620646174612074686174206D65616E73206E6F7468696E6700A9230E14F4F600007A68653420736869342079693167653420737569326A693120646520736875346A75342C207A68653420796934776569347A6865207368656E326D6500A9230E14F4F6000000000044696573206973742065696E207A7566C3A46C6C696765732042697420766F6E20446174656E2C20646965206E696368747320626564657574657400A9230E14F4F600000000000011000300E70300000D0044004C04000082001200BC010000 +symbols: + - Name: '@comp.id' + Value: 16739188 + SectionNumber: -1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '@feat.00' + Value: 17 + SectionNumber: -1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '.debug$S' + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 408 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: '.rsrc$01' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 260 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: '.rsrc$02' + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 224 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: '$R000000' + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '$R000038' + Value: 56 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '$R000080' + Value: 128 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '$R0000C8' + Value: 200 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC +... Index: lld/trunk/test/COFF/Inputs/combined-resources.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/combined-resources.yaml +++ lld/trunk/test/COFF/Inputs/combined-resources.yaml @@ -0,0 +1,42 @@ +--- !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: 000000000000000000000000010004008801008038000080020000006800008004000000B80000800500000008010080090000003801008000000000000000000000000001000000A00100805000008000000000000000000000000000000100090400001002000000000000000000000000000002000000B601008088000080C4010080A000008000000000000000000000000000000100090400002002000000000000000000000000000000000100090400003002000000000000000000000000000001000100CE010080D800008060380000F000008000000000000000000000000000000100090C00004002000000000000000000000000000000000100040800005002000000000000000000000000000001000000DA0100802001008000000000000000000000000000000100090400006002000000000000000000000000000001000100F0010080580100800C000000700100800000000000000000000000000000010009040000700200000000000000000000000000000000010009040000800200000B0053005400520049004E004700410052005200410059000A004D0059005200450053004F005500520043004500060043005500520053004F00520004004F004B00410059000500220045004100540022000A0054004500530054004400490041004C004F0047000E004D00590041004300430045004C0045005200410054004F0052005300000090020000390000000000000000000000D0020000280300000000000000000000F805000028030000000000000000000020090000300000000000000000000000500900002E0000000000000000000000800900006C0000000000000000000000F0090000180000000000000000000000080A0000180000000000000000000000746869732069732061207573657220646566696E6564207265736F7572636500697420636F6E7461696E73206D616E7920737472696E67730000000000000000280000001000000010000000010018000000000000030000C40E0000C40E00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F7F7F7C7C7C787878757575FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF979797FFFFFFFFFFFF838383AAAAAADBDBDB797979757575FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C9C9C989898FFFFFF888888DBDBDBB7B7B77D7D7DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0A0A09C9C9C939393ADADADF2F2F2848484818181FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4A4A4D7D7D79D9D9DD0D0D0EEEEEE9191918D8D8DFFFFFFFFFFFF8181817E7E7EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9A9A9F2F2F2E5E5E5E2E2E29595959191918D8D8D898989868686FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFADADADF2F2F2E1E1E1DFDFDFE7E7E7E4E4E4BBBBBB8E8E8EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5B5B5F2F2F2E8E8E8E7E7E7EAEAEAC6C6C69E9E9EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9B9B9F4F4F4ECECECEDEDEDCBCBCBA7A7A7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBDBDBDF7F7F7EFEFEFD0D0D0AFAFAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1C1C1F7F7F7D5D5D5B6B6B6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4C4C4D9D9D9BEBEBEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8C8C8C5C5C5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCBCBCBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF280000001000000010000000010018000000000000030000C40E0000C40E00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0E3A901B31801B31801B31801B31801B31801B31861D06FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01B31800D7331CDB49DBF9E29BEFAF00D73300D73301B318FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01B31800DE55F6FEF9DBFAE7FEFFFE86EFAE00DE5501B318FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01B31800E676DBFBEC00E67657EFA5FBFFFD55EEA401B318FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01B31800ED9800ED9800ED9800ED9887F7CFFEFFFF01B318FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01B31800F4BA00F4BA00F4BA00F4BA00F4BA9CFBE401B318FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01B31800FBDB00FBDB00FBDB00FBDB00FBDB00FBDB01B318FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9FE2A801B31801B31801B31801B31801B31801B31861D06FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000064006600690073006800000000006500730061006C00610064000000800066006400750063006B0000000000000000006400790075000000000065007300680061006C0061000000800066006B0061006F0079006100000000000000C0800000000002000A000A00C8002C01000000005400650073007400000001000250000000000A000A00E6000E000100FFFF820043006F006E00740069006E00750065003A0000000000000001500000000042008600A1000D000200FFFF800026004F004B00000000000000000011000300E70300000D0044004C04000082001200BC01000011005800A40000000D0048002E16000082001200BC010000 + Relocations: + - VirtualAddress: 528 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 544 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 560 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 576 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 592 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 608 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 624 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 640 + 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/combined-resources.test =================================================================== --- lld/trunk/test/COFF/combined-resources.test +++ lld/trunk/test/COFF/combined-resources.test @@ -4,6 +4,10 @@ // > rc /fo combined-resources.res /nologo combined-resources.rc // > rc /fo combined-resources-2.res /nologo combined-resources-2.rc +// The object files were generated with GNU windres and MS cvtres.exe, +// > x86_64-w64-mingw32-windres combined-resources.res combined-resources.o +// > cvtres -machine:x64 -out:combined-resources-2.o combined-resources.res + # RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj # RUN: lld-link /out:%t.exe /entry:main %t.obj %p/Inputs/resource.res \ # RUN: %p/Inputs/combined-resources.res %p/Inputs/combined-resources-2.res @@ -11,6 +15,21 @@ # RUN: llvm-readobj --coff-resources --file-headers --section-data %t.exe | \ # RUN: FileCheck %s +# RUN: yaml2obj < %p/Inputs/combined-resources.yaml > %t-combined-resources.o +# RUN: yaml2obj < %p/Inputs/combined-resources-2.yaml > %t-combined-resources-2.o + +# RUN: lld-link /lldmingw /out:%t-resobj.exe /entry:main %t.obj %p/Inputs/resource.res \ +# RUN: %t-combined-resources.o %t-combined-resources-2.o + +// As input resources are traversed in a slightly different order, the +// RVAs of data blobs will end up slightly different, even if they are +// equivalent. Filter out such addresses from llvm-readobj's output, +// and compare the rest to make sure it is equivalent. + +# RUN: llvm-readobj --coff-resources %t.exe | sed -E 's/(RVA|Address|File): .*//' > %t-orig.txt +# RUN: llvm-readobj --coff-resources %t-resobj.exe | sed -E 's/(RVA|Address|File): .*//' > %t-resobj.txt +# RUN: cmp %t-orig.txt %t-resobj.txt + CHECK: ResourceTableRVA: 0x2000 CHECK-NEXT: ResourceTableSize: 0xC20 CHECK-DAG: Resources [ Index: lld/trunk/test/COFF/force-multipleres.test =================================================================== --- lld/trunk/test/COFF/force-multipleres.test +++ lld/trunk/test/COFF/force-multipleres.test @@ -9,13 +9,21 @@ 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: cp %S/Inputs/id.res.o %t.dir/id1.o +RUN: cp %S/Inputs/id.res.o %t.dir/id2.o RUN: not lld-link /machine:x64 /nodefaultlib /noentry /dll %t.dir/id1.res %t.dir/id2.res 2>&1 | \ RUN: FileCheck -check-prefix=ERR %s -ERR: error: duplicate resource: type STRINGTABLE (ID 6)/name ID 3/language 1033, in {{.*}}id1.res and in {{.*}}id2.res +RUN: not lld-link /lldmingw /machine:x64 /nodefaultlib /noentry /dll %t.dir/id1.res %t.dir/id2.o 2>&1 | \ +RUN: FileCheck -check-prefix=ERR %s +RUN: not lld-link /lldmingw /machine:x64 /nodefaultlib /noentry /dll %t.dir/id1.o %t.dir/id2.o 2>&1 | \ +RUN: FileCheck -check-prefix=ERR %s +ERR: error: duplicate resource: type STRINGTABLE (ID 6)/name ID 3/language 1033, in {{.*}}id1.{{res|o}} and in {{.*}}id2.{{res|o}} RUN: lld-link /force /machine:x64 /nodefaultlib /noentry /dll %t.dir/id1.res %t.dir/id2.res 2>&1 | \ RUN: FileCheck -check-prefix=WARN %s RUN: lld-link /force:multipleres /machine:x64 /nodefaultlib /noentry /dll %t.dir/id1.res %t.dir/id2.res 2>&1 | \ RUN: FileCheck -check-prefix=WARN %s -WARN: warning: duplicate resource: type STRINGTABLE (ID 6)/name ID 3/language 1033, in {{.*}}id1.res and in {{.*}}id2.res +RUN: lld-link /lldmingw /force:multipleres /machine:x64 /nodefaultlib /noentry /dll %t.dir/id1.o %t.dir/id2.o 2>&1 | \ +RUN: FileCheck -check-prefix=WARN %s +WARN: warning: duplicate resource: type STRINGTABLE (ID 6)/name ID 3/language 1033, in {{.*}}id1.{{res|o}} and in {{.*}}id2.{{res|o}} Index: lld/trunk/test/COFF/mixed-resource-obj.yaml =================================================================== --- lld/trunk/test/COFF/mixed-resource-obj.yaml +++ lld/trunk/test/COFF/mixed-resource-obj.yaml @@ -0,0 +1,61 @@ +# RUN: yaml2obj < %s > %t.o + +# Test that we get both the resource and the code from a single object +# file that contains both, while merging resources from another object +# file. + +# RUN: lld-link -lldmingw -out:%t.exe %t.o %p/Inputs/id.res.o -entry:main +# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s --check-prefix=CHECK-RESOURCES +# RUN: llvm-objdump -d %t.exe | FileCheck %s --check-prefix=CHECK-DISASM + +# CHECK-RESOURCES: Resources [ +# CHECK-RESOURCES-NEXT: Total Number of Resources: 2 + +# CHECK-DISASM: Disassembly of section .text: +# CHECK-DISASM: .text: +# CHECK-DISASM-NEXT: movl $42, %eax +# CHECK-DISASM-NEXT: retq + +--- !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: 0000000000000000000000000000010005000000180000800000000000000000000000000100000048000080300000800000000000000000000000000000010009040000600000000A0054004500530054004400490041004C004F0047000000700000006C00000000000000000000000000C0800000000002000A000A00C8002C01000000005400650073007400000001000250000000000A000A00E6000E000100FFFF820043006F006E00740069006E00750065003A0000000000000001500000000042008600A1000D000200FFFF800026004F004B000000000000000000 + Relocations: + - VirtualAddress: 96 + SymbolName: .rsrc + Type: IMAGE_REL_AMD64_ADDR32NB + - Name: '.text' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: B82A000000C3 +symbols: + - Name: .rsrc + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '.text' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: main + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/trunk/include/llvm/Object/WindowsResource.h =================================================================== --- llvm/trunk/include/llvm/Object/WindowsResource.h +++ llvm/trunk/include/llvm/Object/WindowsResource.h @@ -31,6 +31,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" #include "llvm/Object/Error.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryStreamReader.h" @@ -48,6 +49,7 @@ namespace object { class WindowsResource; +class ResourceSectionRef; const size_t WIN_RES_MAGIC_SIZE = 16; const size_t WIN_RES_NULL_ENTRY_SIZE = 16; @@ -153,6 +155,8 @@ class TreeNode; WindowsResourceParser(); Error parse(WindowsResource *WR, std::vector &Duplicates); + Error parse(ResourceSectionRef &RSR, StringRef Filename, + std::vector &Duplicates); void printTree(raw_ostream &OS) const; const TreeNode &getTree() const { return Root; } const ArrayRef> getData() const { return Data; } @@ -227,7 +231,21 @@ uint32_t Origin; }; + struct StringOrID { + bool IsString; + ArrayRef String; + uint32_t ID; + + StringOrID(uint32_t ID) : IsString(false), ID(ID) {} + StringOrID(ArrayRef String) : IsString(true), String(String) {} + }; + private: + Error addChildren(TreeNode &Node, ResourceSectionRef &RSR, + const coff_resource_dir_table &Table, uint32_t Origin, + std::vector &Context, + std::vector &Duplicates); + TreeNode Root; std::vector> Data; std::vector> StringTable; Index: llvm/trunk/lib/Object/WindowsResource.cpp =================================================================== --- llvm/trunk/lib/Object/WindowsResource.cpp +++ llvm/trunk/lib/Object/WindowsResource.cpp @@ -30,6 +30,18 @@ if (auto EC = X) \ return EC; +#define UNWRAP_REF_OR_RETURN(Name, Expr) \ + auto Name##OrErr = Expr; \ + if (!Name##OrErr) \ + return Name##OrErr.takeError(); \ + const auto &Name = *Name##OrErr; + +#define UNWRAP_OR_RETURN(Name, Expr) \ + auto Name##OrErr = Expr; \ + if (!Name##OrErr) \ + return Name##OrErr.takeError(); \ + auto Name = *Name##OrErr; + const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); // COFF files seem to be inconsistent with alignment between sections, just use @@ -197,6 +209,48 @@ return OS.str(); } +static void printStringOrID(const WindowsResourceParser::StringOrID &S, + raw_string_ostream &OS, bool IsType, bool IsID) { + if (S.IsString) { + std::string UTF8; + if (!convertUTF16LEToUTF8String(S.String, UTF8)) + UTF8 = "(failed conversion from UTF16)"; + OS << '\"' << UTF8 << '\"'; + } else if (IsType) + printResourceTypeName(S.ID, OS); + else if (IsID) + OS << "ID " << S.ID; + else + OS << S.ID; +} + +static std::string makeDuplicateResourceError( + const std::vector &Context, + StringRef File1, StringRef File2) { + std::string Ret; + raw_string_ostream OS(Ret); + + OS << "duplicate resource:"; + + if (Context.size() >= 1) { + OS << " type "; + printStringOrID(Context[0], OS, /* IsType */ true, /* IsID */ true); + } + + if (Context.size() >= 2) { + OS << "/name "; + printStringOrID(Context[1], OS, /* IsType */ false, /* IsID */ true); + } + + if (Context.size() >= 3) { + OS << "/language "; + printStringOrID(Context[2], OS, /* IsType */ false, /* IsID */ false); + } + OS << ", in " << File1 << " and in " << File2; + + return OS.str(); +} + Error WindowsResourceParser::parse(WindowsResource *WR, std::vector &Duplicates) { auto EntryOrErr = WR->getHeadEntry(); @@ -234,6 +288,15 @@ return Error::success(); } +Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename, + std::vector &Duplicates) { + UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable()); + uint32_t Origin = InputFilenames.size(); + InputFilenames.push_back(Filename); + std::vector Context; + return addChildren(Root, RSR, BaseTable, Origin, Context, Duplicates); +} + void WindowsResourceParser::printTree(raw_ostream &OS) const { ScopedPrinter Writer(OS); Root.print(Writer, "Resource Tree"); @@ -248,6 +311,67 @@ return NameNode.addLanguageNode(Entry, Origin, Data, Result); } +Error WindowsResourceParser::addChildren(TreeNode &Node, + ResourceSectionRef &RSR, + const coff_resource_dir_table &Table, + uint32_t Origin, + std::vector &Context, + std::vector &Duplicates) { + + for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; + i++) { + UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i)); + TreeNode *Child; + + if (Entry.Offset.isSubDir()) { + + // Create a new subdirectory and recurse + if (i < Table.NumberOfNameEntries) { + UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry)); + Child = &Node.addNameChild(NameString, StringTable); + Context.push_back(StringOrID(NameString)); + } else { + Child = &Node.addIDChild(Entry.Identifier.ID); + Context.push_back(StringOrID(Entry.Identifier.ID)); + } + + UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry)); + Error E = + addChildren(*Child, RSR, NextTable, Origin, Context, Duplicates); + if (E) + return E; + Context.pop_back(); + + } else { + + // Data leaves are supposed to have a numeric ID as identifier (language). + if (Table.NumberOfNameEntries > 0) + return createStringError(object_error::parse_failed, + "unexpected string key for data object"); + + // Try adding a data leaf + UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); + 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); + if (Added) { + UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); + Data.push_back(ArrayRef( + reinterpret_cast(Contents.data()), + Contents.size())); + } else { + Duplicates.push_back(makeDuplicateResourceError( + Context, InputFilenames[Child->Origin], InputFilenames.back())); + } + Context.pop_back(); + + } + } + return Error::success(); +} + WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex) : StringIndex(StringIndex) {}