Implement .weakref.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@117911 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/MC/MCELFStreamer.cpp b/lib/MC/MCELFStreamer.cpp
index 65a0a7d..e794e57 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 @@
   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 @@
   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);