From: Nico Rieck Date: Sat, 6 Jul 2013 12:13:10 +0000 (+0000) Subject: MC: Implement COFF .linkonce directive X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=80646283796b20c6a1b7d8eb69ce6f0478d54383;p=oota-llvm.git MC: Implement COFF .linkonce directive git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@185753 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/Extensions.rst b/docs/Extensions.rst index 062804a9fc7..78ff874abf0 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 1ebc452f07d..c012ed0f5aa 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 50e33a5b040..754e8290ef8 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 7e7359a8fee..3e1afc01e06 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 d5788332d5d..6e4d82b6e76 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 c2a22619db2..df1794c9799 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(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( + 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( + 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 6cedf0655cf..64aa2c5c49e 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 518b59ee244..cf733960881 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 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( + (*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 75f343c421b..04bfeb4c2ba 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 00000000000..90ce4a7ad31 --- /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 00000000000..e7b7f475a3c --- /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: }