[lld] r357715 - [WebAssembly] Apply data relocations at runtime in shared objects

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 4 11:40:51 PDT 2019


Author: sbc
Date: Thu Apr  4 11:40:51 2019
New Revision: 357715

URL: http://llvm.org/viewvc/llvm-project?rev=357715&view=rev
Log:
[WebAssembly] Apply data relocations at runtime in shared objects

See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md

Data section relocations in wasm shared libraries are applied by the
library itself at static constructor time.  This change adds a new
synthetic function that applies relocations to relevant memory locations
on startup.

Differential Revision: https://reviews.llvm.org/D59278

Modified:
    lld/trunk/test/wasm/shared.ll
    lld/trunk/test/wasm/undefined-data.ll
    lld/trunk/wasm/Driver.cpp
    lld/trunk/wasm/InputChunks.cpp
    lld/trunk/wasm/InputChunks.h
    lld/trunk/wasm/InputFiles.h
    lld/trunk/wasm/MarkLive.cpp
    lld/trunk/wasm/Symbols.cpp
    lld/trunk/wasm/Symbols.h
    lld/trunk/wasm/Writer.cpp

Modified: lld/trunk/test/wasm/shared.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/wasm/shared.ll?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/test/wasm/shared.ll (original)
+++ lld/trunk/test/wasm/shared.ll Thu Apr  4 11:40:51 2019
@@ -9,6 +9,9 @@ target triple = "wasm32-unknown-unknown"
 @indirect_func = local_unnamed_addr global i32 ()* @foo, align 4
 @indirect_func_external = local_unnamed_addr global void ()* @func_external, align 4
 
+ at data_addr = local_unnamed_addr global i32* @data, align 4
+ at data_addr_external = local_unnamed_addr global i32* @data_external, align 4
+
 define default i32 @foo() {
 entry:
   ; To ensure we use __stack_pointer
@@ -36,7 +39,7 @@ declare void @func_external()
 ; CHECK:      Sections:
 ; CHECK-NEXT:   - Type:            CUSTOM
 ; CHECK-NEXT:     Name:            dylink
-; CHECK-NEXT:     MemorySize:      12
+; CHECK-NEXT:     MemorySize:      20
 ; CHECK-NEXT:     MemoryAlignment: 2
 ; CHECK-NEXT:     TableSize:       2
 ; CHECK-NEXT:     TableAlignment:  0
@@ -90,6 +93,12 @@ declare void @func_external()
 ; CHECK-NEXT:         GlobalMutable:   true
 ; CHECK-NEXT:   - Type:            FUNCTION
 
+; CHECK:        - Type:            EXPORT
+; CHECK-NEXT:     Exports:
+; CHECK-NEXT:       - Name:            __wasm_call_ctors
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Index:           1
+
 ; check for elem segment initialized with __table_base global as offset
 
 ; CHECK:        - Type:            ELEM
@@ -97,7 +106,19 @@ declare void @func_external()
 ; CHECK-NEXT:       - Offset:
 ; CHECK-NEXT:           Opcode:          GLOBAL_GET
 ; CHECK-NEXT:           Index:           2
-; CHECK-NEXT:         Functions:       [ 1, 0 ]
+; CHECK-NEXT:         Functions:       [ 3, 0 ]
+
+; check the generated code in __wasm_call_ctors and __wasm_apply_relocs functions
+; TODO(sbc): Disassemble and verify instructions.
+
+; CHECK:        - Type:            CODE
+; CHECK-NEXT:     Functions:
+; CHECK-NEXT:       - Index:           1
+; CHECK-NEXT:         Locals:          []
+; CHECK-NEXT:         Body:            10020B
+; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:         Locals:          []
+; CHECK-NEXT:         Body:            230141046A230241006A360200230141086A230241016A3602002301410C6A230141006A360200230141106A23033602000B
 
 ; check the data segment initialized with __memory_base global as offset
 
@@ -108,4 +129,4 @@ declare void @func_external()
 ; CHECK-NEXT:         Offset:
 ; CHECK-NEXT:           Opcode:          GLOBAL_GET
 ; CHECK-NEXT:           Index:           1
-; CHECK-NEXT:         Content:         '020000000000000001000000'
+; CHECK-NEXT:         Content:         '0200000000000000010000000000000000000000'

Modified: lld/trunk/test/wasm/undefined-data.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/wasm/undefined-data.ll?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/test/wasm/undefined-data.ll (original)
+++ lld/trunk/test/wasm/undefined-data.ll Thu Apr  4 11:40:51 2019
@@ -1,6 +1,6 @@
 ; RUN: llc -filetype=obj %s -o %t.o
 ; RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=UNDEF
-; RUN: not wasm-ld --allow-undefined -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=BADRELOC
+; RUN: not wasm-ld --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=BADRELOC
 
 target triple = "wasm32-unknown-unknown"
 

Modified: lld/trunk/wasm/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Driver.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Driver.cpp (original)
+++ lld/trunk/wasm/Driver.cpp Thu Apr  4 11:40:51 2019
@@ -278,10 +278,15 @@ void LinkerDriver::createFiles(opt::Inpu
   }
 }
 
