[lld][WebAssembly] Fix static linking of -fPIC code with external undefined functions

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

git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@372779 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/wasm/pic-static.ll b/test/wasm/pic-static.ll
index f0187ec..d13d74d 100644
--- a/test/wasm/pic-static.ll
+++ b/test/wasm/pic-static.ll
@@ -3,12 +3,13 @@
 ; fixed values.
 ; RUN: llc -relocation-model=pic -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
 ; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o
-; RUN: wasm-ld -o %t.wasm %t.o %t.ret32.o
+; RUN: wasm-ld --allow-undefined --export-all -o %t.wasm %t.o %t.ret32.o
 ; RUN: obj2yaml %t.wasm | FileCheck %s
 
 target triple = "wasm32-unknown-emscripten"
 
 declare i32 @ret32(float)
+declare i32 @missing_function(float)
 @global_float = global float 1.0
 @hidden_float = hidden global float 2.0
 
@@ -18,6 +19,10 @@
   ret i32 (float)* @ret32;
 }
 
+define i32 (float)* @getaddr_missing_function() {
+  ret i32 (float)* @missing_function;
+}
+
 define i32 ()* @getaddr_hidden() {
   ret i32 ()* @hidden_func;
 }
@@ -60,10 +65,18 @@
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
 ; CHECK-NEXT:           Opcode:          I32_CONST
+; CHECK-NEXT:           Value:           1
+
+; GOT.func.missing_function
+; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:         Type:            I32
+; CHECK-NEXT:         Mutable:         false
+; CHECK-NEXT:         InitExpr:
+; CHECK-NEXT:           Opcode:          I32_CONST
 ; CHECK-NEXT:           Value:           2
 
 ; __table_base
-; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:       - Index:           3
 ; CHECK-NEXT:         Type:            I32
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
@@ -71,7 +84,7 @@
 ; CHECK-NEXT:           Value:           1
 
 ; GOT.mem.global_float
-; CHECK-NEXT:       - Index:           3
+; CHECK-NEXT:       - Index:           4
 ; CHECK-NEXT:         Type:            I32
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
@@ -79,7 +92,7 @@
 ; CHECK-NEXT:           Value:           1024
 
 ; GOT.mem.ret32_ptr
-; CHECK-NEXT:       - Index:           4
+; CHECK-NEXT:       - Index:           5
 ; CHECK-NEXT:         Type:            I32
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
@@ -87,7 +100,7 @@
 ; CHECK-NEXT:           Value:           1032
 
 ; __memory_base
