Skip to content

Commit

Permalink
[WebAssembly] Error on mismatched function signature in final output
Browse files Browse the repository at this point in the history
During symbol resolution, emit warnings for function signature
mismatches.  During GC, if any mismatched symbol is marked as live
then generate an error.

This means that we only error out if the mismatch is written to the
final output.  i.e. if we would generate an invalid wasm file.

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

llvm-svn: 335192
  • Loading branch information
sbc100 committed Jun 21, 2018
1 parent 90ae967 commit 1369dfa
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 16 deletions.
5 changes: 5 additions & 0 deletions lld/test/wasm/fatal-warnings.ll
Expand Up @@ -9,6 +9,11 @@
target triple = "wasm32-unknown-unknown"

define hidden void @_start() local_unnamed_addr #0 {
entry:
ret void
}

define hidden void @_call_ret32() local_unnamed_addr #0 {
entry:
%call = tail call i32 @ret32(i32 1, i64 2, i32 3) #2
ret void
Expand Down
3 changes: 2 additions & 1 deletion lld/test/wasm/signature-mismatch-weak.ll
@@ -1,7 +1,7 @@
; RUN: llc -filetype=obj %p/Inputs/weak-symbol1.ll -o %t.weak.o
; RUN: llc -filetype=obj %p/Inputs/strong-symbol.ll -o %t.strong.o
; RUN: llc -filetype=obj %s -o %t.o
; RUN: wasm-ld -o %t.wasm %t.o %t.strong.o %t.weak.o 2>&1 | FileCheck %s
; RUN: not wasm-ld -o %t.wasm %t.o %t.strong.o %t.weak.o 2>&1 | FileCheck %s

target triple = "wasm32-unknown-unknown"

Expand All @@ -16,3 +16,4 @@ entry:
; CHECK: warning: function signature mismatch: weakFn
; CHECK-NEXT: >>> defined as () -> I32 in {{.*}}signature-mismatch-weak.ll.tmp.o
; CHECK-NEXT: >>> defined as () -> I64 in {{.*}}signature-mismatch-weak.ll.tmp.strong.o
; CHECK: error: function signature mismatch: weakFn
15 changes: 11 additions & 4 deletions lld/test/wasm/signature-mismatch.ll
@@ -1,10 +1,11 @@
; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
; RUN: llc -filetype=obj %s -o %t.main.o
; RUN: not wasm-ld --fatal-warnings -o %t.wasm %t.main.o %t.ret32.o 2>&1 | FileCheck %s
; RUN: not wasm-ld -o %t.wasm %t.main.o %t.ret32.o 2>&1 | FileCheck %s
; RUN: not wasm-ld --no-gc-sections -o %t.wasm %t.main.o %t.ret32.o 2>&1 | FileCheck %s -check-prefix=NO-GC
; Run the test again by with the object files in the other order to verify
; the check works when the undefined symbol is resolved by an existing defined
; one.
; RUN: not wasm-ld --fatal-warnings -o %t.wasm %t.ret32.o %t.main.o 2>&1 | FileCheck %s -check-prefix=REVERSE
; RUN: not wasm-ld -o %t.wasm %t.ret32.o %t.main.o 2>&1 | FileCheck %s -check-prefix=REVERSE

target triple = "wasm32-unknown-unknown"

Expand All @@ -17,10 +18,16 @@ entry:

declare i32 @ret32(i32, i64, i32) local_unnamed_addr #1

; CHECK: error: function signature mismatch: ret32
; CHECK: warning: function signature mismatch: ret32
; CHECK-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
; CHECK-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.o
; CHECK: error: function signature mismatch: ret32

; REVERSE: error: function signature mismatch: ret32
; NO-GC: error: function signature mismatch: ret32
; NO-GC-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
; NO-GC-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.o

; REVERSE: warning: function signature mismatch: ret32
; REVERSE-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.o
; REVERSE-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
; REVERSE: error: function signature mismatch: ret32
3 changes: 2 additions & 1 deletion lld/wasm/Driver.cpp
Expand Up @@ -483,7 +483,8 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;

// Do size optimizations: garbage collection
markLive();
if (Config->GcSections)
markLive();

// Write the result to the file.
writeResult();
Expand Down
5 changes: 2 additions & 3 deletions lld/wasm/MarkLive.cpp
Expand Up @@ -34,16 +34,15 @@ using namespace lld;
using namespace lld::wasm;

void lld::wasm::markLive() {
if (!Config->GcSections)
return;

LLVM_DEBUG(dbgs() << "markLive\n");
SmallVector<InputChunk *, 256> Q;

auto Enqueue = [&](Symbol *Sym) {
if (!Sym || Sym->isLive())
return;
Sym->markLive();
if (Sym->SignatureMismatch)
error("function signature mismatch: " + Sym->getName());
if (InputChunk *Chunk = Sym->getChunk())
Q.push_back(Chunk);
};
Expand Down
25 changes: 20 additions & 5 deletions lld/wasm/SymbolTable.cpp
Expand Up @@ -106,20 +106,35 @@ static void reportTypeError(const Symbol *Existing, const InputFile *File,
" in " + toString(File));
}

static void checkFunctionType(const Symbol *Existing, const InputFile *File,
static void checkFunctionType(Symbol *Existing, const InputFile *File,
const WasmSignature *NewSig) {
auto ExistingFunction = dyn_cast<FunctionSymbol>(Existing);
if (!ExistingFunction) {
reportTypeError(Existing, File, WASM_SYMBOL_TYPE_FUNCTION);
return;
}


const WasmSignature *OldSig = ExistingFunction->getFunctionType();
if (OldSig && NewSig && *NewSig != *OldSig) {
warn("function signature mismatch: " + Existing->getName() +
"\n>>> defined as " + toString(*OldSig) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " +
toString(*NewSig) + " in " + toString(File));
// Don't generate more than one warning per symbol.
if (Existing->SignatureMismatch)
return;
Existing->SignatureMismatch = true;

std::string msg = ("function signature mismatch: " + Existing->getName() +
"\n>>> defined as " + toString(*OldSig) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " +
toString(*NewSig) + " in " + toString(File))
.str();
// A function signature mismatch is only really problem if the mismatched
// symbol is included in the final output, and gc-sections can remove the
// offending uses. Therefore we delay reporting this as an error when
// section GC is enabled.
if (Config->GcSections)
warn(msg);
else
error(msg);
}
}

Expand Down
6 changes: 4 additions & 2 deletions lld/wasm/Symbols.h
Expand Up @@ -93,11 +93,12 @@ class Symbol {

// True if this symbol was referenced by a regular (non-bitcode) object.
unsigned IsUsedInRegularObj : 1;
unsigned SignatureMismatch : 1;

protected:
Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
: IsUsedInRegularObj(false), Name(Name), SymbolKind(K), Flags(Flags),
File(F), Referenced(!Config->GcSections) {}
: IsUsedInRegularObj(false), SignatureMismatch(false), Name(Name),
SymbolKind(K), Flags(Flags), File(F), Referenced(!Config->GcSections) {}

StringRef Name;
Kind SymbolKind;
Expand Down Expand Up @@ -340,6 +341,7 @@ T *replaceSymbol(Symbol *S, ArgT &&... Arg) {

T *S2 = new (S) T(std::forward<ArgT>(Arg)...);
S2->IsUsedInRegularObj = SymCopy.IsUsedInRegularObj;
S2->SignatureMismatch = SymCopy.SignatureMismatch;
return S2;
}

Expand Down

0 comments on commit 1369dfa

Please sign in to comment.