Skip to content

Commit 59f959f

Browse files
committed
[WebAssembly] Relax signature checking for undefined functions that are not called directly
When function signatures don't match and the undefined function is not called directly (i.e. only has its address taken) we don't issue a warning or create a runtime thunk for the undefined function. Instead in this case we simply use the defined version of the function. This is possible since checking signatures of dynamic calls happens at runtime so any invalid usage will still result in a runtime error. This is needed to allow C++ programs to link without generating warnings. Its not uncommon in C++ for vtables to be populated by function address whee the signature of the function is not known in the compilation unit. In this case clang declares the method as void(void) and relies on the vtable caller casting the data back to the correct signature. Fixes: https://bugs.llvm.org/show_bug.cgi?id=40412 Differential Revision: https://reviews.llvm.org/D62153 llvm-svn: 361678
1 parent bede937 commit 59f959f

File tree

7 files changed

+65
-21
lines changed

7 files changed

+65
-21
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o
2+
; RUN: llc -filetype=obj %s -o %t.main.o
3+
; RUN: wasm-ld --fatal-warnings -o %t.wasm %t.ret32.o %t.main.o
4+
; RUN: wasm-ld --fatal-warnings -o %t.wasm %t.main.o %t.ret32.o
5+
6+
target triple = "wasm32-unknown-unknown"
7+
8+
; Function declartion with incorrect signature.
9+
declare dso_local void @ret32()
10+
11+
; Simply taking the address of the function should *not* generate the
12+
; the signature mismatch warning.
13+
@ptr = dso_local global i8* bitcast (void ()* @ret32 to i8*), align 8
14+
15+
define hidden void @_start() local_unnamed_addr {
16+
%addr = load i32 ()*, i32 ()** bitcast (i8** @ptr to i32 ()**), align 8
17+
call i32 %addr()
18+
ret void
19+
}

lld/wasm/Driver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ struct WrappedSymbol {
550550
};
551551

552552
static Symbol *addUndefined(StringRef Name) {
553-
return Symtab->addUndefinedFunction(Name, "", "", 0, nullptr, nullptr);
553+
return Symtab->addUndefinedFunction(Name, "", "", 0, nullptr, nullptr, false);
554554
}
555555

556556
// Handles -wrap option.

lld/wasm/InputFiles.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -271,21 +271,28 @@ void ObjFile::parse(bool IgnoreComdats) {
271271
}
272272
}
273273

274-
// Find the code and data sections. Wasm objects can have at most one code
275-
// and one data section.
276274
uint32_t SectionIndex = 0;
275+
SymbolIsCalledDirectly.resize(WasmObj->getNumberOfSymbols(), false);
277276
for (const SectionRef &Sec : WasmObj->sections()) {
278277
const WasmSection &Section = WasmObj->getWasmSection(Sec);
278+
// Wasm objects can have at most one code and one data section.
279279
if (Section.Type == WASM_SEC_CODE) {
280+
assert(!CodeSection);
280281
CodeSection = &Section;
281282
} else if (Section.Type == WASM_SEC_DATA) {
283+
assert(!DataSection);
282284
DataSection = &Section;
283285
} else if (Section.Type == WASM_SEC_CUSTOM) {
284286
CustomSections.emplace_back(make<InputSection>(Section, this));
285287
CustomSections.back()->setRelocations(Section.Relocations);
286288
CustomSectionsByIndex[SectionIndex] = CustomSections.back();
287289
}
288290
SectionIndex++;
291+
// Scans relocations to dermine determine if a function symbol is called
292+
// directly
293+
for (const WasmRelocation &Reloc : Section.Relocations)
294+
if (Reloc.Type == R_WASM_FUNCTION_INDEX_LEB)
295+
SymbolIsCalledDirectly[Reloc.Index] = true;
289296
}
290297

291298
TypeMap.resize(getWasmObj()->types().size());
@@ -326,10 +333,16 @@ void ObjFile::parse(bool IgnoreComdats) {
326333
Symbols.reserve(WasmObj->getNumberOfSymbols());
327334
for (const SymbolRef &Sym : WasmObj->symbols()) {
328335
const WasmSymbol &WasmSym = WasmObj->getWasmSymbol(Sym.getRawDataRefImpl());
329-
if (Symbol *Sym = createDefined(WasmSym))
330-
Symbols.push_back(Sym);
331-
else
332-
Symbols.push_back(createUndefined(WasmSym));
336+
if (WasmSym.isDefined()) {
337+
// createDefined may fail if the symbol is comdat excluded in which case
338+
// we fall back to creating an undefined symbol
339+
if (Symbol *D = createDefined(WasmSym)) {
340+
Symbols.push_back(D);
341+
continue;
342+
}
343+
}
344+
size_t Idx = Symbols.size();
345+
Symbols.push_back(createUndefined(WasmSym, SymbolIsCalledDirectly[Idx]));
333346
}
334347
}
335348

@@ -361,9 +374,6 @@ DataSymbol *ObjFile::getDataSymbol(uint32_t Index) const {
361374
}
362375

