[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:
parent
90ae9677e7
commit
1369dfa30b
|
@ -9,6 +9,11 @@
|
||||||
target triple = "wasm32-unknown-unknown"
|
target triple = "wasm32-unknown-unknown"
|
||||||
|
|
||||||
define hidden void @_start() local_unnamed_addr #0 {
|
define hidden void @_start() local_unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @_call_ret32() local_unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%call = tail call i32 @ret32(i32 1, i64 2, i32 3) #2
|
%call = tail call i32 @ret32(i32 1, i64 2, i32 3) #2
|
||||||
ret void
|
ret void
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
; RUN: llc -filetype=obj %p/Inputs/weak-symbol1.ll -o %t.weak.o
|
; 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 %p/Inputs/strong-symbol.ll -o %t.strong.o
|
||||||
; RUN: llc -filetype=obj %s -o %t.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"
|
target triple = "wasm32-unknown-unknown"
|
||||||
|
|
||||||
|
@ -16,3 +16,4 @@ entry:
|
||||||
; CHECK: warning: function signature mismatch: weakFn
|
; CHECK: warning: function signature mismatch: weakFn
|
||||||
; CHECK-NEXT: >>> defined as () -> I32 in {{.*}}signature-mismatch-weak.ll.tmp.o
|
; 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-NEXT: >>> defined as () -> I64 in {{.*}}signature-mismatch-weak.ll.tmp.strong.o
|
||||||
|
; CHECK: error: function signature mismatch: weakFn
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
|
; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
|
||||||
; RUN: llc -filetype=obj %s -o %t.main.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
|
; 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
|
; the check works when the undefined symbol is resolved by an existing defined
|
||||||
; one.
|
; 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"
|
target triple = "wasm32-unknown-unknown"
|
||||||
|
|
||||||
|
@ -17,10 +18,16 @@ entry:
|
||||||
|
|
||||||
declare i32 @ret32(i32, i64, i32) local_unnamed_addr #1
|
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 (I32, I64, I32) -> I32 in {{.*}}.main.o
|
||||||
; CHECK-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.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 (F32) -> I32 in {{.*}}.ret32.o
|
||||||
; REVERSE-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
|
; REVERSE-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
|
||||||
|
; REVERSE: error: function signature mismatch: ret32
|
||||||
|
|
|
@ -483,7 +483,8 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Do size optimizations: garbage collection
|
// Do size optimizations: garbage collection
|
||||||
markLive();
|
if (Config->GcSections)
|
||||||
|
markLive();
|
||||||
|
|
||||||
// Write the result to the file.
|
// Write the result to the file.
|
||||||
writeResult();
|
writeResult();
|
||||||
|
|
|
@ -34,9 +34,6 @@ using namespace lld;
|
||||||
using namespace lld::wasm;
|
using namespace lld::wasm;
|
||||||
|
|
||||||
void lld::wasm::markLive() {
|
void lld::wasm::markLive() {
|
||||||
if (!Config->GcSections)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LLVM_DEBUG(dbgs() << "markLive\n");
|
LLVM_DEBUG(dbgs() << "markLive\n");
|
||||||
SmallVector<InputChunk *, 256> Q;
|
SmallVector<InputChunk *, 256> Q;
|
||||||
|
|
||||||
|
@ -44,6 +41,8 @@ void lld::wasm::markLive() {
|
||||||
if (!Sym || Sym->isLive())
|
if (!Sym || Sym->isLive())
|
||||||
return;
|
return;
|
||||||
Sym->markLive();
|
Sym->markLive();
|
||||||
|
if (Sym->SignatureMismatch)
|
||||||
|
error("function signature mismatch: " + Sym->getName());
|
||||||
if (InputChunk *Chunk = Sym->getChunk())
|
if (InputChunk *Chunk = Sym->getChunk())
|
||||||
Q.push_back(Chunk);
|
Q.push_back(Chunk);
|
||||||
};
|
};
|
||||||
|
|
|
@ -106,7 +106,7 @@ static void reportTypeError(const Symbol *Existing, const InputFile *File,
|
||||||
" in " + toString(File));
|
" in " + toString(File));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkFunctionType(const Symbol *Existing, const InputFile *File,
|
static void checkFunctionType(Symbol *Existing, const InputFile *File,
|
||||||
const WasmSignature *NewSig) {
|
const WasmSignature *NewSig) {
|
||||||
auto ExistingFunction = dyn_cast<FunctionSymbol>(Existing);
|
auto ExistingFunction = dyn_cast<FunctionSymbol>(Existing);
|
||||||
if (!ExistingFunction) {
|
if (!ExistingFunction) {
|
||||||
|
@ -114,12 +114,27 @@ static void checkFunctionType(const Symbol *Existing, const InputFile *File,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const WasmSignature *OldSig = ExistingFunction->getFunctionType();
|
const WasmSignature *OldSig = ExistingFunction->getFunctionType();
|
||||||
if (OldSig && NewSig && *NewSig != *OldSig) {
|
if (OldSig && NewSig && *NewSig != *OldSig) {
|
||||||
warn("function signature mismatch: " + Existing->getName() +
|
// Don't generate more than one warning per symbol.
|
||||||
"\n>>> defined as " + toString(*OldSig) + " in " +
|
if (Existing->SignatureMismatch)
|
||||||
toString(Existing->getFile()) + "\n>>> defined as " +
|
return;
|
||||||
toString(*NewSig) + " in " + toString(File));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,11 +93,12 @@ public:
|
||||||
|
|
||||||
// True if this symbol was referenced by a regular (non-bitcode) object.
|
// True if this symbol was referenced by a regular (non-bitcode) object.
|
||||||
unsigned IsUsedInRegularObj : 1;
|
unsigned IsUsedInRegularObj : 1;
|
||||||
|
unsigned SignatureMismatch : 1;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
|
Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
|
||||||
: IsUsedInRegularObj(false), Name(Name), SymbolKind(K), Flags(Flags),
|
: IsUsedInRegularObj(false), SignatureMismatch(false), Name(Name),
|
||||||
File(F), Referenced(!Config->GcSections) {}
|
SymbolKind(K), Flags(Flags), File(F), Referenced(!Config->GcSections) {}
|
||||||
|
|
||||||
StringRef Name;
|
StringRef Name;
|
||||||
Kind SymbolKind;
|
Kind SymbolKind;
|
||||||
|
@ -340,6 +341,7 @@ T *replaceSymbol(Symbol *S, ArgT &&... Arg) {
|
||||||
|
|
||||||
T *S2 = new (S) T(std::forward<ArgT>(Arg)...);
|
T *S2 = new (S) T(std::forward<ArgT>(Arg)...);
|
||||||
S2->IsUsedInRegularObj = SymCopy.IsUsedInRegularObj;
|
S2->IsUsedInRegularObj = SymCopy.IsUsedInRegularObj;
|
||||||
|
S2->SignatureMismatch = SymCopy.SignatureMismatch;
|
||||||
return S2;
|
return S2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue