diff --git a/lld/test/wasm/signature-mismatch-unknown.ll b/lld/test/wasm/signature-mismatch-unknown.ll new file mode 100644 --- /dev/null +++ b/lld/test/wasm/signature-mismatch-unknown.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: wasm-ld --fatal-warnings -o %t.wasm %t.ret32.o %t.main.o +; RUN: wasm-ld --fatal-warnings -o %t.wasm %t.main.o %t.ret32.o + +target triple = "wasm32-unknown-unknown" + +; Function declartion with incorrect signature. +declare dso_local void @ret32() + +; Simply taking the address of the function should *not* generate the +; the signature mismatch warning. +@ptr = dso_local global i8* bitcast (void ()* @ret32 to i8*), align 8 + +define hidden void @_start() local_unnamed_addr { + %addr = load i32 ()*, i32 ()** bitcast (i8** @ptr to i32 ()**), align 8 + call i32 %addr() + ret void +} diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -69,6 +69,13 @@ // List of all symbols referenced or defined by this file. std::vector Symbols; + // Bool for each symbol, true if called directly. This allows us to implement + // a weaker form of signature checking where undefined functions that are not + // called directly (i.e. only address taken) don't have to match the defined + // function's signature. We cannot do this for directly called functions + // because those signatures are checked at validation times. + // See https://bugs.llvm.org/show_bug.cgi?id=40412 + std::vector SymbolIsCalledDirectly; private: const Kind FileKind; @@ -138,7 +145,7 @@ private: Symbol *createDefined(const WasmSymbol &Sym); - Symbol *createUndefined(const WasmSymbol &Sym); + Symbol *createUndefined(const WasmSymbol &Sym, bool IsCalledDirectly); bool isExcludedByComdat(InputChunk *Chunk) const; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -271,14 +271,16 @@ } } - // Find the code and data sections. Wasm objects can have at most one code - // and one data section. uint32_t SectionIndex = 0; + SymbolIsCalledDirectly.resize(WasmObj->getNumberOfSymbols(), false); for (const SectionRef &Sec : WasmObj->sections()) { const WasmSection &Section = WasmObj->getWasmSection(Sec); + // Wasm objects can have at most one code and one data section. if (Section.Type == WASM_SEC_CODE) { + assert(!CodeSection); CodeSection = &Section; } else if (Section.Type == WASM_SEC_DATA) { + assert(!DataSection); DataSection = &Section; } else if (Section.Type == WASM_SEC_CUSTOM) { CustomSections.emplace_back(make(Section, this)); @@ -286,6 +288,11 @@ CustomSectionsByIndex[SectionIndex] = CustomSections.back(); } SectionIndex++; + // Scans relocations to dermine determine if a function symbol is called + // directly + for (const WasmRelocation &Reloc : Section.Relocations) + if (Reloc.Type == R_WASM_FUNCTION_INDEX_LEB) + SymbolIsCalledDirectly[Reloc.Index] = true; } TypeMap.resize(getWasmObj()->types().size()); @@ -326,10 +333,16 @@ Symbols.reserve(WasmObj->getNumberOfSymbols()); for (const SymbolRef &Sym : WasmObj->symbols()) { const WasmSymbol &WasmSym = WasmObj->getWasmSymbol(Sym.getRawDataRefImpl()); - if (Symbol *Sym = createDefined(WasmSym)) - Symbols.push_back(Sym); - else - Symbols.push_back(createUndefined(WasmSym)); + if (WasmSym.isDefined()) { + // createDefined may fail if the symbol is comdat excluded in which case + // we fall back to creating an undefined symbol + if (Symbol *D = createDefined(WasmSym)) { + Symbols.push_back(D); + continue; + } + } + size_t Idx = Symbols.size(); + Symbols.push_back(createUndefined(WasmSym, SymbolIsCalledDirectly[Idx])); } } @@ -361,9 +374,6 @@ } Symbol *ObjFile::createDefined(const WasmSymbol &Sym) { - if (!Sym.isDefined()) - return nullptr; - StringRef Name = Sym.Info.Name; uint32_t Flags = Sym.Info.Flags; @@ -417,7 +427,7 @@ llvm_unreachable("unknown symbol kind"); } -Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) { +Symbol *ObjFile::createUndefined(const WasmSymbol &Sym, bool IsCalledDirectly) { StringRef Name = Sym.Info.Name; uint32_t Flags = Sym.Info.Flags; @@ -425,7 +435,7 @@ case WASM_SYMBOL_TYPE_FUNCTION: return Symtab->addUndefinedFunction(Name, Sym.Info.ImportName, Sym.Info.ImportModule, Flags, this, - Sym.Signature); + Sym.Signature, IsCalledDirectly); case WASM_SYMBOL_TYPE_DATA: return Symtab->addUndefinedData(Name, Flags, this); case WASM_SYMBOL_TYPE_GLOBAL: @@ -499,7 +509,7 @@ if (ObjSym.isUndefined() || ExcludedByComdat) { if (ObjSym.isExecutable()) return Symtab->addUndefinedFunction(Name, Name, DefaultModule, Flags, &F, - nullptr); + nullptr, true); return Symtab->addUndefinedData(Name, Flags, &F); } diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -63,7 +63,8 @@ Symbol *addUndefinedFunction(StringRef Name, StringRef ImportName, StringRef ImportModule, uint32_t Flags, - InputFile *File, const WasmSignature *Signature); + InputFile *File, const WasmSignature *Signature, + bool IsCalledDirectly); Symbol *addUndefinedData(StringRef Name, uint32_t Flags, InputFile *File); Symbol *addUndefinedGlobal(StringRef Name, StringRef ImportName, StringRef ImportModule, uint32_t Flags, diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -286,7 +286,11 @@ return S; } - if (Function && !signatureMatches(ExistingFunction, &Function->Signature)) { + bool CheckSig = true; + if (auto UD = dyn_cast(ExistingFunction)) + CheckSig = UD->IsCalledDirectly; + + if (CheckSig && Function && !signatureMatches(ExistingFunction, &Function->Signature)) { Symbol* Variant; if (getFunctionVariant(S, &Function->Signature, File, &Variant)) // New variant, always replace @@ -384,7 +388,8 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef Name, StringRef ImportName, StringRef ImportModule, uint32_t Flags, InputFile *File, - const WasmSignature *Sig) { + const WasmSignature *Sig, + bool IsCalledDirectly) { LLVM_DEBUG(dbgs() << "addUndefinedFunction: " << Name << " [" << (Sig ? toString(*Sig) : "none") << "]\n"); @@ -396,7 +401,7 @@ auto Replace = [&]() { replaceSymbol(S, Name, ImportName, ImportModule, Flags, - File, Sig); + File, Sig, IsCalledDirectly); }; if (WasInserted) @@ -409,7 +414,7 @@ reportTypeError(S, File, WASM_SYMBOL_TYPE_FUNCTION); return S; } - if (!signatureMatches(ExistingFunction, Sig)) + if (IsCalledDirectly && !signatureMatches(ExistingFunction, Sig)) if (getFunctionVariant(S, Sig, File, &S)) Replace(); } diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -194,9 +194,10 @@ UndefinedFunction(StringRef Name, StringRef ImportName, StringRef ImportModule, uint32_t Flags, InputFile *File = nullptr, - const WasmSignature *Type = nullptr) + const WasmSignature *Type = nullptr, + bool IsCalledDirectly = true) : FunctionSymbol(Name, UndefinedFunctionKind, Flags, File, Type), - ImportName(ImportName), ImportModule(ImportModule) {} + ImportName(ImportName), ImportModule(ImportModule), IsCalledDirectly(IsCalledDirectly) {} static bool classof(const Symbol *S) { return S->kind() == UndefinedFunctionKind; @@ -204,6 +205,7 @@ StringRef ImportName; StringRef ImportModule; + bool IsCalledDirectly; }; // Section symbols for output sections are different from those for input