MC: Implement COFF .linkonce directive

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@185753 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Nico Rieck 2013-07-06 12:13:10 +00:00
parent 2be430d251
commit 8064628379
11 changed files with 426 additions and 29 deletions

View File

@ -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
...

View File

@ -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);
/// @}

View File

@ -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,

View File

@ -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())

View File

@ -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
//===----------------------------------------------------------------------===//

View File

@ -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))

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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

179
test/MC/COFF/linkonce.s Normal file
View File

@ -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: }