summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNico Rieck <nico.rieck@gmail.com>2013-07-06 12:13:10 +0000
committerNico Rieck <nico.rieck@gmail.com>2013-07-06 12:13:10 +0000
commit80646283796b20c6a1b7d8eb69ce6f0478d54383 (patch)
tree83b9be6b71faffa6cd481cea7736a172d36609ea
parent2be430d251a781e76634e945d56224a7a0ef5a39 (diff)
downloadllvm-80646283796b20c6a1b7d8eb69ce6f0478d54383.tar.gz
llvm-80646283796b20c6a1b7d8eb69ce6f0478d54383.tar.bz2
llvm-80646283796b20c6a1b7d8eb69ce6f0478d54383.tar.xz
MC: Implement COFF .linkonce directive
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@185753 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--docs/Extensions.rst56
-rw-r--r--include/llvm/MC/MCContext.h13
-rw-r--r--include/llvm/MC/MCSectionCOFF.h26
-rw-r--r--lib/CodeGen/TargetLoweringObjectFileImpl.cpp6
-rw-r--r--lib/MC/MCContext.cpp18
-rw-r--r--lib/MC/MCParser/COFFAsmParser.cpp60
-rw-r--r--lib/MC/MCSectionCOFF.cpp24
-rw-r--r--lib/MC/WinCOFFObjectWriter.cpp31
-rw-r--r--lib/MC/WinCOFFStreamer.cpp2
-rw-r--r--test/MC/COFF/linkonce-invalid.s40
-rw-r--r--test/MC/COFF/linkonce.s179
11 files changed, 426 insertions, 29 deletions
diff --git a/docs/Extensions.rst b/docs/Extensions.rst
index 062804a9fc..78ff874abf 100644
--- a/docs/Extensions.rst
+++ b/docs/Extensions.rst
@@ -4,7 +4,6 @@ LLVM Extensions
.. contents::
:local:
- :depth: 1
.. toctree::
:hidden:
@@ -21,6 +20,9 @@ Machine-specific Assembly Syntax
X86/COFF-Dependent
------------------
+Relocations
+^^^^^^^^^^^
+
The following additional relocation type is supported:
**@IMGREL** (AT&T syntax only) generates an image-relative relocation that
@@ -37,3 +39,55 @@ corresponds to the COFF relocation types ``IMAGE_REL_I386_DIR32NB`` (32-bit) or
.long fun@IMGREL
.long (fun@imgrel + 0x3F)
.long $unwind$fun@imgrel
+
+
+``.linkonce`` Directive
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+
+ ``.linkonce [ comdat type [ section identifier ] ]``
+
+Supported COMDAT types:
+
+``discard``
+ Discards duplicate sections with the same COMDAT symbol. This is the default
+ if no type is specified.
+
+``one_only``
+ If the symbol is defined multiple times, the linker issues an error.
+
+``same_size``
+ Duplicates are discarded, but the linker issues an error if any have
+ different sizes.
+
+``same_contents``
+ Duplicates are discarded, but the linker issues an error if any duplicates
+ do not have exactly the same content.
+
+``associative``
+ Links the section if a certain other COMDAT section is linked. This other
+ section is indicated by its section identifier following the comdat type.
+ The following restrictions apply to the associated section:
+
+ 1. It must be the name of a section already defined.
+ 2. It must differ from the current section.
+ 3. It must be a COMDAT section.
+ 4. It cannot be another associative COMDAT section.
+
+``largest``
+ Links the largest section from among the duplicates.
+
+``newest``
+ Links the newest section from among the duplicates.
+
+
+.. code-block:: gas
+
+ .section .text$foo
+ .linkonce
+ ...
+
+ .section .xdata$foo
+ .linkonce associative .text$foo
+ ...
diff --git a/include/llvm/MC/MCContext.h b/include/llvm/MC/MCContext.h
index 1ebc452f07..c012ed0f5a 100644
--- a/include/llvm/MC/MCContext.h
+++ b/include/llvm/MC/MCContext.h
@@ -38,6 +38,7 @@ namespace llvm {
class Twine;
class MCSectionMachO;
class MCSectionELF;
+ class MCSectionCOFF;
/// MCContext - Context object for machine code objects. This class owns all
/// of the sections that it creates.
@@ -255,14 +256,12 @@ namespace llvm {
const MCSectionELF *CreateELFGroupSection();
- const MCSection *getCOFFSection(StringRef Section, unsigned Characteristics,
- int Selection, SectionKind Kind);
-
- const MCSection *getCOFFSection(StringRef Section, unsigned Characteristics,
- SectionKind Kind) {
- return getCOFFSection (Section, Characteristics, 0, Kind);
- }
+ const MCSectionCOFF *getCOFFSection(StringRef Section,
+ unsigned Characteristics,
+ SectionKind Kind, int Selection = 0,
+ const MCSectionCOFF *Assoc = 0);
+ const MCSectionCOFF *getCOFFSection(StringRef Section);
/// @}
diff --git a/include/llvm/MC/MCSectionCOFF.h b/include/llvm/MC/MCSectionCOFF.h
index 50e33a5b04..754e8290ef 100644
--- a/include/llvm/MC/MCSectionCOFF.h
+++ b/include/llvm/MC/MCSectionCOFF.h
@@ -25,22 +25,33 @@ namespace llvm {
// The memory for this string is stored in the same MCContext as *this.
StringRef SectionName;
+ // FIXME: The following fields should not be mutable, but are for now so
+ // the asm parser can honor the .linkonce directive.
+
/// Characteristics - This is the Characteristics field of a section,
- // drawn from the enums below.
- unsigned Characteristics;
+ /// drawn from the enums below.
+ mutable unsigned Characteristics;
/// Selection - This is the Selection field for the section symbol, if
/// it is a COMDAT section (Characteristics & IMAGE_SCN_LNK_COMDAT) != 0
- int Selection;
+ mutable int Selection;
+
+ /// Assoc - This is name of the associated section, if it is a COMDAT
+ /// section (Characteristics & IMAGE_SCN_LNK_COMDAT) != 0 with an
+ /// associative Selection (IMAGE_COMDAT_SELECT_ASSOCIATIVE).
+ mutable const MCSectionCOFF *Assoc;
private:
friend class MCContext;
MCSectionCOFF(StringRef Section, unsigned Characteristics,
- int Selection, SectionKind K)
+ int Selection, const MCSectionCOFF *Assoc, SectionKind K)
: MCSection(SV_COFF, K), SectionName(Section),
- Characteristics(Characteristics), Selection (Selection) {
+ Characteristics(Characteristics), Selection(Selection), Assoc(Assoc) {
assert ((Characteristics & 0x00F00000) == 0 &&
"alignment must not be set upon section creation");
+ assert ((Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) ==
+ (Assoc != 0) &&
+ "associative COMDAT section must have an associated section");
}
~MCSectionCOFF();
@@ -57,7 +68,10 @@ namespace llvm {
return SectionName.str() + "_end";
}
unsigned getCharacteristics() const { return Characteristics; }
- int getSelection () const { return Selection; }
+ int getSelection() const { return Selection; }
+ const MCSectionCOFF *getAssocSection() const { return Assoc; }
+
+ void setSelection(int Selection, const MCSectionCOFF *Assoc = 0) const;
virtual void PrintSwitchToSection(const MCAsmInfo &MAI,
raw_ostream &OS,
diff --git a/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 7e7359a8fe..3e1afc01e0 100644
--- a/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -732,8 +732,8 @@ getExplicitSectionGlobal(const GlobalValue *GV, SectionKind Kind,
}
return getContext().getCOFFSection(Name,
Characteristics,
- Selection,
- Kind);
+ Kind,
+ Selection);
}
static const char *getCOFFSectionPrefixForUniqueGlobal(SectionKind Kind) {
@@ -769,7 +769,7 @@ SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind,
Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT;
return getContext().getCOFFSection(Name.str(), Characteristics,
- COFF::IMAGE_COMDAT_SELECT_ANY, Kind);
+ Kind, COFF::IMAGE_COMDAT_SELECT_ANY);
}
if (Kind.isText())
diff --git a/lib/MC/MCContext.cpp b/lib/MC/MCContext.cpp
index d5788332d5..6e4d82b6e7 100644
--- a/lib/MC/MCContext.cpp
+++ b/lib/MC/MCContext.cpp
@@ -274,10 +274,10 @@ const MCSectionELF *MCContext::CreateELFGroupSection() {
return Result;
}
-const MCSection *MCContext::getCOFFSection(StringRef Section,
- unsigned Characteristics,
- int Selection,
- SectionKind Kind) {
+const MCSectionCOFF *MCContext::getCOFFSection(StringRef Section,
+ unsigned Characteristics,
+ SectionKind Kind, int Selection,
+ const MCSectionCOFF *Assoc) {
if (COFFUniquingMap == 0)
COFFUniquingMap = new COFFUniqueMapTy();
COFFUniqueMapTy &Map = *(COFFUniqueMapTy*)COFFUniquingMap;
@@ -288,12 +288,20 @@ const MCSection *MCContext::getCOFFSection(StringRef Section,
MCSectionCOFF *Result = new (*this) MCSectionCOFF(Entry.getKey(),
Characteristics,
- Selection, Kind);
+ Selection, Assoc, Kind);
Entry.setValue(Result);
return Result;
}
+const MCSectionCOFF *MCContext::getCOFFSection(StringRef Section) {
+ if (COFFUniquingMap == 0)
+ COFFUniquingMap = new COFFUniqueMapTy();
+ COFFUniqueMapTy &Map = *(COFFUniqueMapTy*)COFFUniquingMap;
+
+ return Map.lookup(Section);
+}
+
//===----------------------------------------------------------------------===//
// Dwarf Management
//===----------------------------------------------------------------------===//
diff --git a/lib/MC/MCParser/COFFAsmParser.cpp b/lib/MC/MCParser/COFFAsmParser.cpp
index c2a22619db..df1794c979 100644
--- a/lib/MC/MCParser/COFFAsmParser.cpp
+++ b/lib/MC/MCParser/COFFAsmParser.cpp
@@ -51,6 +51,7 @@ class COFFAsmParser : public MCAsmParserExtension {
addDirectiveHandler<&COFFAsmParser::ParseDirectiveType>(".type");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveEndef>(".endef");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSecRel32>(".secrel32");
+ addDirectiveHandler<&COFFAsmParser::ParseDirectiveLinkOnce>(".linkonce");
// Win64 EH directives.
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveStartProc>(
@@ -110,6 +111,7 @@ class COFFAsmParser : public MCAsmParserExtension {
bool ParseDirectiveType(StringRef, SMLoc);
bool ParseDirectiveEndef(StringRef, SMLoc);
bool ParseDirectiveSecRel32(StringRef, SMLoc);
+ bool ParseDirectiveLinkOnce(StringRef, SMLoc);
// Win64 EH directives.
bool ParseSEHDirectiveStartProc(StringRef, SMLoc);
@@ -407,6 +409,64 @@ bool COFFAsmParser::ParseDirectiveSecRel32(StringRef, SMLoc) {
return false;
}
+/// ParseDirectiveLinkOnce
+/// ::= .linkonce [ identifier [ identifier ] ]
+bool COFFAsmParser::ParseDirectiveLinkOnce(StringRef, SMLoc Loc) {
+ COFF::COMDATType Type = COFF::IMAGE_COMDAT_SELECT_ANY;
+
+ if (getLexer().is(AsmToken::Identifier)) {
+ StringRef TypeId = getTok().getIdentifier();
+
+ Type = StringSwitch<COFF::COMDATType>(TypeId)
+ .Case("one_only", COFF::IMAGE_COMDAT_SELECT_NODUPLICATES)
+ .Case("discard", COFF::IMAGE_COMDAT_SELECT_ANY)
+ .Case("same_size", COFF::IMAGE_COMDAT_SELECT_SAME_SIZE)
+ .Case("same_contents", COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH)
+ .Case("associative", COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ .Case("largest", COFF::IMAGE_COMDAT_SELECT_LARGEST)
+ .Case("newest", COFF::IMAGE_COMDAT_SELECT_NEWEST)
+ .Default((COFF::COMDATType)0);
+
+ if (Type == 0)
+ return TokError(Twine("unrecognized COMDAT type '" + TypeId + "'"));
+
+ Lex();
+ }
+
+ const MCSectionCOFF *Current = static_cast<const MCSectionCOFF*>(
+ getStreamer().getCurrentSection().first);
+
+ const MCSectionCOFF *Assoc = 0;
+ if (Type == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+ StringRef AssocName;
+ SMLoc Loc = getTok().getLoc();
+ if (ParseSectionName(AssocName))
+ return TokError("expected associated section name");
+
+ Assoc = static_cast<const MCSectionCOFF*>(
+ getContext().getCOFFSection(AssocName));
+ if (!Assoc)
+ return Error(Loc, "cannot associate unknown section '" + AssocName + "'");
+ if (Assoc == Current)
+ return Error(Loc, "cannot associate a section with itself");
+ if (!(Assoc->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT))
+ return Error(Loc, "associated section must be a COMDAT section");
+ if (Assoc->getSelection() == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ return Error(Loc, "associated section cannot be itself associative");
+ }
+
+ if (Current->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT)
+ return Error(Loc, Twine("section '") + Current->getSectionName() +
+ "' is already linkonce");
+
+ Current->setSelection(Type, Assoc);
+
+ if (getLexer().isNot(AsmToken::EndOfStatement))
+ return TokError("unexpected token in directive");
+
+ return false;
+}
+
bool COFFAsmParser::ParseSEHDirectiveStartProc(StringRef, SMLoc) {
StringRef SymbolID;
if (getParser().parseIdentifier(SymbolID))
diff --git a/lib/MC/MCSectionCOFF.cpp b/lib/MC/MCSectionCOFF.cpp
index 6cedf0655c..64aa2c5c49 100644
--- a/lib/MC/MCSectionCOFF.cpp
+++ b/lib/MC/MCSectionCOFF.cpp
@@ -28,6 +28,17 @@ bool MCSectionCOFF::ShouldOmitSectionDirective(StringRef Name,
return false;
}
+void MCSectionCOFF::setSelection(int Selection,
+ const MCSectionCOFF *Assoc) const {
+ assert(Selection != 0 && "invalid COMDAT selection type");
+ assert((Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) ==
+ (Assoc != 0) &&
+ "associative COMDAT section must have an associated section");
+ this->Selection = Selection;
+ this->Assoc = Assoc;
+ Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT;
+}
+
void MCSectionCOFF::PrintSwitchToSection(const MCAsmInfo &MAI,
raw_ostream &OS,
const MCExpr *Subsection) const {
@@ -63,12 +74,15 @@ void MCSectionCOFF::PrintSwitchToSection(const MCAsmInfo &MAI,
case COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH:
OS << "\t.linkonce same_contents\n";
break;
- //NOTE: as of binutils 2.20, there is no way to specifiy select largest
- // with the .linkonce directive. For now, we treat it as an invalid
- // comdat selection value.
+ case COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE:
+ OS << "\t.linkonce associative " << Assoc->getSectionName() << "\n";
+ break;
case COFF::IMAGE_COMDAT_SELECT_LARGEST:
- // OS << "\t.linkonce largest\n";
- // break;
+ OS << "\t.linkonce largest\n";
+ break;
+ case COFF::IMAGE_COMDAT_SELECT_NEWEST:
+ OS << "\t.linkonce newest\n";
+ break;
default:
assert (0 && "unsupported COFF selection type");
break;
diff --git a/lib/MC/WinCOFFObjectWriter.cpp b/lib/MC/WinCOFFObjectWriter.cpp
index 518b59ee24..cf73396088 100644
--- a/lib/MC/WinCOFFObjectWriter.cpp
+++ b/lib/MC/WinCOFFObjectWriter.cpp
@@ -18,6 +18,7 @@
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
@@ -707,10 +708,13 @@ void WinCOFFObjectWriter::WriteObject(MCAssembler &Asm,
// Assign symbol and section indexes and offsets.
Header.NumberOfSections = 0;
+ DenseMap<COFFSection *, uint16_t> SectionIndices;
for (sections::iterator i = Sections.begin(),
e = Sections.end(); i != e; i++) {
if (Layout.getSectionAddressSize((*i)->MCData) > 0) {
- MakeSectionReal(**i, ++Header.NumberOfSections);
+ size_t Number = ++Header.NumberOfSections;
+ SectionIndices[*i] = Number;
+ MakeSectionReal(**i, Number);
} else {
(*i)->Number = -1;
}
@@ -754,6 +758,31 @@ void WinCOFFObjectWriter::WriteObject(MCAssembler &Asm,
}
}
+ // Fixup associative COMDAT sections.
+ for (sections::iterator i = Sections.begin(),
+ e = Sections.end(); i != e; i++) {
+ if ((*i)->Symbol->Aux[0].Aux.SectionDefinition.Selection !=
+ COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ continue;
+
+ const MCSectionCOFF &MCSec = static_cast<const MCSectionCOFF &>(
+ (*i)->MCData->getSection());
+
+ COFFSection *Assoc = SectionMap.lookup(MCSec.getAssocSection());
+ if (!Assoc) {
+ report_fatal_error(Twine("Missing associated COMDAT section ") +
+ MCSec.getAssocSection()->getSectionName() +
+ " for section " + MCSec.getSectionName());
+ }
+
+ // Skip this section if the associated section is unused.
+ if (Assoc->Number == -1)
+ continue;
+
+ (*i)->Symbol->Aux[0].Aux.SectionDefinition.Number = SectionIndices[Assoc];
+ }
+
+
// Assign file offsets to COFF object file structures.
unsigned offset = 0;
diff --git a/lib/MC/WinCOFFStreamer.cpp b/lib/MC/WinCOFFStreamer.cpp
index 75f343c421..04bfeb4c2b 100644
--- a/lib/MC/WinCOFFStreamer.cpp
+++ b/lib/MC/WinCOFFStreamer.cpp
@@ -155,7 +155,7 @@ void WinCOFFStreamer::AddCommonSymbol(MCSymbol *Symbol, uint64_t Size,
int Selection = COFF::IMAGE_COMDAT_SELECT_LARGEST;
const MCSection *Section = MCStreamer::getContext().getCOFFSection(
- SectionName, Characteristics, Selection, SectionKind::getBSS());
+ SectionName, Characteristics, SectionKind::getBSS(), Selection);
MCSectionData &SectionData = getAssembler().getOrCreateSectionData(*Section);
diff --git a/test/MC/COFF/linkonce-invalid.s b/test/MC/COFF/linkonce-invalid.s
new file mode 100644
index 0000000000..90ce4a7ad3
--- /dev/null
+++ b/test/MC/COFF/linkonce-invalid.s
@@ -0,0 +1,40 @@
+// Test invalid use of the .linkonce directive.
+//
+// RUN: not llvm-mc -triple i386-pc-win32 -filetype=obj %s 2>&1 | FileCheck %s
+
+.section non_comdat
+
+.section comdat
+.linkonce discard
+
+.section assoc
+.linkonce associative comdat
+
+
+.section invalid
+
+// CHECK: error: unrecognized COMDAT type 'unknown'
+.linkonce unknown
+
+// CHECK: error: unexpected token in directive
+.linkonce discard foo
+
+// CHECK: error: expected associated section name
+.linkonce associative
+
+// CHECK: error: cannot associate unknown section 'unknown'
+.linkonce associative unknown
+
+// CHECK: error: cannot associate a section with itself
+.linkonce associative invalid
+
+// CHECK: error: associated section must be a COMDAT section
+.linkonce associative non_comdat
+
+// CHECK: error: associated section cannot be itself associative
+.linkonce associative assoc
+
+// CHECK: error: section 'multi' is already linkonce
+.section multi
+.linkonce discard
+.linkonce same_size
diff --git a/test/MC/COFF/linkonce.s b/test/MC/COFF/linkonce.s
new file mode 100644
index 0000000000..e7b7f475a3
--- /dev/null
+++ b/test/MC/COFF/linkonce.s
@@ -0,0 +1,179 @@
+// Test section manipulation via .linkonce directive.
+//
+// RUN: llvm-mc -triple i386-pc-win32 -filetype=obj %s | llvm-readobj -s -t | FileCheck %s
+// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -t | FileCheck %s
+
+.section s1
+.linkonce
+.long 1
+
+.section s2
+.linkonce one_only
+.long 1
+
+.section s3
+.linkonce discard
+.long 1
+
+.section s4
+.linkonce same_size
+.long 1
+
+.section s5
+.linkonce same_contents
+.long 1
+
+.section s6
+.linkonce associative s1
+.long 1
+
+.section s7
+.linkonce largest
+.long 1
+
+.section s8
+.linkonce newest
+.long 1
+
+.section .foo$bar
+.linkonce discard
+.long 1
+
+// Check that valid '.section' names can be associated.
+.section multi
+.linkonce associative .foo$bar
+.long 1
+
+
+// CHECK: Sections [
+// CHECK: Section {
+// CHECK: Name: s1
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s2
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s3
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s4
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s5
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s6
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s7
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: s8
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Name: multi
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: ]
+// CHECK: Symbols [
+// CHECK: Symbol {
+// CHECK: Name: s1
+// CHECK: Section: s1 (1)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 1
+// CHECK: Selection: Any (0x2)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s2
+// CHECK: Section: s2 (2)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 2
+// CHECK: Selection: NoDuplicates (0x1)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s3
+// CHECK: Section: s3 (3)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 3
+// CHECK: Selection: Any (0x2)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s4
+// CHECK: Section: s4 (4)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 4
+// CHECK: Selection: SameSize (0x3)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s5
+// CHECK: Section: s5 (5)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 5
+// CHECK: Selection: ExactMatch (0x4)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s6
+// CHECK: Section: s6 (6)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 1
+// CHECK: Selection: Associative (0x5)
+// CHECK: AssocSection: s1
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s7
+// CHECK: Section: s7 (7)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 7
+// CHECK: Selection: Largest (0x6)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: s8
+// CHECK: Section: s8 (8)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 8
+// CHECK: Selection: Newest (0x7)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: multi
+// CHECK: Value: 0
+// CHECK: Section: multi (10)
+// CHECK: AuxSectionDef {
+// CHECK: Number: 9
+// CHECK: Selection: Associative (0x5)
+// CHECK: AssocSection: .foo$bar
+// CHECK: }
+// CHECK: }