-static StringRef getEntry(opt::InputArgList &Args, StringRef Default) {
+static StringRef getEntry(opt::InputArgList &Args) {
   auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
-  if (!Arg)
-    return Default;
+  if (!Arg) {
+    if (Args.hasArg(OPT_relocatable))
+      return "";
+    if (Args.hasArg(OPT_shared))
+      return "__wasm_call_ctors";
+    return "_start";
+  }
   if (Arg->getOption().getID() == OPT_no_entry)
     return "";
   return Arg->getValue();
@@ -298,7 +303,7 @@ static void setConfigs(opt::InputArgList
   Config->CompressRelocations = Args.hasArg(OPT_compress_relocations);
   Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
   Config->DisableVerify = Args.hasArg(OPT_disable_verify);
-  Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
+  Config->Entry = getEntry(Args);
   Config->ExportAll = Args.hasArg(OPT_export_all);
   Config->ExportDynamic = Args.hasFlag(OPT_export_dynamic,
       OPT_no_export_dynamic, false);
@@ -422,11 +427,21 @@ static void createSyntheticSymbols() {
   static llvm::wasm::WasmGlobalType MutableGlobalTypeI32 = {WASM_TYPE_I32,
                                                             true};
 
-  if (!Config->Relocatable)
+  if (!Config->Relocatable) {
     WasmSym::CallCtors = Symtab->addSyntheticFunction(
         "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
         make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
 
+    if (Config->Pic) {
+      // For PIC code we create a synthetic function call __wasm_apply_relocs
+      // and add this as the first call in __wasm_call_ctors.
+      // We also unconditionally export 
+      WasmSym::ApplyRelocs = Symtab->addSyntheticFunction(
+          "__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+          make<SyntheticFunction>(NullSignature, "__wasm_apply_relocs"));
+    }
+  }
+
   // The __stack_pointer is imported in the shared library case, and exported
   // in the non-shared (executable) case.
   if (Config->Shared) {
@@ -518,12 +533,6 @@ void LinkerDriver::link(ArrayRef<const c
     Config->ImportTable = true;
   }
 
-  if (Config->Shared) {
-    Config->ImportMemory = true;
-    Config->ExportDynamic = true;
-    Config->AllowUndefined = true;
-  }
-
   // Handle --trace-symbol.
   for (auto *Arg : Args.filtered(OPT_trace_symbol))
     Symtab->trace(Arg->getValue());
@@ -531,6 +540,12 @@ void LinkerDriver::link(ArrayRef<const c
   if (!Config->Relocatable)
     createSyntheticSymbols();
 
+  if (Config->Shared) {
+    Config->ImportMemory = true;
+    Config->ExportDynamic = true;
+    Config->AllowUndefined = true;
+  }
+
   createFiles(Args);
   if (errorCount())
     return;
@@ -547,15 +562,13 @@ void LinkerDriver::link(ArrayRef<const c
     handleUndefined(Arg->getValue());
 
   Symbol *EntrySym = nullptr;
-  if (!Config->Relocatable) {
-    if (!Config->Shared && !Config->Entry.empty()) {
-      EntrySym = handleUndefined(Config->Entry);
-      if (EntrySym && EntrySym->isDefined())
-        EntrySym->ForceExport = true;
-      else
-        error("entry symbol not defined (pass --no-entry to supress): " +
-              Config->Entry);
-    }
+  if (!Config->Relocatable && !Config->Entry.empty()) {
+    EntrySym = handleUndefined(Config->Entry);
+    if (EntrySym && EntrySym->isDefined())
+      EntrySym->ForceExport = true;
+    else
+      error("entry symbol not defined (pass --no-entry to supress): " +
+            Config->Entry);
   }
 
   if (errorCount())

Modified: lld/trunk/wasm/InputChunks.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/InputChunks.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/InputChunks.cpp (original)
+++ lld/trunk/wasm/InputChunks.cpp Thu Apr  4 11:40:51 2019
@@ -296,3 +296,58 @@ void InputFunction::writeTo(uint8_t *Buf
   memcpy(Buf, LastRelocEnd, ChunkSize);
   LLVM_DEBUG(dbgs() << "  total: " << (Buf + ChunkSize - Orig) << "\n");
 }
+
+// Generate code to apply relocations to the data section at runtime.
+// This is only called when generating shared libaries (PIC) where address are
+// not known at static link time.
+void InputSegment::generateRelocationCode(raw_ostream &OS) const {
+  uint32_t SegmentVA = OutputSeg->StartVA + OutputSegmentOffset;
+  for (const WasmRelocation &Rel : Relocations) {
+    uint32_t Offset = Rel.Offset - getInputSectionOffset();
+    uint32_t OutputVA = SegmentVA + Offset;
+
+    // Get __memory_base
+    writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+    writeUleb128(OS, WasmSym::MemoryBase->getGlobalIndex(), "memory_base");
+
+    // Add the offset of the relocation
+    writeU8(OS, WASM_OPCODE_I32_CONST, "I32_CONST");
+    writeSleb128(OS, OutputVA, "offset");
+    writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
+
+    // Now figure out what we want to store
+    switch (Rel.Type) {
+    case R_WASM_TABLE_INDEX_I32:
+      // Add the table index to the __table_base
+      writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+      writeUleb128(OS, WasmSym::TableBase->getGlobalIndex(), "table_base");
+      writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
+      writeSleb128(OS, File->calcNewValue(Rel), "new table index");
+      writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
+      break;
+    case R_WASM_MEMORY_ADDR_I32: {
+      Symbol *Sym = File->getSymbol(Rel);
+      if (Sym->isUndefined()) {
+        // Undefined addresses are accessed via imported GOT globals
+        writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+        writeUleb128(OS, Sym->getGOTIndex(), "global index");
+      } else {
+        // Defined global data is accessed via known offset from __memory_base
+        writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+        writeUleb128(OS, WasmSym::MemoryBase->getGlobalIndex(), "memory_base");
+        writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
+        writeSleb128(OS, File->calcNewValue(Rel), "new memory offset");
+        writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
+      }
+      break;
+    }
+    default:
+      llvm_unreachable("unexpected relocation type in data segment");
+    }
+
+    // Store that value at the virtual address
+    writeU8(OS, WASM_OPCODE_I32_STORE, "I32_STORE");
+    writeUleb128(OS, 2, "align");
+    writeUleb128(OS, 0, "offset");
+  }
+}

Modified: lld/trunk/wasm/InputChunks.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/InputChunks.h?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/InputChunks.h (original)
+++ lld/trunk/wasm/InputChunks.h Thu Apr  4 11:40:51 2019
@@ -92,6 +92,8 @@ public:
 
   static bool classof(const InputChunk *C) { return C->kind() == DataSegment; }
 
+  void generateRelocationCode(raw_ostream &OS) const;
+
   uint32_t getAlignment() const { return Segment.Data.Alignment; }
   StringRef getName() const override { return Segment.Data.Name; }
   StringRef getDebugName() const override { return StringRef(); }

Modified: lld/trunk/wasm/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/InputFiles.h?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/InputFiles.h (original)
+++ lld/trunk/wasm/InputFiles.h Thu Apr  4 11:40:51 2019
@@ -96,6 +96,9 @@ public:
   uint32_t calcNewValue(const WasmRelocation &Reloc) const;
   uint32_t calcNewAddend(const WasmRelocation &Reloc) const;
   uint32_t calcExpectedValue(const WasmRelocation &Reloc) const;
+  Symbol *getSymbol(const WasmRelocation &Reloc) const {
+    return Symbols[Reloc.Index];
+  };
 
   const WasmSection *CodeSection = nullptr;
   const WasmSection *DataSection = nullptr;

Modified: lld/trunk/wasm/MarkLive.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/MarkLive.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/MarkLive.cpp (original)
+++ lld/trunk/wasm/MarkLive.cpp Thu Apr  4 11:40:51 2019
@@ -76,6 +76,11 @@ void lld::wasm::markLive() {
     }
   }
 
+  if (Config->Pic) {
+    Enqueue(WasmSym::CallCtors);
+    Enqueue(WasmSym::ApplyRelocs);
+  }
+
   // Follow relocations to mark all reachable chunks.
   while (!Q.empty()) {
     InputChunk *C = Q.pop_back_val();

Modified: lld/trunk/wasm/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.cpp (original)
+++ lld/trunk/wasm/Symbols.cpp Thu Apr  4 11:40:51 2019
@@ -24,6 +24,7 @@ using namespace lld;
 using namespace lld::wasm;
 
 DefinedFunction *WasmSym::CallCtors;
+DefinedFunction *WasmSym::ApplyRelocs;
 DefinedData *WasmSym::DsoHandle;
 DefinedData *WasmSym::DataEnd;
 DefinedData *WasmSym::HeapBase;

Modified: lld/trunk/wasm/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Symbols.h?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Symbols.h (original)
+++ lld/trunk/wasm/Symbols.h Thu Apr  4 11:40:51 2019
@@ -406,6 +406,10 @@ struct WasmSym {
   // Function that directly calls all ctors in priority order.
   static DefinedFunction *CallCtors;
 
+  // __wasm_apply_relocs
+  // Function that applies relocations to data segment post-instantiation.
+  static DefinedFunction *ApplyRelocs;
+
   // __dso_handle
   // Symbol used in calls to __cxa_atexit to determine current DLL
   static DefinedData *DsoHandle;

Modified: lld/trunk/wasm/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/wasm/Writer.cpp?rev=357715&r1=357714&r2=357715&view=diff
==============================================================================
--- lld/trunk/wasm/Writer.cpp (original)
+++ lld/trunk/wasm/Writer.cpp Thu Apr  4 11:40:51 2019
@@ -65,7 +65,9 @@ private:
   uint32_t lookupType(const WasmSignature &Sig);
   uint32_t registerType(const WasmSignature &Sig);
 
-  void createCtorFunction();
+  void createApplyRelocationsFunction();
+  void createCallCtorsFunction();
+
   void calculateInitFunctions();
   void processRelocations(InputChunk *Chunk);
   void assignIndexes();
@@ -1149,17 +1151,6 @@ void Writer::processRelocations(InputChu
       File->TypeMap[Reloc.Index] = registerType(Types[Reloc.Index]);
       File->TypeIsUsed[Reloc.Index] = true;
       break;
-    case R_WASM_MEMORY_ADDR_SLEB:
-    case R_WASM_MEMORY_ADDR_I32:
-    case R_WASM_MEMORY_ADDR_LEB: {
-      DataSymbol *Sym = File->getDataSymbol(Reloc.Index);
-      if (!Config->Relocatable && !isa<DefinedData>(Sym) && !Sym->isWeak())
-        error(File->getName() + ": relocation " +
-              relocTypeToString(Reloc.Type) + " cannot be used againt symbol " +
-              Sym->getName() + "; recompile with -fPIC");
-
-      break;
-    }
     case R_WASM_GLOBAL_INDEX_LEB: {
       auto* Sym = File->getSymbols()[Reloc.Index];
       if (!isa<GlobalSymbol>(Sym) && !Sym->isInGOT()) {
@@ -1168,7 +1159,24 @@ void Writer::processRelocations(InputChu
       }
     }
     }
+
+    if (Config->Pic) {
+      // Certain relocation types can't be used when building PIC output, since
+      // they would require absolute symbol addresses at link time.
+      switch (Reloc.Type) {
+      case R_WASM_TABLE_INDEX_SLEB:
+      case R_WASM_MEMORY_ADDR_SLEB:
+      case R_WASM_MEMORY_ADDR_LEB: {
+        Symbol *Sym = File->getSymbols()[Reloc.Index];
+        error(toString(File) + ": relocation " +
+              relocTypeToString(Reloc.Type) + " cannot be used againt symbol " +
+              toString(*Sym) + "; recompile with -fPIC");
+        break;
+      }
+      }
+    }
   }
+
 }
 
 void Writer::assignIndexes() {
@@ -1272,12 +1280,38 @@ void Writer::createOutputSegments() {
   }
 }
 
-static const int OPCODE_CALL = 0x10;
-static const int OPCODE_END = 0xb;
+// For -shared (PIC) output, we create create a synthetic function which will
+// apply any relocations to the data segments on startup.  This function is
+// called __wasm_apply_relocs and is added at the very beginning of
+// __wasm_call_ctors before any of the constructors run.
+void Writer::createApplyRelocationsFunction() {
+  LLVM_DEBUG(dbgs() << "createApplyRelocationsFunction\n");
+  // First write the body's contents to a string.
+  std::string BodyContent;
+  {
+    raw_string_ostream OS(BodyContent);
+    writeUleb128(OS, 0, "num locals");
+    for (const OutputSegment *Seg : Segments)
+      for (const InputSegment *InSeg : Seg->InputSegments)
+        InSeg->generateRelocationCode(OS);
+    writeU8(OS, WASM_OPCODE_END, "END");
+  }
+
+  // Once we know the size of the body we can create the final function body
+  std::string FunctionBody;
+  {
+    raw_string_ostream OS(FunctionBody);
+    writeUleb128(OS, BodyContent.size(), "function size");
+    OS << BodyContent;
+  }
+
+  ArrayRef<uint8_t> Body = arrayRefFromStringRef(Saver.save(FunctionBody));
+  cast<SyntheticFunction>(WasmSym::ApplyRelocs->Function)->setBody(Body);
+}
 
 // Create synthetic "__wasm_call_ctors" function based on ctor functions
 // in input object.
-void Writer::createCtorFunction() {
+void Writer::createCallCtorsFunction() {
   if (!WasmSym::CallCtors->isLive())
     return;
 
@@ -1286,11 +1320,16 @@ void Writer::createCtorFunction() {
   {
     raw_string_ostream OS(BodyContent);
     writeUleb128(OS, 0, "num locals");
+    if (Config->Pic) {
+      writeU8(OS, WASM_OPCODE_CALL, "CALL");
+      writeUleb128(OS, WasmSym::ApplyRelocs->getFunctionIndex(),
+                   "function index");
+    }
     for (const WasmInitEntry &F : InitFunctions) {
-      writeU8(OS, OPCODE_CALL, "CALL");
+      writeU8(OS, WASM_OPCODE_CALL, "CALL");
       writeUleb128(OS, F.Sym->getFunctionIndex(), "function index");
     }
-    writeU8(OS, OPCODE_END, "END");
+    writeU8(OS, WASM_OPCODE_END, "END");
   }
 
   // Once we know the size of the body we can create the final function body
@@ -1348,12 +1387,15 @@ void Writer::run() {
   assignIndexes();
   log("-- calculateInitFunctions");
   calculateInitFunctions();
-  if (!Config->Relocatable)
-    createCtorFunction();
   log("-- calculateTypes");
   calculateTypes();
   log("-- layoutMemory");
   layoutMemory();
+  if (!Config->Relocatable) {
+    if (Config->Pic)
+      createApplyRelocationsFunction();
+    createCallCtorsFunction();
+  }
   log("-- calculateExports");
   calculateExports();
   log("-- calculateCustomSections");




More information about the llvm-commits mailing list