[lld] r352590 - lld/coff: Implement some support for the comdat selection field
Nico Weber via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 29 18:17:27 PST 2019
Author: nico
Date: Tue Jan 29 18:17:27 2019
New Revision: 352590
URL: http://llvm.org/viewvc/llvm-project?rev=352590&view=rev
Log:
lld/coff: Implement some support for the comdat selection field
LLD used to handle comdats as if the selection field was always set to
IMAGE_COMDAT_SELECT_ANY. This means for obj files produced by `cl /Gy`, LLD
would never report a duplicate symbol error.
This change:
- adds validation for the Selection field (should make no difference in
practice for compiler-generated obj inputs)
- rejects comdats that have different Selection fields in different obj files
(likewise). This is a bit more strict but also more self-consistent thank
link.exe (see comment in code)
- implements handling for all the selection kinds
In practice, compilers only generate comdats with
IMAGE_COMDAT_SELECT_NODUPLICATES (LLD now produces duplicate symbol errors for
these), IMAGE_COMDAT_SELECT_ANY (no behavior change), and
IMAGE_COMDAT_SELECT_LARGEST (for RTTI data; here LLD should no longer create
broken executables when linking some TUs with RTTI enabled and some with it
disabled – but see below).
The implementation of `IMAGE_COMDAT_SELECT_LARGEST` is incomplete: If one
SELECT_LARGEST comdat replaces an earlier one, the comdat symbol is replaced
correctly, but the old section stays loaded and if /opt:ref is disabled (via
/opt:noref or /debug) it's still written to the output. That's not ideal, but
better than the current treatment of just picking any one of those comdats. I
hope to fix this better later.
Fixes most of PR40094.
Differential Revision: https://reviews.llvm.org/D57324
Added:
lld/trunk/test/COFF/comdat-selection-associative-largest.s
lld/trunk/test/COFF/comdat-selection.s
Modified:
lld/trunk/COFF/InputFiles.cpp
lld/trunk/COFF/InputFiles.h
lld/trunk/COFF/SymbolTable.cpp
lld/trunk/COFF/SymbolTable.h
Modified: lld/trunk/COFF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.cpp?rev=352590&r1=352589&r2=352590&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.cpp (original)
+++ lld/trunk/COFF/InputFiles.cpp Tue Jan 29 18:17:27 2019
@@ -126,6 +126,13 @@ void ObjFile::parse() {
initializeSymbols();
}
+const coff_section* ObjFile::getSection(uint32_t I) {
+ const coff_section *Sec;
+ if (auto EC = COFFObj->getSection(I, Sec))
+ fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
+ return Sec;
+}
+
// We set SectionChunk pointers in the SparseChunks vector to this value
// temporarily to mark comdat sections as having an unknown resolution. As we
// walk the object file's symbol table, once we visit either a leader symbol or
@@ -139,10 +146,7 @@ void ObjFile::initializeChunks() {
Chunks.reserve(NumSections);
SparseChunks.resize(NumSections + 1);
for (uint32_t I = 1; I < NumSections + 1; ++I) {
- const coff_section *Sec;
- if (auto EC = COFFObj->getSection(I, Sec))
- fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
-
+ const coff_section *Sec = getSection(I);
if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
SparseChunks[I] = PendingComdat;
else
@@ -153,9 +157,7 @@ void ObjFile::initializeChunks() {
SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
const coff_aux_section_definition *Def,
StringRef LeaderName) {
- const coff_section *Sec;
- if (auto EC = COFFObj->getSection(SectionNumber, Sec))
- fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
+ const coff_section *Sec = getSection(SectionNumber);
StringRef Name;
if (auto EC = COFFObj->getSectionName(Sec, Name))
@@ -231,8 +233,7 @@ void ObjFile::readAssociativeDefinition(
StringRef Name, ParentName;
COFFObj->getSymbolName(Sym, Name);
- const coff_section *ParentSec;
- COFFObj->getSection(ParentIndex, ParentSec);
+ const coff_section *ParentSec = getSection(ParentIndex);
COFFObj->getSectionName(ParentSec, ParentName);
error(toString(this) + ": associative comdat " + Name + " (sec " +
Twine(SectionNumber) + ") has invalid reference to section " +
@@ -429,10 +430,21 @@ Optional<Symbol *> ObjFile::createDefine
fatal(toString(this) + ": " + GetName() +
" should not refer to non-existent section " + Twine(SectionNumber));
- // Handle comdat leader symbols.
+ // Comdat handling.
+ // A comdat symbol consists of two symbol table entries.
+ // The first symbol entry has the name of the section (e.g. .text), fixed
+ // values for the other fields, and one auxilliary record.
+ // The second symbol entry has the name of the comdat symbol, called the
+ // "comdat leader".
+ // When this function is called for the first symbol entry of a comdat,
+ // it sets ComdatDefs and returns None, and when it's called for the second
+ // symbol entry it reads ComdatDefs and then sets it back to nullptr.
+
+ // Handle comdat leader.
if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
ComdatDefs[SectionNumber] = nullptr;
- Symbol *Leader;
+ DefinedRegular *Leader;
+
if (Sym.isExternal()) {
std::tie(Leader, Prevailing) =
Symtab->addComdat(this, GetName(), Sym.getGeneric());
@@ -442,10 +454,111 @@ Optional<Symbol *> ObjFile::createDefine
Prevailing = true;
}
+ if (Def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES ||
+ // Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe
+ // doesn't understand IMAGE_COMDAT_SELECT_NEWEST either.
+ Def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) {
+ fatal("unknown comdat type " + std::to_string((int)Def->Selection) +
+ " for " + GetName() + " in " + toString(this));
+ }
+ COMDATType Selection = (COMDATType)Def->Selection;
+
+ if (!Prevailing && Leader->isCOMDAT()) {
+ // There's already an existing comdat for this symbol: `Leader`.
+ // Use the comdats's selection field to determine if the new
+ // symbol in `Sym` should be discarded, produce a duplicate symbol
+ // error, etc.
+
+ SectionChunk *LeaderChunk = nullptr;
+ COMDATType LeaderSelection = IMAGE_COMDAT_SELECT_ANY;
+
+ if (Leader->Data) {
+ LeaderChunk = Leader->getChunk();
+ LeaderSelection = LeaderChunk->Selection;
+ } else {
+ // FIXME: comdats from LTO files don't know their selection; treat them
+ // as "any".
+ Selection = LeaderSelection;
+ }
+
+ // Requiring selections to match exactly is a bit more strict than
+ // link.exe which allows merging "any" and "largest" if "any" is the first
+ // symbol the linker sees, and it allows merging "largest" with everything
+ // (!) if "largest" is the first symbol the linker sees. Making this
+ // symmetric independent of which selection is seen first seems better
+ // though, and if we can get away with not allowing merging "any" and
+ // "largest" that keeps things more regular too.
+ // (ModuleLinker::getComdatResult() also does comdat type merging in a
+ // different way and it's also a bit more permissive.)
+ if (Selection != LeaderSelection) {
+ std::string Msg = ("conflicting comdat type for " + toString(*Leader) +
+ ": " + Twine((int)LeaderSelection) + " in " +
+ toString(Leader->getFile()) + " and " +
+ Twine((int)Selection) + " in " + toString(this))
+ .str();
+ if (Config->ForceMultiple)
+ warn(Msg);
+ else
+ error(Msg);
+ }
+
+ switch (Selection) {
+ case IMAGE_COMDAT_SELECT_NODUPLICATES:
+ Symtab->reportDuplicate(Leader, this);
+ break;
+
+ case IMAGE_COMDAT_SELECT_ANY:
+ // Nothing to do.
+ break;
+
+ case IMAGE_COMDAT_SELECT_SAME_SIZE:
+ if (LeaderChunk->getSize() != getSection(SectionNumber)->SizeOfRawData)
+ Symtab->reportDuplicate(Leader, this);
+ break;
+
+ case IMAGE_COMDAT_SELECT_EXACT_MATCH: {
+ SectionChunk NewChunk(this, getSection(SectionNumber));
+ // link.exe only compares section contents here and doesn't complain
+ // if the two comdat sections have e.g. different alignment.
+ // Match that.
+ if (LeaderChunk->getContents() != NewChunk.getContents())
+ Symtab->reportDuplicate(Leader, this);
+ break;
+ }
+
+ case IMAGE_COMDAT_SELECT_ASSOCIATIVE:
+ // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE.
+ // (This means lld-link doesn't produce duplicate symbol errors for
+ // associative comdats while link.exe does, but associate comdats
+ // are never extern in practice.)
+ llvm_unreachable("createDefined not called for associative comdats");
+
+ case IMAGE_COMDAT_SELECT_LARGEST:
+ if (LeaderChunk->getSize() < getSection(SectionNumber)->SizeOfRawData) {
+ // Replace the existing comdat symbol with the new one.
+ // FIXME: This is incorrect: With /opt:noref, the previous sections
+ // make it into the final executable as well. Correct handling would
+ // be to undo reading of the whole old section that's being replaced,
+ // or doing one pass that determines what the final largest comdat
+ // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading
+ // only the largest one.
+ replaceSymbol<DefinedRegular>(
+ Leader, this, GetName(), /*IsCOMDAT*/ true,
+ /*IsExternal*/ true, Sym.getGeneric(), nullptr);
+ Prevailing = true;
+ }
+ break;
+
+ case IMAGE_COMDAT_SELECT_NEWEST:
+ llvm_unreachable("should have been rejected earlier");
+ }
+ }
+
if (Prevailing) {
SectionChunk *C = readSection(SectionNumber, Def, GetName());
SparseChunks[SectionNumber] = C;
C->Sym = cast<DefinedRegular>(Leader);
+ C->Selection = Selection;
cast<DefinedRegular>(Leader)->Data = &C->Repl;
} else {
SparseChunks[SectionNumber] = nullptr;
@@ -534,6 +647,8 @@ void BitcodeFile::parse() {
MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
std::vector<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size());
for (size_t I = 0; I != Obj->getComdatTable().size(); ++I)
+ // FIXME: lto::InputFile doesn't keep enough data to do correct comdat
+ // selection handling.
Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I]));
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
StringRef SymName = Saver.save(ObjSym.getName());
Modified: lld/trunk/COFF/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.h?rev=352590&r1=352589&r2=352590&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.h (original)
+++ lld/trunk/COFF/InputFiles.h Tue Jan 29 18:17:27 2019
@@ -156,6 +156,8 @@ public:
llvm::Optional<uint32_t> PCHSignature;
private:
+ const coff_section* getSection(uint32_t I);
+
void initializeChunks();
void initializeSymbols();
Modified: lld/trunk/COFF/SymbolTable.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/SymbolTable.cpp?rev=352590&r1=352589&r2=352590&view=diff
==============================================================================
--- lld/trunk/COFF/SymbolTable.cpp (original)
+++ lld/trunk/COFF/SymbolTable.cpp Tue Jan 29 18:17:27 2019
@@ -399,7 +399,7 @@ Symbol *SymbolTable::addRegular(InputFil
return S;
}
-std::pair<Symbol *, bool>
+std::pair<DefinedRegular *, bool>
SymbolTable::addComdat(InputFile *F, StringRef N,
const coff_symbol_generic *Sym) {
Symbol *S;
@@ -408,11 +408,12 @@ SymbolTable::addComdat(InputFile *F, Str
if (WasInserted || !isa<DefinedRegular>(S)) {
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
/*IsExternal*/ true, Sym, nullptr);
- return {S, true};
+ return {cast<DefinedRegular>(S), true};
}
- if (!cast<DefinedRegular>(S)->isCOMDAT())
+ auto *ExistingSymbol = cast<DefinedRegular>(S);
+ if (!ExistingSymbol->isCOMDAT())
reportDuplicate(S, F);
- return {S, false};
+ return {ExistingSymbol, false};
}
Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
Modified: lld/trunk/COFF/SymbolTable.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/SymbolTable.h?rev=352590&r1=352589&r2=352590&view=diff
==============================================================================
--- lld/trunk/COFF/SymbolTable.h (original)
+++ lld/trunk/COFF/SymbolTable.h Tue Jan 29 18:17:27 2019
@@ -27,6 +27,7 @@ class Chunk;
class CommonChunk;
class Defined;
class DefinedAbsolute;
+class DefinedRegular;
class DefinedRelative;
class Lazy;
class SectionChunk;
@@ -88,7 +89,7 @@ public:
Symbol *addRegular(InputFile *F, StringRef N,
const llvm::object::coff_symbol_generic *S = nullptr,
SectionChunk *C = nullptr);
- std::pair<Symbol *, bool>
+ std::pair<DefinedRegular *, bool>
addComdat(InputFile *F, StringRef N,
const llvm::object::coff_symbol_generic *S = nullptr);
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
Added: lld/trunk/test/COFF/comdat-selection-associative-largest.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/comdat-selection-associative-largest.s?rev=352590&view=auto
==============================================================================
--- lld/trunk/test/COFF/comdat-selection-associative-largest.s (added)
+++ lld/trunk/test/COFF/comdat-selection-associative-largest.s Tue Jan 29 18:17:27 2019
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+
+# Tests handling of several comdats with "largest" selection type that each
+# has an associative comdat.
+
+# Create obj files.
+# RUN: sed -e s/TYPE/.byte/ -e s/SIZE/1/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.1.obj
+# RUN: sed -e s/TYPE/.short/ -e s/SIZE/2/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.2.obj
+# RUN: sed -e s/TYPE/.long/ -e s/SIZE/4/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.4.obj
+
+ .section .text$ac, "", associative, symbol
+assocsym:
+ .long SIZE
+
+ .section .text$nm, "", largest, symbol
+ .globl symbol
+symbol:
+ TYPE SIZE
+
+# Pass the obj files in different orders and check that only the associative
+# comdat of the largest obj file makes it into the output, independent of
+# the order of the obj files on the command line.
+
+# FIXME: Make these pass when /opt:noref is passed.
+
+# RUN: lld-link /include:symbol /dll /noentry /nodefaultlib %t.1.obj %t.2.obj %t.4.obj /out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck --check-prefix=ALL124 %s
+# ALL124: Contents of section .text:
+# ALL124: 180001000 04000000 04000000 ....
+
+# RUN: lld-link /include:symbol /dll /noentry /nodefaultlib %t.4.obj %t.2.obj %t.1.obj /out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck --check-prefix=ALL421 %s
+# ALL421: Contents of section .text:
+# ALL421: 180001000 04000000 04000000 ....
+
+# RUN: lld-link /include:symbol /dll /noentry /nodefaultlib %t.2.obj %t.4.obj %t.1.obj /out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck --check-prefix=ALL241 %s
+# ALL241: Contents of section .text:
+# ALL241: 180001000 04000000 04000000 ....
+
+# RUN: lld-link /include:symbol /dll /noentry /nodefaultlib %t.2.obj %t.1.obj /out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck --check-prefix=JUST21 %s
+# JUST21: Contents of section .text:
+# JUST21: 180001000 02000000 0200 ....
+
Added: lld/trunk/test/COFF/comdat-selection.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/comdat-selection.s?rev=352590&view=auto
==============================================================================
--- lld/trunk/test/COFF/comdat-selection.s (added)
+++ lld/trunk/test/COFF/comdat-selection.s Tue Jan 29 18:17:27 2019
@@ -0,0 +1,87 @@
+# REQUIRES: x86
+
+# Tests handling of the comdat selection type.
+# (Except associative which is tested in associative-comdat.s and
+# comdat-selection-associate-largest.s instead.)
+
+# Create obj files with each selection type.
+# RUN: sed -e s/SEL/discard/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.discard.obj
+# RUN: sed -e s/SEL/one_only/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.one_only.obj
+# RUN: sed -e s/SEL/same_size/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.same_size.obj
+# RUN: sed -e s/SEL/same_size/ -e s/.long/.short/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.same_size.short.obj
+# RUN: sed -e s/SEL/same_contents/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.same_contents.obj
+# RUN: sed -e s/SEL/same_contents/ -e s/.long/.short/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.same_contents.short.obj
+# RUN: sed -e s/SEL/same_contents/ -e s/1/2/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.same_contents.2.obj
+# RUN: sed -e s/SEL/largest/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.largest.obj
+# RUN: sed -e s/SEL/largest/ -e s/.long/.short/ -e s/1/2/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.largest.short.2.obj
+# RUN: sed -e s/SEL/newest/ %s | llvm-mc -triple x86_64-pc-win32 -filetype=obj -o %t.newest.obj
+
+ .section .text$nm, "", SEL, symbol
+ .globl symbol
+symbol:
+ .long 1
+
+# First, pass each selection type twice. All should link fine except for
+# one_only which should report a duplicate symbol error and newest which
+# link.exe (and hence lld-link) doesn't understand.
+
+# RUN: cp %t.discard.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.discard.obj %t.obj
+# RUN: cp %t.one_only.obj %t.obj && not lld-link /dll /noentry /nodefaultlib %t.one_only.obj %t.obj 2>&1 | FileCheck --check-prefix=ONEONE %s
+# ONEONE: lld-link: error: duplicate symbol: symbol
+# RUN: cp %t.same_size.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.same_size.obj %t.obj
+# RUN: cp %t.same_contents.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.same_contents.obj %t.obj
+# RUN: cp %t.largest.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.largest.obj %t.obj
+# RUN: cp %t.newest.obj %t.obj && not lld-link /dll /noentry /nodefaultlib %t.newest.obj %t.obj 2>&1 | FileCheck --check-prefix=NEWNEW %s
+# NEWNEW: lld-link: error: unknown comdat type 7 for symbol
+
+# /force doesn't affect errors about unknown comdat types.
+# RUN: cp %t.newest.obj %t.obj && not lld-link /force /dll /noentry /nodefaultlib %t.newest.obj %t.obj 2>&1 | FileCheck --check-prefix=NEWNEWFORCE %s
+# NEWNEWFORCE: lld-link: error: unknown comdat type 7 for symbol
+
+# Check that same_size, same_contents, largest do what they're supposed to.
+
+# Check that the "same_size" selection produces an error if passed two symbols
+# with different size.
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.same_size.obj %t.same_size.short.obj 2>&1 | FileCheck --check-prefix=SAMESIZEDUPE %s
+# SAMESIZEDUPE: lld-link: error: duplicate symbol: symbol
+
+# Check that the "same_contents" selection produces an error if passed two
+# symbols with different contents.
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.same_contents.obj %t.same_contents.2.obj 2>&1 | FileCheck --check-prefix=SAMECONTENTSDUPE1 %s
+# SAMECONTENTSDUPE1: lld-link: error: duplicate symbol: symbol
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.same_contents.obj %t.same_contents.2.obj 2>&1 | FileCheck --check-prefix=SAMECONTENTSDUPE2 %s
+# SAMECONTENTSDUPE2: lld-link: error: duplicate symbol: symbol
+
+# Check that the "largest" selection picks the larger comdat (independent of
+# the order the .obj files are passed on the commandline).
+# RUN: lld-link /opt:noref /include:symbol /dll /noentry /nodefaultlib %t.largest.obj %t.largest.short.2.obj /out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck --check-prefix=LARGEST1 %s
+# LARGEST1: Contents of section .text:
+# LARGEST1: 180001000 01000000 ....
+
+# FIXME: Make this pass when /opt:noref is passed.
+# RUN: lld-link /include:symbol /dll /noentry /nodefaultlib %t.largest.short.2.obj %t.largest.obj /out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck --check-prefix=LARGEST2 %s
+# LARGEST2: Contents of section .text:
+# LARGEST2: 180001000 01000000 ....
+
+
+# Test linking the same symbol with different comdat selection types.
+# link.exe generally rejects this, except for "largest" which is allowed to
+# combine with everything (https://bugs.llvm.org/show_bug.cgi?id=40094#c7).
+# lld-link rejects all comdat selection type mismatches. Spot-test just a few
+# combinations.
+
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.discard.obj %t.one_only.obj 2>&1 | FileCheck --check-prefix=DISCARDONE %s
+# DISCARDONE: lld-link: error: conflicting comdat type for symbol: 2 in
+# RUN: lld-link /force /dll /noentry /nodefaultlib %t.discard.obj %t.one_only.obj 2>&1 | FileCheck --check-prefix=DISCARDONEFORCE %s
+# DISCARDONEFORCE: lld-link: warning: conflicting comdat type for symbol: 2 in
+
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.same_contents.obj %t.same_size.obj 2>&1 | FileCheck --check-prefix=CONTENTSSIZE %s
+# CONTENTSSIZE: lld-link: error: conflicting comdat type for symbol: 4 in
+
+# These cases are accepted by link.exe but not by lld-link.
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.discard.obj %t.largest.obj 2>&1 | FileCheck --check-prefix=DISCARDLARGEST %s
+# DISCARDLARGEST: lld-link: error: conflicting comdat type for symbol: 2 in
+# RUN: not lld-link /dll /noentry /nodefaultlib %t.largest.obj %t.one_only.obj 2>&1 | FileCheck --check-prefix=LARGESTONE %s
+# LARGESTONE: lld-link: error: conflicting comdat type for symbol: 6 in
More information about the llvm-commits
mailing list