diff options
Diffstat (limited to 'lib/MC/MCELFStreamer.cpp')
-rw-r--r-- | lib/MC/MCELFStreamer.cpp | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/lib/MC/MCELFStreamer.cpp b/lib/MC/MCELFStreamer.cpp index 65a0a7d7e6..e794e57e93 100644 --- a/lib/MC/MCELFStreamer.cpp +++ b/lib/MC/MCELFStreamer.cpp @@ -24,6 +24,7 @@ #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCValue.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ELF.h" #include "llvm/Support/ErrorHandling.h" @@ -51,6 +52,7 @@ public: virtual void EmitLabel(MCSymbol *Symbol); virtual void EmitAssemblerFlag(MCAssemblerFlag Flag); virtual void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value); + virtual void EmitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol); virtual void EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute); virtual void EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) { assert(0 && "ELF doesn't support this directive"); @@ -193,6 +195,64 @@ void MCELFStreamer::EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) { Symbol->setVariableValue(AddValueSymbols(Value)); } +// This is a hack. To be able to implement weakrefs the writer has to be able +// to distinguish +// .weakref foo, bar +// .long foo +// from +// .weakref foo, bar +// .long bar +// since the first case should produce a weak undefined reference and the second +// one a strong one. +// If we created foo as a regular alias pointing to bar (foo = bar), then +// MCExpr::EvaluateAsRelocatable would recurse on foo and the writer would +// never see it used in a relocation. +// What we do is create a MCTargetExpr that when evaluated produces a symbol +// ref to a temporary symbol. This temporary symbol in turn is a variable +// that equals the original symbol (tmp = bar). With this hack the writer +// gets a relocation with tmp and can correctly implement weak references. + +class WeakRefExpr : public MCTargetExpr { +private: + const MCSymbolRefExpr *Alias; + + explicit WeakRefExpr(const MCSymbolRefExpr *Alias_) + : MCTargetExpr(), Alias(Alias_) {} + +public: + virtual void PrintImpl(raw_ostream &OS) const { + llvm_unreachable("Unimplemented"); + } + + virtual bool EvaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout) const { + Res = MCValue::get(Alias, 0, 0); + return true; + } + + static const WeakRefExpr *Create(const MCSymbol *Alias, MCContext &Ctx) { + const MCSymbolRefExpr *A = MCSymbolRefExpr::Create(Alias, Ctx); + return new (Ctx) WeakRefExpr(A); + } +}; + +void MCELFStreamer::EmitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) { + getAssembler().getOrCreateSymbolData(*Symbol); + MCSymbolData &AliasSD = getAssembler().getOrCreateSymbolData(*Alias); + AliasSD.setFlags(AliasSD.getFlags() | ELF_Other_Weakref); + + // Create the alias that actually points to Symbol + const MCSymbolRefExpr *SymRef = MCSymbolRefExpr::Create(Symbol, getContext()); + MCSymbol *RealAlias = getContext().CreateTempSymbol(); + RealAlias->setVariableValue(SymRef); + + MCSymbolData &RealAliasSD = getAssembler().getOrCreateSymbolData(*RealAlias); + RealAliasSD.setFlags(RealAliasSD.getFlags() | ELF_Other_Weakref); + + const MCExpr *Value = WeakRefExpr::Create(RealAlias, getContext()); + Alias->setVariableValue(Value); +} + static void SetBinding(MCSymbolData &SD, unsigned Binding) { assert(Binding == ELF::STB_LOCAL || Binding == ELF::STB_GLOBAL || Binding == ELF::STB_WEAK); |