-; CHECK-NEXT:       - Index:           5
+; CHECK-NEXT:       - Index:           6
 ; CHECK-NEXT:         Type:            I32
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
diff --git a/wasm/InputFiles.cpp b/wasm/InputFiles.cpp
index ed9378c..198ac1c 100644
--- a/wasm/InputFiles.cpp
+++ b/wasm/InputFiles.cpp
@@ -165,10 +165,15 @@
   switch (reloc.Type) {
   case R_WASM_TABLE_INDEX_I32:
   case R_WASM_TABLE_INDEX_SLEB:
-  case R_WASM_TABLE_INDEX_REL_SLEB:
+  case R_WASM_TABLE_INDEX_REL_SLEB: {
     if (!getFunctionSymbol(reloc.Index)->hasTableIndex())
       return 0;
-    return getFunctionSymbol(reloc.Index)->getTableIndex();
+    uint32_t index = getFunctionSymbol(reloc.Index)->getTableIndex();
+    if (reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB)
+      index -= config->tableBase;
+    return index;
+
+  }
   case R_WASM_MEMORY_ADDR_SLEB:
   case R_WASM_MEMORY_ADDR_I32:
   case R_WASM_MEMORY_ADDR_LEB:
diff --git a/wasm/Relocations.cpp b/wasm/Relocations.cpp
index ed160a5..018fea9 100644
--- a/wasm/Relocations.cpp
+++ b/wasm/Relocations.cpp
@@ -51,7 +51,7 @@
   if (config->isPic)
     out.importSec->addGOTEntry(sym);
   else
-    out.globalSec->addDummyGOTEntry(sym);
+    out.globalSec->addStaticGOTEntry(sym);
 }
 
 void lld::wasm::scanRelocations(InputChunk *chunk) {
diff --git a/wasm/Symbols.h b/wasm/Symbols.h
index 7ee76d7..787ce7d 100644
--- a/wasm/Symbols.h
+++ b/wasm/Symbols.h
@@ -113,8 +113,6 @@
 
   const WasmSignature* getSignature() const;
 
-  bool isInGOT() const { return gotIndex != INVALID_INDEX; }
-
   uint32_t getGOTIndex() const {
     assert(gotIndex != INVALID_INDEX);
     return gotIndex;
@@ -126,8 +124,9 @@
 protected:
   Symbol(StringRef name, Kind k, uint32_t flags, InputFile *f)
       : name(name), file(f), flags(flags), symbolKind(k),
-        referenced(!config->gcSections), isUsedInRegularObj(false),
-        forceExport(false), canInline(false), traced(false) {}
+        referenced(!config->gcSections), requiresGOT(false),
+        isUsedInRegularObj(false), forceExport(false), canInline(false),
+        traced(false) {}
 
   StringRef name;
   InputFile *file;
@@ -139,6 +138,10 @@
 public:
   bool referenced : 1;
 
+  // True for data symbols that needs a dummy GOT entry.  Used for static
+  // linking of GOT accesses.
+  bool requiresGOT : 1;
+
   // True if the symbol was used for linking and thus need to be added to the
   // output file's symbol table. This is true for all symbols except for
   // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
diff --git a/wasm/SyntheticSections.cpp b/wasm/SyntheticSections.cpp
index c3dd32a..7c3ea32 100644
--- a/wasm/SyntheticSections.cpp
+++ b/wasm/SyntheticSections.cpp
@@ -103,9 +103,9 @@
 
 void ImportSection::addGOTEntry(Symbol *sym) {
   assert(!isSealed);
-  LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n");
   if (sym->hasGOTIndex())
     return;
+  LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n");
   sym->setGOTIndex(numImportedGlobals++);
   gotSymbols.push_back(sym);
 }
@@ -244,15 +244,21 @@
   uint32_t globalIndex = out.importSec->getNumImportedGlobals();
   for (InputGlobal *g : inputGlobals)
     g->setGlobalIndex(globalIndex++);
-  for (Symbol *sym : gotSymbols)
+  for (Symbol *sym : staticGotSymbols)
     sym->setGOTIndex(globalIndex++);
+  isSealed = true;
 }
 
-void GlobalSection::addDummyGOTEntry(Symbol *sym) {
-  LLVM_DEBUG(dbgs() << "addDummyGOTEntry: " << toString(*sym) << "\n");
-  if (sym->hasGOTIndex())
+void GlobalSection::addStaticGOTEntry(Symbol *sym) {
+  assert(!isSealed);
+  if (sym->requiresGOT)
     return;
-  gotSymbols.push_back(sym);
+  LLVM_DEBUG(dbgs() << "addStaticGOTEntry: " << sym->getName() << " "
+                    << toString(sym->kind()) << "\n");
+  sym->requiresGOT = true;
+  if (auto *F = dyn_cast<FunctionSymbol>(sym))
+    out.elemSec->addEntry(F);
+  staticGotSymbols.push_back(sym);
 }
 
 void GlobalSection::writeBody() {
@@ -261,26 +267,27 @@
   writeUleb128(os, numGlobals(), "global count");
   for (InputGlobal *g : inputGlobals)
     writeGlobal(os, g->global);
-  for (const DefinedData *sym : definedFakeGlobals) {
+  for (const Symbol *sym : staticGotSymbols) {
+    WasmGlobal global;
+    global.Type = {WASM_TYPE_I32, false};
+    global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+    if (auto *d = dyn_cast<DefinedData>(sym))
+      global.InitExpr.Value.Int32 = d->getVirtualAddress();
+    else if (auto *f = cast<FunctionSymbol>(sym))
+      global.InitExpr.Value.Int32 = f->getTableIndex();
+    writeGlobal(os, global);
+  }
+  for (const DefinedData *sym : dataAddressGlobals) {
     WasmGlobal global;
     global.Type = {WASM_TYPE_I32, false};
     global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
     global.InitExpr.Value.Int32 = sym->getVirtualAddress();
     writeGlobal(os, global);
   }
-  for (const Symbol *sym : gotSymbols) {
-    WasmGlobal global;
-    global.Type = {WASM_TYPE_I32, false};
-    global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
-    if (auto *d = dyn_cast<DefinedData>(sym))
-      global.InitExpr.Value.Int32 = d->getVirtualAddress();
-    else if (auto *f = cast<DefinedFunction>(sym))
-      global.InitExpr.Value.Int32 = f->getTableIndex();
-    writeGlobal(os, global);
-  }
 }
 
 void GlobalSection::addGlobal(InputGlobal *global) {
+  assert(!isSealed);
   if (!global->live)
     return;
   inputGlobals.push_back(global);
diff --git a/wasm/SyntheticSections.h b/wasm/SyntheticSections.h
index 88dc1fe..1557ce7 100644
--- a/wasm/SyntheticSections.h
+++ b/wasm/SyntheticSections.h
@@ -175,17 +175,23 @@
 public:
   GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {}
   uint32_t numGlobals() const {
-    return inputGlobals.size() + definedFakeGlobals.size() + gotSymbols.size();
+    assert(isSealed);
+    return inputGlobals.size() + dataAddressGlobals.size() +
+           staticGotSymbols.size();
   }
   bool isNeeded() const override { return numGlobals() > 0; }
   void assignIndexes() override;
   void writeBody() override;
   void addGlobal(InputGlobal *global);
-  void addDummyGOTEntry(Symbol *sym);
+  void addDataAddressGlobal(DefinedData *global);
+  void addStaticGOTEntry(Symbol *sym);
 
-  std::vector<const DefinedData *> definedFakeGlobals;
+  std::vector<const DefinedData *> dataAddressGlobals;
+
+protected:
+  bool isSealed = false;
   std::vector<InputGlobal *> inputGlobals;
-  std::vector<Symbol *> gotSymbols;
+  std::vector<Symbol *> staticGotSymbols;
 };
 
 // The event section contains a list of declared wasm events associated with the
diff --git a/wasm/Writer.cpp b/wasm/Writer.cpp
index 1880b3e..d841d21 100644
--- a/wasm/Writer.cpp
+++ b/wasm/Writer.cpp
@@ -509,8 +509,8 @@
     out.exportSec->exports.push_back(
         WasmExport{functionTableName, WASM_EXTERNAL_TABLE, 0});
 
-  unsigned fakeGlobalIndex = out.importSec->getNumImportedGlobals() +
-                             out.globalSec->inputGlobals.size();
+  unsigned globalIndex =
+      out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals();
 
   for (Symbol *sym : symtab->getSymbols()) {
     if (!sym->isExported())
@@ -536,8 +536,8 @@
       export_ = {name, WASM_EXTERNAL_EVENT, e->getEventIndex()};
     } else {
       auto *d = cast<DefinedData>(sym);
-      out.globalSec->definedFakeGlobals.emplace_back(d);
-      export_ = {name, WASM_EXTERNAL_GLOBAL, fakeGlobalIndex++};
+      out.globalSec->dataAddressGlobals.push_back(d);
+      export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
     }
 
     LLVM_DEBUG(dbgs() << "Export: " << name << "\n");
@@ -1035,7 +1035,7 @@
 
   if (errorHandler().verbose) {
     log("Defined Functions: " + Twine(out.functionSec->inputFunctions.size()));
-    log("Defined Globals  : " + Twine(out.globalSec->inputGlobals.size()));
+    log("Defined Globals  : " + Twine(out.globalSec->numGlobals()));
     log("Defined Events   : " + Twine(out.eventSec->inputEvents.size()));
     log("Function Imports : " +
         Twine(out.importSec->getNumImportedFunctions()));