[WebAssembly] Initial support for shared objects (-shared)

Based on the initial spec proposal:
https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md

The llvm/codegen side of this is still missing but I believe this change is
still worth landing as an incremental step

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

llvm-svn: 346918
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index cb413de..6048508 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -39,7 +39,6 @@
 using namespace lld::wasm;
 
 static constexpr int kStackAlignment = 16;
-static constexpr int kInitialTableOffset = 1;
 static constexpr const char *kFunctionTableName = "__indirect_function_table";
 
 namespace {
@@ -90,6 +89,7 @@
   void createCustomSections();
 
   // Custom sections
+  void createDylinkSection();
   void createRelocSections();
   void createLinkingSection();
   void createNameSection();
@@ -98,8 +98,13 @@
   void writeSections();
 
   uint64_t FileSize = 0;
+  uint32_t TableBase = 0;
   uint32_t NumMemoryPages = 0;
   uint32_t MaxMemoryPages = 0;
+  // Memory size and aligment. Written to the "dylink" section
+  // when build with -shared or -pie.
+  uint32_t MemAlign = 0;
+  uint32_t MemSize = 0;
 
   std::vector<const WasmSignature *> Types;
   DenseMap<WasmSignature, int32_t> TypeIndices;
@@ -161,7 +166,7 @@
   }
 
   if (Config->ImportTable) {
-    uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+    uint32_t TableSize = TableBase + IndirectFunctions.size();
     WasmImport Import;
     Import.Module = "env";
     Import.Field = kFunctionTableName;
@@ -259,7 +264,7 @@
   //     no address-taken function will fail at validation time since it is
   //     a validation error to include a call_indirect instruction if there
   //     is not table.
-  uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+  uint32_t TableSize = TableBase + IndirectFunctions.size();
 
   SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE);
   raw_ostream &OS = Section->getStream();
@@ -325,12 +330,18 @@
   writeUleb128(OS, 1, "segment count");
   writeUleb128(OS, 0, "table index");
   WasmInitExpr InitExpr;
-  InitExpr.Opcode = WASM_OPCODE_I32_CONST;
-  InitExpr.Value.Int32 = kInitialTableOffset;
+  if (Config->Pic) {
+    InitExpr.Opcode = WASM_OPCODE_GET_GLOBAL;
+    InitExpr.Value.Global =
+        cast<GlobalSymbol>(WasmSym::TableBase)->getGlobalIndex();
+  } else {
+    InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+    InitExpr.Value.Int32 = TableBase;
+  }
   writeInitExpr(OS, InitExpr);
   writeUleb128(OS, IndirectFunctions.size(), "elem count");
 
-  uint32_t TableIndex = kInitialTableOffset;
+  uint32_t TableIndex = TableBase;
   for (const FunctionSymbol *Sym : IndirectFunctions) {
     assert(Sym->getTableIndex() == TableIndex);
     writeUleb128(OS, Sym->getFunctionIndex(), "function index");
@@ -425,6 +436,20 @@
   raw_string_ostream OS{Body};
 };
 
+// Create the custom "dylink" section containing information for the dynamic
+// linker.
+// See
+// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+void Writer::createDylinkSection() {
+  SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, "dylink");
+  raw_ostream &OS = Section->getStream();
+
+  writeUleb128(OS, MemSize, "MemSize");
+  writeUleb128(OS, int(log2(MemAlign)), "MemAlign");
+  writeUleb128(OS, IndirectFunctions.size(), "TableSize");
+  writeUleb128(OS, 0, "TableAlign");
+}
+
 // Create the custom "linking" section containing linker metadata.
 // This is only created when relocatable output is requested.
 void Writer::createLinkingSection() {
@@ -599,7 +624,7 @@
   uint32_t MemoryPtr = 0;
 
   auto PlaceStack = [&]() {
-    if (Config->Relocatable)
+    if (Config->Relocatable || Config->Shared)
       return;
     MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
     if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
@@ -625,7 +650,9 @@
   if (WasmSym::DsoHandle)
     WasmSym::DsoHandle->setVirtualAddress(DataStart);
 
+  MemAlign = 0;
   for (OutputSegment *Seg : Segments) {
+    MemAlign = std::max(MemAlign, Seg->Alignment);
     MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
     Seg->StartVA = MemoryPtr;
     log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", Seg->Name,
@@ -658,8 +685,8 @@
     else
       MemoryPtr = Config->InitialMemory;
   }
-  uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize);
-  NumMemoryPages = MemSize / WasmPageSize;
+  MemSize = MemoryPtr;
+  NumMemoryPages = alignTo(MemoryPtr, WasmPageSize) / WasmPageSize;
   log("mem: total pages = " + Twine(NumMemoryPages));
 
   if (Config->MaxMemory != 0) {
@@ -682,6 +709,8 @@
 
 void Writer::createSections() {
   // Known sections
+  if (Config->Pic)
+    createDylinkSection();
   createTypeSection();
   createImportSection();
   createFunctionSection();
@@ -874,7 +903,7 @@
       AddDefinedFunction(Func);
   }
 
-  uint32_t TableIndex = kInitialTableOffset;
+  uint32_t TableIndex = TableBase;
   auto HandleRelocs = [&](InputChunk *Chunk) {
     if (!Chunk->Live)
       return;
@@ -926,6 +955,10 @@
 }
 
 static StringRef getOutputDataSegmentName(StringRef Name) {
+  // With PIC code we currently only support a single data segment since
+  // we only have a single __memory_base to use as our base address.
+  if (Config->Pic)
+    return "data";
   if (!Config->MergeDataSegments)
     return Name;
   if (Name.startswith(".text."))
@@ -1010,9 +1043,14 @@
 }
 
 void Writer::run() {
-  if (Config->Relocatable)
+  if (Config->Relocatable || Config->Pic)
     Config->GlobalBase = 0;
 
+  // For PIC code the table base is assigned dynamically by the loader.
+  // For non-PIC, we start at 1 so that accessing table index 0 always traps.
+  if (!Config->Pic)
+    TableBase = 1;
+
   log("-- calculateImports");
   calculateImports();
   log("-- assignIndexes");