Index: lld/COFF/DriverUtils.cpp =================================================================== --- lld/COFF/DriverUtils.cpp +++ lld/COFF/DriverUtils.cpp @@ -697,6 +697,7 @@ } // 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) { object::WindowsResourceParser Parser; Index: lld/COFF/InputFiles.h =================================================================== --- lld/COFF/InputFiles.h +++ lld/COFF/InputFiles.h @@ -162,10 +162,13 @@ // precompiled object. Any difference indicates out-of-date objects. llvm::Optional PCHSignature; - // Tells whether this file was compiled with /hotpatch + // Whether this is an object file created from .res files. + bool IsResourceObjFile = false; + + // Whether this file was compiled with /hotpatch. bool HotPatchable = false; - // Whether the object was already merged into the final PDB or not + // Whether the object was already merged into the final PDB. bool MergedIntoPDB = false; // If the OBJ has a .debug$T stream, this tells how it will be handled. Index: lld/COFF/InputFiles.cpp =================================================================== --- lld/COFF/InputFiles.cpp +++ lld/COFF/InputFiles.cpp @@ -206,6 +206,10 @@ 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()) @@ -667,7 +671,8 @@ // types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu. // And PDB type servers, when compiling with /Zi. This function extracts these // dependencies and makes them available as a TpiSource interface (see -// DebugTypes.h). +// DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular +// output even with /Yc and /Yu and with /Zi. void ObjFile::initializeDependencies() { if (!Config->Debug) return; Index: lld/COFF/SymbolTable.h =================================================================== --- lld/COFF/SymbolTable.h +++ lld/COFF/SymbolTable.h @@ -119,6 +119,8 @@ llvm::DenseMap SymMap; std::unique_ptr LTO; + + ObjFile *ResourceObjFile = nullptr; }; extern SymbolTable *Symtab; Index: lld/COFF/SymbolTable.cpp =================================================================== --- lld/COFF/SymbolTable.cpp +++ lld/COFF/SymbolTable.cpp @@ -44,6 +44,15 @@ } if (auto *F = dyn_cast(File)) { + if (F->IsResourceObjFile) { + if (!ResourceObjFile) + ResourceObjFile = F; + else + error(toString(File) + + ": more than one resource obj file not allowed, already got " + + toString(ResourceObjFile)); + } + ObjFile::Instances.push_back(F); } else if (auto *F = dyn_cast(File)) { BitcodeFile::Instances.push_back(F); Index: lld/test/CMakeLists.txt =================================================================== --- lld/test/CMakeLists.txt +++ lld/test/CMakeLists.txt @@ -34,8 +34,8 @@ set(LLD_TEST_DEPS lld) if (NOT LLD_BUILT_STANDALONE) list(APPEND LLD_TEST_DEPS - FileCheck count llc llvm-ar llvm-as llvm-bcanalyzer llvm-config llvm-dis - llvm-dwarfdump llvm-lib llvm-mc llvm-nm llvm-objcopy llvm-objdump + FileCheck count llc llvm-ar llvm-as llvm-bcanalyzer llvm-config llvm-cvtres + llvm-dis llvm-dwarfdump llvm-lib llvm-mc llvm-nm llvm-objcopy llvm-objdump llvm-pdbutil llvm-readelf llvm-readobj not obj2yaml opt yaml2obj ) endif() Index: lld/test/COFF/multiple-resource-objs.test =================================================================== --- /dev/null +++ lld/test/COFF/multiple-resource-objs.test @@ -0,0 +1,11 @@ +# RUN: llvm-cvtres /out:%t_resource.obj %S/Inputs/resource.res +# RUN: llvm-cvtres /out:%t_id.obj %S/Inputs/id.res +# RUN: not lld-link /out:%t.exe /dll /noentry %t_id.obj %t_resource.obj 2>&1 | \ +# RUN: FileCheck --check-prefix=TWOOBJ %s + +TWOOBJ: error: {{.*}}_resource.obj: more than one resource obj file not allowed, already got {{.*}}_id.obj + +# RUN: not lld-link /out:%t.exe /dll /noentry %S/Inputs/id.res %t_resource.obj 2>&1 | \ +# RUN: FileCheck --check-prefix=OBJRES %s + +OBJRES: error: internal .obj file created from .res files: more than one resource obj file not allowed, already got {{.*}}_resource.obj Index: llvm/lib/Object/WindowsResource.cpp =================================================================== --- llvm/lib/Object/WindowsResource.cpp +++ llvm/lib/Object/WindowsResource.cpp @@ -442,7 +442,8 @@ Data(Parser.getData()), StringTable(Parser.getStringTable()) { performFileLayout(); - OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(FileSize); + OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( + FileSize, "internal .obj file created from .res files"); } void WindowsResourceCOFFWriter::performFileLayout() { @@ -527,7 +528,7 @@ Header->NumberOfSections = 2; Header->TimeDateStamp = getTime(); Header->PointerToSymbolTable = SymbolTableOffset; - // One symbol for every resource plus 2 for each section and @feat.00 + // One symbol for every resource plus 2 for each section and 1 for @feat.00 Header->NumberOfSymbols = Data.size() + 5; Header->SizeOfOptionalHeader = 0; Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; Index: llvm/utils/gn/secondary/lld/test/BUILD.gn =================================================================== --- llvm/utils/gn/secondary/lld/test/BUILD.gn +++ llvm/utils/gn/secondary/lld/test/BUILD.gn @@ -82,6 +82,7 @@ "//llvm/tools/llvm-ar:symlinks", "//llvm/tools/llvm-as", "//llvm/tools/llvm-bcanalyzer", + "//llvm/tools/llvm-cvtres", "//llvm/tools/llvm-dis", "//llvm/tools/llvm-dwarfdump", "//llvm/tools/llvm-mc",