363376
Symbol *ObjFile::createDefined(const WasmSymbol &Sym) {
364-
if (!Sym.isDefined())
365-
return nullptr;
366-
367377
StringRef Name = Sym.Info.Name;
368378
uint32_t Flags = Sym.Info.Flags;
369379

@@ -417,15 +427,15 @@ Symbol *ObjFile::createDefined(const WasmSymbol &Sym) {
417427
llvm_unreachable("unknown symbol kind");
418428
}
419429

420-
Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) {
430+
Symbol *ObjFile::createUndefined(const WasmSymbol &Sym, bool IsCalledDirectly) {
421431
StringRef Name = Sym.Info.Name;
422432
uint32_t Flags = Sym.Info.Flags;
423433

424434
switch (Sym.Info.Kind) {
425435
case WASM_SYMBOL_TYPE_FUNCTION:
426436
return Symtab->addUndefinedFunction(Name, Sym.Info.ImportName,
427437
Sym.Info.ImportModule, Flags, this,
428-
Sym.Signature);
438+
Sym.Signature, IsCalledDirectly);
429439
case WASM_SYMBOL_TYPE_DATA:
430440
return Symtab->addUndefinedData(Name, Flags, this);
431441
case WASM_SYMBOL_TYPE_GLOBAL:
@@ -499,7 +509,7 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
499509
if (ObjSym.isUndefined() || ExcludedByComdat) {
500510
if (ObjSym.isExecutable())
501511
return Symtab->addUndefinedFunction(Name, Name, DefaultModule, Flags, &F,
502-
nullptr);
512+
nullptr, true);
503513
return Symtab->addUndefinedData(Name, Flags, &F);
504514
}
505515

lld/wasm/InputFiles.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ class InputFile {
6969

7070
// List of all symbols referenced or defined by this file.
7171
std::vector<Symbol *> Symbols;
72+
// Bool for each symbol, true if called directly. This allows us to implement
73+
// a weaker form of signature checking where undefined functions that are not
74+
// called directly (i.e. only address taken) don't have to match the defined
75+
// function's signature. We cannot do this for directly called functions
76+
// because those signatures are checked at validation times.
77+
// See https://bugs.llvm.org/show_bug.cgi?id=40412
78+
std::vector<bool> SymbolIsCalledDirectly;
7279

7380
private:
7481
const Kind FileKind;
@@ -138,7 +145,7 @@ class ObjFile : public InputFile {
138145

139146
private:
140147
Symbol *createDefined(const WasmSymbol &Sym);
141-
Symbol *createUndefined(const WasmSymbol &Sym);
148+
Symbol *createUndefined(const WasmSymbol &Sym, bool IsCalledDirectly);
142149

143150
bool isExcludedByComdat(InputChunk *Chunk) const;
144151

lld/wasm/SymbolTable.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,11 @@ Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags,
286286
return S;
287287
}
288288

289-
if (Function && !signatureMatches(ExistingFunction, &Function->Signature)) {
289+
bool CheckSig = true;
290+
if (auto UD = dyn_cast<UndefinedFunction>(ExistingFunction))
291+
CheckSig = UD->IsCalledDirectly;
292+
293+
if (CheckSig && Function && !signatureMatches(ExistingFunction, &Function->Signature)) {
290294
Symbol* Variant;
291295
if (getFunctionVariant(S, &Function->Signature, File, &Variant))
292296
// New variant, always replace
@@ -384,7 +388,8 @@ Symbol *SymbolTable::addDefinedEvent(StringRef Name, uint32_t Flags,
384388
Symbol *SymbolTable::addUndefinedFunction(StringRef Name, StringRef ImportName,
385389
StringRef ImportModule,
386390
uint32_t Flags, InputFile *File,
387-
const WasmSignature *Sig) {
391+
const WasmSignature *Sig,
392+
bool IsCalledDirectly) {
388393
LLVM_DEBUG(dbgs() << "addUndefinedFunction: " << Name <<
389394
" [" << (Sig ? toString(*Sig) : "none") << "]\n");
390395

@@ -396,7 +401,7 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef Name, StringRef ImportName,
396401

397402
auto Replace = [&]() {
398403
replaceSymbol<UndefinedFunction>(S, Name, ImportName, ImportModule, Flags,
399-
File, Sig);
404+
File, Sig, IsCalledDirectly);
400405
};
401406

402407
if (WasInserted)
@@ -409,7 +414,7 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef Name, StringRef ImportName,
409414
reportTypeError(S, File, WASM_SYMBOL_TYPE_FUNCTION);
410415
return S;
411416
}
412-
if (!signatureMatches(ExistingFunction, Sig))
417+
if (IsCalledDirectly && !signatureMatches(ExistingFunction, Sig))
413418
if (getFunctionVariant(S, Sig, File, &S))
414419
Replace();
415420
}

lld/wasm/SymbolTable.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ class SymbolTable {
6363

6464
Symbol *addUndefinedFunction(StringRef Name, StringRef ImportName,
6565
StringRef ImportModule, uint32_t Flags,
66-
InputFile *File, const WasmSignature *Signature);
66+
InputFile *File, const WasmSignature *Signature,
67+
bool IsCalledDirectly);
6768
Symbol *addUndefinedData(StringRef Name, uint32_t Flags, InputFile *File);
6869
Symbol *addUndefinedGlobal(StringRef Name, StringRef ImportName,
6970
StringRef ImportModule, uint32_t Flags,

lld/wasm/Symbols.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,18 @@ class UndefinedFunction : public FunctionSymbol {
194194
UndefinedFunction(StringRef Name, StringRef ImportName,
195195
StringRef ImportModule, uint32_t Flags,
196196
InputFile *File = nullptr,
197-
const WasmSignature *Type = nullptr)
197+
const WasmSignature *Type = nullptr,
198+
bool IsCalledDirectly = true)
198199
: FunctionSymbol(Name, UndefinedFunctionKind, Flags, File, Type),
199-
ImportName(ImportName), ImportModule(ImportModule) {}
200+
ImportName(ImportName), ImportModule(ImportModule), IsCalledDirectly(IsCalledDirectly) {}
200201

201202
static bool classof(const Symbol *S) {
202203
return S->kind() == UndefinedFunctionKind;
203204
}
204205

205206
StringRef ImportName;
206207
StringRef ImportModule;
208+
bool IsCalledDirectly;
207209
};
208210

209211
// Section symbols for output sections are different from those for input

0 commit comments

Comments
 (0)