Index: test/wasm/signature-mismatch.ll
===================================================================
--- /dev/null
+++ test/wasm/signature-mismatch.ll
@@ -0,0 +1,19 @@
+; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -filetype=obj %s -o %t.main.o
+; RUN: not lld -flavor wasm --check-signatures -o %t.wasm %t.main.o %t.ret32.o 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+  %call = tail call i32 @ret32(i32 1, i64 2, i32 3) #2
+  ret void
+}
+
+declare i32 @ret32(i32, i64, i32) local_unnamed_addr #1
+
+; CHECK: error: function signature mismatch: ret32
+; CHECK-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
+; CHECK-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.o
Index: wasm/Config.h
===================================================================
--- wasm/Config.h
+++ wasm/Config.h
@@ -24,6 +24,7 @@
 
 struct Configuration {
   bool AllowUndefined;
+  bool CheckSignatures;
   bool Demangle;
   bool EmitRelocs;
   bool ImportMemory;
Index: wasm/Driver.cpp
===================================================================
--- wasm/Driver.cpp
+++ wasm/Driver.cpp
@@ -153,9 +153,10 @@
 
 // Inject a new undefined symbol into the link.  This will cause the link to
 // fail unless this symbol can be found.
-static void addSyntheticUndefinedFunction(StringRef Name) {
+static void addSyntheticUndefinedFunction(StringRef Name,
+                                          const WasmSignature *Type) {
   log("injecting undefined func: " + Name);
-  Symtab->addUndefinedFunction(Name);
+  Symtab->addUndefinedFunction(Name, Type);
 }
 
 static void printHelp(const char *Argv0) {
@@ -243,6 +244,8 @@
   }
 
   Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
+  Config->CheckSignatures =
+      Args.hasFlag(OPT_check_signatures, OPT_no_check_signatures, false);
   Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
   Config->Entry = Args.getLastArgValue(OPT_entry);
   Config->ImportMemory = Args.hasArg(OPT_import_memory);
@@ -278,7 +281,8 @@
   if (!Config->Relocatable) {
     if (Config->Entry.empty())
       Config->Entry = "_start";
-    addSyntheticUndefinedFunction(Config->Entry);
+    static WasmSignature Signature = { {}, WASM_TYPE_NORESULT };
+    addSyntheticUndefinedFunction(Config->Entry, &Signature);
 
     addSyntheticGlobal("__stack_pointer", 0);
   }
Index: wasm/Options.td
===================================================================
--- wasm/Options.td
+++ wasm/Options.td
@@ -29,11 +29,15 @@
 def no_color_diagnostics: F<"no-color-diagnostics">,
   HelpText<"Do not use colors in diagnostics">;
 
+def no_check_signatures: F<"no-check-signatures">, HelpText<"Don't check function signatures">;
+
 def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
   HelpText<"Path to file to write output">;
 
 def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
 
+def check_signatures: F<"check-signatures">, HelpText<"Check function signatures">;
+
 def v: Flag<["-"], "v">, HelpText<"Display the version number">;
 
 def version: F<"version">, HelpText<"Display the version number and exit">;
Index: wasm/SymbolTable.h
===================================================================
--- wasm/SymbolTable.h
+++ wasm/SymbolTable.h
@@ -18,6 +18,7 @@
 #include "llvm/Support/raw_ostream.h"
 
 using llvm::object::WasmSymbol;
+using llvm::wasm::WasmSignature;
 
 namespace lld {
 namespace wasm {
@@ -51,7 +52,7 @@
   Symbol *addDefined(InputFile *F, const WasmSymbol *Sym,
                      const InputSegment *Segment = nullptr);
   Symbol *addUndefined(InputFile *F, const WasmSymbol *Sym);
-  Symbol *addUndefinedFunction(StringRef Name);
+  Symbol *addUndefinedFunction(StringRef Name, const WasmSignature *Type);
   Symbol *addDefinedGlobal(StringRef Name);
   void addLazy(ArchiveFile *F, const Archive::Symbol *Sym);
 
Index: wasm/SymbolTable.cpp
===================================================================
--- wasm/SymbolTable.cpp
+++ 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;
+
+  // 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;
 
-  bool NewIsFunction = New->Type == WasmSymbol::SymbolType::FUNCTION_EXPORT ||
-                       New->Type == WasmSymbol::SymbolType::FUNCTION_IMPORT;
-  if (Existing->isFunction() == NewIsFunction)
+  DEBUG(dbgs() << "checkSymbolTypes: " << New.Name << "\n");
+  assert(NewSig);
+
+  const WasmSignature &OldSig = Existing.getFunctionType();
+  if (*NewSig == OldSig)
     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());
+  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;
 }
Index: wasm/Symbols.h
===================================================================
--- wasm/Symbols.h
+++ wasm/Symbols.h
@@ -16,6 +16,7 @@
 
 using llvm::object::Archive;
 using llvm::object::WasmSymbol;
+using llvm::wasm::WasmSignature;
 using llvm::wasm::WasmImport;
 using llvm::wasm::WasmExport;
 
@@ -40,7 +41,7 @@
   };
 
   Symbol(StringRef Name, bool IsLocal)
-      : WrittenToSymtab(0), WrittenToNameSec(0), Name(Name), IsLocal(IsLocal) {}
+      : WrittenToSymtab(0), WrittenToNameSec(0), IsLocal(IsLocal), Name(Name) {}
 
   Kind getKind() const { return SymbolKind; }
 
@@ -66,7 +67,8 @@
 
   uint32_t getGlobalIndex() const;
   uint32_t getFunctionIndex() const;
-  uint32_t getFunctionTypeIndex() const;
+
+  const WasmSignature &getFunctionType() const;
   uint32_t getOutputIndex() const;
 
   // Returns the virtual address of a defined global.
@@ -81,7 +83,8 @@
   void setOutputIndex(uint32_t Index);
 
   void update(Kind K, InputFile *F = nullptr, const WasmSymbol *Sym = nullptr,
-              const InputSegment *Segment = nullptr);
+              const InputSegment *Segment = nullptr,
+              const WasmSignature *Sig = nullptr);
 
   void setArchiveSymbol(const Archive::Symbol &Sym) { ArchiveSymbol = Sym; }
   const Archive::Symbol &getArchiveSymbol() { return ArchiveSymbol; }
