[WebAssembly] Error on mismatched function signature in final output

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
This commit is contained in:
Sam Clegg 2018-06-21 00:12:25 +00:00
parent 90ae9677e7
commit 1369dfa30b
7 changed files with 46 additions and 16 deletions

View File

@ -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

View File

@ -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"
@ -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

View File

@ -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"
@ -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

View File

@ -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();

View File

@ -34,9 +34,6 @@ using namespace lld;
using namespace lld::wasm;
void lld::wasm::markLive() {
if (!Config->GcSections)
return;
LLVM_DEBUG(dbgs() << "markLive\n");
SmallVector<InputChunk *, 256> Q;
@ -44,6 +41,8 @@ void lld::wasm::markLive() {
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);
};

View File

@ -106,7 +106,7 @@ 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) {
@ -114,12 +114,27 @@ static void checkFunctionType(const Symbol *Existing, const InputFile *File,
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);
}
}

View File

@ -93,11 +93,12 @@ public:
// 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;
@ -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;
}