[WebAssembly] Allow function signature checking at link time

This change allows checking of function signatures but
does not yes enable it by default.  In this mode, linking
two objects that were compiled with a different signatures
for the same function will produce a link error.

New options for enabling and disabling this feature have been
added: (--check-signatures/--no-check-signatures).

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

llvm-svn: 319396
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 72f6bdd..ea74b65 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -11,6 +11,7 @@
 
 #include "Config.h"
 #include "Strings.h"
+#include "WriterUtils.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
 
@@ -76,20 +77,59 @@
         toString(NewFile));
 }
 
-static void checkSymbolTypes(Symbol *Existing, InputFile *F,
-                             const WasmSymbol *New) {
-  if (Existing->isLazy())
+// Get the signature for a given function symbol, either by looking
+// it up in function sections (for defined functions), of the imports section
+// (for imported functions).
+static const WasmSignature *getFunctionSig(const ObjFile &Obj,
+                                           const WasmSymbol &Sym) {
+  DEBUG(dbgs() << "getFunctionSig: " << Sym.Name << "\n");
+  const WasmObjectFile *WasmObj = Obj.getWasmObj();
+  uint32_t FunctionType;
+  if (Obj.isImportedFunction(Sym.ElementIndex)) {
+    const WasmImport &Import = WasmObj->imports()[Sym.ImportIndex];
+    FunctionType = Import.SigIndex;
+  } else {
+    uint32_t FuntionIndex = Sym.ElementIndex - Obj.NumFunctionImports();
+    FunctionType = WasmObj->functionTypes()[FuntionIndex];
+  }
+  return &WasmObj->types()[FunctionType];
+}
+
+// Check the type of new symbol matches that of the symbol is replacing.
+// For functions this can also involve verifying that the signatures match.
+static void checkSymbolTypes(const Symbol &Existing, const InputFile &F,
+                             const WasmSymbol &New,
+                             const WasmSignature *NewSig) {
+  if (Existing.isLazy())
     return;
 
-  bool NewIsFunction = New->Type == WasmSymbol::SymbolType::FUNCTION_EXPORT ||
-                       New->Type == WasmSymbol::SymbolType::FUNCTION_IMPORT;
-  if (Existing->isFunction() == NewIsFunction)
+  bool NewIsFunction = New.Type == WasmSymbol::SymbolType::FUNCTION_EXPORT ||
+                       New.Type == WasmSymbol::SymbolType::FUNCTION_IMPORT;
+
+  // First check the symbol types match (i.e. either both are function
+  // symbols or both are data symbols).
+  if (Existing.isFunction() != NewIsFunction) {
+    error("symbol type mismatch: " + New.Name + "\n>>> defined as " +
+          (Existing.isFunction() ? "Function" : "Global") + " in " +
+          toString(Existing.getFile()) + "\n>>> defined as " +
+          (NewIsFunction ? "Function" : "Global") + " in " + F.getName());
+    return;
+  }
+
+  // For function symbols, optionally check the function signature matches too.
+  if (!NewIsFunction || !Config->CheckSignatures)
     return;
 
-  error("symbol type mismatch: " + New->Name + "\n>>> defined as " +
-        (Existing->isFunction() ? "Function" : "Global") + " in " +
-        toString(Existing->getFile()) + "\n>>> defined as " +
-        (NewIsFunction ? "Function" : "Global") + " in " + F->getName());
+  DEBUG(dbgs() << "checkSymbolTypes: " << New.Name << "\n");
+  assert(NewSig);
+
+  const WasmSignature &OldSig = Existing.getFunctionType();
+  if (*NewSig == OldSig)
+    return;
+
+  error("function signature mismatch: " + New.Name + "\n>>> defined as " +
+        toString(OldSig) + " in " + toString(Existing.getFile()) +
+        "\n>>> defined as " + toString(*NewSig) + " in " + F.getName());
 }
 
 Symbol *SymbolTable::addDefinedGlobal(StringRef Name) {
@@ -110,19 +150,27 @@
   Symbol *S;
   bool WasInserted;
   Symbol::Kind Kind = Symbol::DefinedFunctionKind;
+  const WasmSignature *NewSig = nullptr;
   if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_EXPORT)
     Kind = Symbol::DefinedGlobalKind;
+  else
+    NewSig = getFunctionSig(*cast<ObjFile>(F), *Sym);
 
   std::tie(S, WasInserted) = insert(Sym->Name);
   if (WasInserted) {
-    S->update(Kind, F, Sym, Segment);
+    S->update(Kind, F, Sym, Segment, NewSig);
+  } else if (S->isLazy()) {
+    // The existing symbol is lazy. Replace it without checking types since
+    // lazy symbols don't have any type information.
+    DEBUG(dbgs() << "replacing existing lazy symbol: " << Sym->Name << "\n");
+    S->update(Kind, F, Sym, Segment, NewSig);
   } else if (!S->isDefined()) {
     // The existing symbol table entry is undefined. The new symbol replaces
-    // it
+    // it, after checkign the type matches
     DEBUG(dbgs() << "resolving existing undefined symbol: " << Sym->Name
                  << "\n");
-    checkSymbolTypes(S, F, Sym);
-    S->update(Kind, F, Sym, Segment);
+    checkSymbolTypes(*S, *F, *Sym, NewSig);
+    S->update(Kind, F, Sym, Segment, NewSig);
   } else if (Sym->isWeak()) {
     // the new symbol is weak we can ignore it
     DEBUG(dbgs() << "existing symbol takes precensence\n");
@@ -130,7 +178,8 @@
     // the new symbol is not weak and the existing symbol is, so we replace
     // it
     DEBUG(dbgs() << "replacing existing weak symbol\n");
-    S->update(Kind, F, Sym, Segment);
+    checkSymbolTypes(*S, *F, *Sym, NewSig);
+    S->update(Kind, F, Sym, Segment, NewSig);
   } else {
     // niether symbol is week. They conflict.
     reportDuplicate(S, F);
@@ -138,14 +187,16 @@
   return S;
 }
 
-Symbol *SymbolTable::addUndefinedFunction(StringRef Name) {
+Symbol *SymbolTable::addUndefinedFunction(StringRef Name,
+                                          const WasmSignature *Type) {
   Symbol *S;
   bool WasInserted;
   std::tie(S, WasInserted) = insert(Name);
-  if (WasInserted)
-    S->update(Symbol::UndefinedFunctionKind);
-  else if (!S->isFunction())
+  if (WasInserted) {
+    S->update(Symbol::UndefinedFunctionKind, nullptr, nullptr, nullptr, Type);
+  } else if (!S->isFunction()) {
     error("symbol type mismatch: " + Name);
+  }
   return S;
 }
 
@@ -154,18 +205,21 @@
   Symbol *S;
   bool WasInserted;
   Symbol::Kind Kind = Symbol::UndefinedFunctionKind;
+  const WasmSignature *NewSig = nullptr;
   if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_IMPORT)
     Kind = Symbol::UndefinedGlobalKind;
+  else
+    NewSig = getFunctionSig(*cast<ObjFile>(F), *Sym);
   std::tie(S, WasInserted) = insert(Sym->Name);
   if (WasInserted) {
-    S->update(Kind, F, Sym);
+    S->update(Kind, F, Sym, nullptr, NewSig);
   } else if (S->isLazy()) {
     DEBUG(dbgs() << "resolved by existing lazy\n");
     auto *AF = cast<ArchiveFile>(S->getFile());
     AF->addMember(&S->getArchiveSymbol());
   } else if (S->isDefined()) {
     DEBUG(dbgs() << "resolved by existing\n");
-    checkSymbolTypes(S, F, Sym);
+    checkSymbolTypes(*S, *F, *Sym, NewSig);
   }
   return S;
 }