@@ -92,14 +95,16 @@
   unsigned WrittenToNameSec : 1;
 
 protected:
+  unsigned IsLocal : 1;
+
   StringRef Name;
-  bool IsLocal;
   Archive::Symbol ArchiveSymbol = {nullptr, 0, 0};
   Kind SymbolKind = InvalidKind;
   InputFile *File = nullptr;
   const WasmSymbol *Sym = nullptr;
   const InputSegment *Segment = nullptr;
   llvm::Optional<uint32_t> OutputIndex;
+  const WasmSignature* FunctionType;
 };
 
 } // namespace wasm
Index: wasm/Symbols.cpp
===================================================================
--- wasm/Symbols.cpp
+++ wasm/Symbols.cpp
@@ -31,19 +31,9 @@
   return Sym->ElementIndex;
 }
 
-uint32_t Symbol::getFunctionTypeIndex() const {
-  assert(Sym->isFunction());
-  ObjFile *Obj = cast<ObjFile>(File);
-  if (Obj->isImportedFunction(Sym->ElementIndex)) {
-    const WasmImport &Import = Obj->getWasmObj()->imports()[Sym->ImportIndex];
-    DEBUG(dbgs() << "getFunctionTypeIndex: import: " << Sym->ImportIndex
-                 << " -> " << Import.SigIndex << "\n");
-    return Import.SigIndex;
-  }
-  DEBUG(dbgs() << "getFunctionTypeIndex: non import: " << Sym->ElementIndex
-               << "\n");
-  uint32_t FuntionIndex = Sym->ElementIndex - Obj->NumFunctionImports();
-  return Obj->getWasmObj()->functionTypes()[FuntionIndex];
+const WasmSignature &Symbol::getFunctionType() const {
+  assert(FunctionType != nullptr);
+  return *FunctionType;
 }
 
 uint32_t Symbol::getVirtualAddress() const {
@@ -74,11 +64,12 @@
 }
 
 void Symbol::update(Kind K, InputFile *F, const WasmSymbol *WasmSym,
-                    const InputSegment *Seg) {
+                    const InputSegment *Seg, const WasmSignature *Sig) {
   SymbolKind = K;
   File = F;
   Sym = WasmSym;
   Segment = Seg;
+  FunctionType = Sig;
 }
 
 bool Symbol::isWeak() const { return Sym && Sym->isWeak(); }
Index: wasm/Writer.cpp
===================================================================
--- wasm/Writer.cpp
+++ wasm/Writer.cpp
@@ -35,11 +35,6 @@
 
 namespace {
 
-// Needed for WasmSignatureDenseMapInfo
-bool operator==(const WasmSignature &LHS, const WasmSignature &RHS) {
-  return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes;
-}
-
 // Traits for using WasmSignature in a DenseMap.
 struct WasmSignatureDenseMapInfo {
   static WasmSignature getEmptyKey() {
@@ -72,6 +67,7 @@
 private:
   void openFile();
 
+  uint32_t getTypeIndex(const WasmSignature &Sig);
   void assignSymbolIndexes();
   void calculateImports();
   void calculateOffsets();
@@ -158,8 +154,8 @@
     Import.Module = "env";
     Import.Field = Sym->getName();
     Import.Kind = WASM_EXTERNAL_FUNCTION;
-    auto *Obj = cast<ObjFile>(Sym->getFile());
-    Import.SigIndex = Obj->relocateTypeIndex(Sym->getFunctionTypeIndex());
+    assert(TypeIndices.count(Sym->getFunctionType()) > 0);
+    Import.SigIndex = TypeIndices.lookup(Sym->getFunctionType());
     writeImport(OS, Import);
   }
 
@@ -179,9 +175,6 @@
     Import.Field = Sym->getName();
     Import.Kind = WASM_EXTERNAL_GLOBAL;
     Import.Global.Mutable = false;
-    assert(isa<ObjFile>(Sym->getFile()));
-    // TODO(sbc): Set type of this import
-    // ObjFile* Obj = dyn_cast<ObjFile>(Sym->getFile());
     Import.Global.Type = WASM_TYPE_I32; // Sym->getGlobalType();
     writeImport(OS, Import);
   }
@@ -634,17 +627,18 @@
   }
 }
 
