[LLD] [COFF] Support merging resource object files

Extend WindowsResourceParser to support using a ResourceSectionRef for
loading resources from an object file.

Only allow merging resource object files in mingw mode; keep the
existing error on multiple resource objects in link mode.

If there only is one resource object file and no .res resources,
don't parse and recreate the .rsrc section, but just link it in without
inspecting it. This allows users to produce any .rsrc section (outside
of what the parser supports), just like before. (I don't have a specific
need for this, but it reduces the risk of this new feature.)

Separate out the .rsrc section chunks in InputFiles.cpp, and only include
them in the list of section chunks to link if we've determined that there
only was one single resource object. (We need to keep other chunks from
those object files, as they can legitimately contain other sections as
well, in addition to .rsrc section chunks.)

Differential Revision: https://reviews.llvm.org/D66824

git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@370436 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 5c5a89b..312c161 100644
--- a/COFF/Driver.cpp
+++ b/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;
+void LinkerDriver::convertResources() {
+  std::vector<ObjFile *> resourceObjFiles;
+
   for (ObjFile *f : ObjFile::instances) {
-    if (!f->isResourceObjFile)
-      continue;
-
-    if (!resourceObjFile) {
-      resourceObjFile = f;
-      continue;
-    }
-
-    error(toString(f) +
-          ": more than one resource obj file not allowed, already got " +
-          toString(resourceObjFile));
+    if (f->isResourceObjFile())
+      resourceObjFiles.push_back(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(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<ObjFile>(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<ObjFile>(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) {
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 01bfb02..42aaea3 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -98,6 +98,10 @@
   // Library search path. The first element is always "" (current directory).
   std::vector<StringRef> 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<MemoryBufferRef> mbs);
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+                                 ArrayRef<ObjFile *> objs);
 
 void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
 
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 49aff05..ab96e22 100644
--- a/COFF/DriverUtils.cpp
+++ b/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<MemoryBufferRef> mbs) {
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+                                 ArrayRef<ObjFile *> objs) {
   object::WindowsResourceParser parser;
 
+  std::vector<std::string> duplicates;
   for (MemoryBufferRef mb : mbs) {
     std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
     object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
     if (!rf)
       fatal("cannot compile non-resource file as resource");
 
-    std::vector<std::string> duplicates;
     if (auto ec = parser.parse(rf, duplicates))
       fatal(toString(std::move(ec)));
-
-    for (const auto &dupeDiag : duplicates)
-      if (config->forceMultipleRes)
-        warn(dupeDiag);
-      else
-        error(dupeDiag);
   }
 
+  // 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)));
+
+    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<std::unique_ptr<MemoryBuffer>> e =
       llvm::object::writeWindowsResourceCOFF(config->machine, parser,
                                              config->timestamp);
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index d02fedf..c24b59d 100644
--- a/COFF/InputFiles.cpp
+++ b/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()));
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 958d820..d822685 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -134,6 +134,10 @@
     return symbols.size() - 1;
   }
 
+  void includeResourceChunks();
+
+  bool isResourceObjFile() const { return !resourceChunks.empty(); }
+
   static std::vector<ObjFile *> 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<uint32_t> 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<Chunk *> chunks;
 
+  std::vector<SectionChunk *> resourceChunks;
+
   // CodeView debug info sections.
   std::vector<SectionChunk *> debugChunks;
 
diff --git a/test/COFF/Inputs/combined-resources-2.yaml b/test/COFF/Inputs/combined-resources-2.yaml
new file mode 100644
index 0000000..b0cc981
--- /dev/null
+++ b/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
+...
diff --git a/test/COFF/Inputs/combined-resources.yaml b/test/COFF/Inputs/combined-resources.yaml
new file mode 100644
index 0000000..870c825
--- /dev/null
+++ b/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
+...
diff --git a/test/COFF/combined-resources.test b/test/COFF/combined-resources.test
index 04cb45c..e14c819 100644
--- a/test/COFF/combined-resources.test
+++ b/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 [
diff --git a/test/COFF/force-multipleres.test b/test/COFF/force-multipleres.test
index 0932968..944f63f 100644
--- a/test/COFF/force-multipleres.test
+++ b/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}}
diff --git a/test/COFF/mixed-resource-obj.yaml b/test/COFF/mixed-resource-obj.yaml
new file mode 100644
index 0000000..60f0ebe
--- /dev/null
+++ b/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
+...