+uint32_t Writer::getTypeIndex(const WasmSignature &Sig) {
+  auto Pair = TypeIndices.insert(std::make_pair(Sig, Types.size()));
+  if (Pair.second)
+    Types.push_back(&Sig);
+  return Pair.first->second;
+}
+
 void Writer::calculateTypes() {
   for (ObjFile *File : Symtab->ObjectFiles) {
     File->TypeMap.reserve(File->getWasmObj()->types().size());
-    for (const WasmSignature &Sig : File->getWasmObj()->types()) {
-      auto Pair = TypeIndices.insert(std::make_pair(Sig, Types.size()));
-      if (Pair.second)
-        Types.push_back(&Sig);
-
-      // Now we map the input files index to the index in the linked output
-      File->TypeMap.push_back(Pair.first->second);
-    }
+    for (const WasmSignature &Sig : File->getWasmObj()->types())
+      File->TypeMap.push_back(getTypeIndex(Sig));
   }
 }
 
Index: wasm/WriterUtils.h
===================================================================
--- wasm/WriterUtils.h
+++ wasm/WriterUtils.h
@@ -16,6 +16,17 @@
 
 using llvm::raw_ostream;
 
+// Needed for WasmSignatureDenseMapInfo
+inline bool operator==(const llvm::wasm::WasmSignature &LHS,
+                       const llvm::wasm::WasmSignature &RHS) {
+  return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes;
+}
+
+inline bool operator!=(const llvm::wasm::WasmSignature &LHS,
+                       const llvm::wasm::WasmSignature &RHS) {
+  return !(LHS == RHS);
+}
+
 namespace lld {
 namespace wasm {
 
@@ -58,6 +69,10 @@
 void writeReloc(raw_ostream &OS, const OutputRelocation &Reloc);
 
 } // namespace wasm
+
+std::string toString(const llvm::wasm::ValType Type);
+std::string toString(const llvm::wasm::WasmSignature &Sig);
+
 } // namespace lld
 
 #endif // LLD_WASM_WRITERUTILS_H
Index: wasm/WriterUtils.cpp
===================================================================
--- wasm/WriterUtils.cpp
+++ wasm/WriterUtils.cpp
@@ -187,3 +187,32 @@
 }
 
 } // namespace lld
+
+std::string lld::toString(ValType Type) {
+  switch (Type) {
+  case ValType::I32:
+    return "I32";
+  case ValType::I64:
+    return "I64";
+  case ValType::F32:
+    return "F32";
+  case ValType::F64:
+    return "F64";
+  }
+  llvm_unreachable("Invalid wasm::ValType");
+}
+
+std::string lld::toString(const WasmSignature &Sig) {
+  SmallString<128> S("(");
+  for (uint32_t Type : Sig.ParamTypes) {
+    if (S.size() != 1)
+      S += ", ";
+    S += toString(static_cast<ValType>(Type));
+  }
+  S += ") -> ";
+  if (Sig.ReturnType == WASM_TYPE_NORESULT)
+    S += "void";
+  else
+    S += toString(static_cast<ValType>(Sig.ReturnType));
+  return S.str();
+}