[WebAssembly] Add explicit symbol table
This change modified lld to in response the llvm change which
moved to a more explicit symbol table in the object format.
Based on patches by Nicholas Wilson:
1. https://reviews.llvm.org/D41955
2. https://reviews.llvm.org/D42585
The primary difference that we see in the test output is that
for relocatable (-r) output we now have symbol table which
replaces exports/imports and globals.
See: https://github.com/WebAssembly/tool-conventions/issues/38
Differential Revision: https://reviews.llvm.org/D43264
llvm-svn: 325861
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 094b566..d8d7be9 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -10,6 +10,7 @@
#include "SymbolTable.h"
#include "Config.h"
#include "InputChunks.h"
+#include "InputGlobal.h"
#include "WriterUtils.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
@@ -73,50 +74,58 @@
// Check the type of new symbol matches that of the symbol is replacing.
// For functions this can also involve verifying that the signatures match.
static void checkSymbolTypes(const Symbol &Existing, const InputFile &F,
- bool NewIsFunction, const WasmSignature *NewSig) {
+ WasmSymbolType NewType,
+ const WasmSignature *NewFunctionSig,
+ const WasmGlobalType *NewGlobalType) {
if (Existing.isLazy())
return;
+ WasmSymbolType ExistingType = Existing.getWasmType();
+
// First check the symbol types match (i.e. either both are function
// symbols or both are data symbols).
- if (isa<FunctionSymbol>(Existing) != NewIsFunction) {
+ if (NewType != ExistingType) {
error("symbol type mismatch: " + Existing.getName() + "\n>>> defined as " +
- (isa<FunctionSymbol>(Existing) ? "Function" : "Data") + " in " +
- toString(Existing.getFile()) + "\n>>> defined as " +
- (NewIsFunction ? "Function" : "Data") + " in " + F.getName());
+ toString(ExistingType) + " in " + toString(Existing.getFile()) +
+ "\n>>> defined as " + toString(NewType) + " in " + F.getName());
return;
}
- // For function symbols, optionally check the function signature matches too.
- auto *ExistingFunc = dyn_cast<FunctionSymbol>(&Existing);
- if (!ExistingFunc || !Config->CheckSignatures)
+ // For function/global symbols, optionally check the type matches too.
+ if (NewType == WASM_SYMBOL_TYPE_DATA || !Config->CheckSignatures)
return;
- const WasmSignature *OldSig = ExistingFunc->getFunctionType();
+ DEBUG(dbgs() << "checkSymbolTypes: " << Existing.getName() << "\n");
- // Skip the signature check if the existing function has no signature (e.g.
- // if it is an undefined symbol generated by --undefined command line flag).
- if (OldSig == nullptr)
- return;
+ auto ReportError = [&](const Twine &Old, const Twine &New) {
+ error(toString(NewType) + " type mismatch: " + Existing.getName() +
+ "\n>>> defined as " + Old + " in " + toString(Existing.getFile()) +
+ "\n>>> defined as " + New + " in " + F.getName());
+ };
- DEBUG(dbgs() << "checkSymbolTypes: " << ExistingFunc->getName() << "\n");
- assert(NewSig);
+ if (NewType == WASM_SYMBOL_TYPE_FUNCTION) {
+ // Skip the signature check if the existing function has no signature (e.g.
+ // if it is an undefined symbol generated by --undefined command line flag).
+ auto &Sym = cast<FunctionSymbol>(Existing);
+ const WasmSignature *OldSig = Sym.getFunctionType();
+ if (!OldSig)
+ return;
- if (*NewSig == *OldSig)
- return;
+ assert(NewFunctionSig);
+ if (*NewFunctionSig == *OldSig)
+ return;
- error("function signature mismatch: " + ExistingFunc->getName() +
- "\n>>> defined as " + toString(*OldSig) + " in " +
- toString(ExistingFunc->getFile()) + "\n>>> defined as " +
- toString(*NewSig) + " in " + F.getName());
-}
+ ReportError(toString(*OldSig), toString(*NewFunctionSig));
+ } else {
+ auto &Sym = cast<GlobalSymbol>(Existing);
-static void checkSymbolTypes(const Symbol &Existing, const InputFile &F,
- bool IsFunction, const InputChunk *Chunk) {
- const WasmSignature *Sig = nullptr;
- if (auto *F = dyn_cast_or_null<InputFunction>(Chunk))
- Sig = &F->Signature;
- return checkSymbolTypes(Existing, F, IsFunction, Sig);
+ assert(NewGlobalType != nullptr);
+ const WasmGlobalType *OldType = Sym.getGlobalType();
+ if (*NewGlobalType == *OldType)
+ return;
+
+ ReportError(toString(*OldType), toString(*NewGlobalType));
+ }
}
DefinedFunction *SymbolTable::addSyntheticFunction(StringRef Name,
@@ -140,9 +149,21 @@
return replaceSymbol<DefinedData>(S, Name, Flags);
}
+DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef Name, uint32_t Flags,
+ InputGlobal *Global) {
+ DEBUG(dbgs() << "addSyntheticGlobal: " << Name << " -> " << Global << "\n");
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ assert(WasInserted);
+ return replaceSymbol<DefinedGlobal>(S, Name, Flags, nullptr, Global);
+}
+
static bool shouldReplace(const Symbol &Existing, InputFile *NewFile,
- uint32_t NewFlags, InputChunk *NewChunk,
- bool NewIsFunction) {
+ WasmSymbolType NewType, uint32_t NewFlags,
+ const WasmSignature *NewFuncType = nullptr,
+ const WasmGlobalType *NewGlobalType = nullptr) {
+
// If existing symbol is lazy, replace it without checking types since
// lazy symbols don't have any type information.
if (Existing.isLazy()) {
@@ -155,7 +176,7 @@
// symbol name must have the same type, even if they are undefined. This
// is different from ELF because symbol types are not that significant
// in ELF, and undefined symbols in ELF don't have type in the first place.
- checkSymbolTypes(Existing, *NewFile, NewIsFunction, NewChunk);
+ checkSymbolTypes(Existing, *NewFile, NewType, NewFuncType, NewGlobalType);
// If existing symbol is undefined, replace it.
if (!Existing.isDefined()) {
@@ -188,38 +209,58 @@
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
- if (WasInserted || shouldReplace(*S, F, Flags, Function, true))
+ if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_FUNCTION, Flags,
+ &Function->Signature))
replaceSymbol<DefinedFunction>(S, Name, Flags, F, Function);
return S;
}
Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags,
- InputFile *F, InputSegment *Segment,
- uint32_t Address) {
+ InputFile *F, InputSegment *Segment,
+ uint32_t Address, uint32_t Size) {
DEBUG(dbgs() << "addDefinedData:" << Name << " addr:" << Address << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
- if (WasInserted || shouldReplace(*S, F, Flags, Segment, false))
- replaceSymbol<DefinedData>(S, Name, Flags, F, Segment, Address);
+ if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_DATA, Flags))
+ replaceSymbol<DefinedData>(S, Name, Flags, F, Segment, Address, Size);
return S;
}
-Symbol *SymbolTable::addUndefined(StringRef Name, Symbol::Kind Kind,
+Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
+ InputFile *F, InputGlobal *Global) {
+ DEBUG(dbgs() << "addDefinedGlobal:" << Name << "\n");
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted || shouldReplace(*S, F, WASM_SYMBOL_TYPE_GLOBAL, Flags,
+ nullptr, &Global->getType()))
+ replaceSymbol<DefinedGlobal>(S, Name, Flags, F, Global);
+ return S;
+}
+
+Symbol *SymbolTable::addUndefined(StringRef Name, WasmSymbolType Type,
uint32_t Flags, InputFile *F,
- const WasmSignature *Type) {
- DEBUG(dbgs() << "addUndefined: " << Name << "\n");
+ const WasmSignature *FunctionType,
+ const WasmGlobalType *GlobalType) {
+ DEBUG(dbgs() << "addUndefined type=" << Type << ": " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
- bool IsFunction = Kind == Symbol::UndefinedFunctionKind;
if (WasInserted) {
- if (IsFunction)
- replaceSymbol<UndefinedFunction>(S, Name, Flags, F, Type);
- else
+ switch (Type) {
+ case WASM_SYMBOL_TYPE_FUNCTION:
+ replaceSymbol<UndefinedFunction>(S, Name, Flags, F, FunctionType);
+ break;
+ case WASM_SYMBOL_TYPE_GLOBAL:
+ replaceSymbol<UndefinedGlobal>(S, Name, Flags, F, GlobalType);
+ break;
+ case WASM_SYMBOL_TYPE_DATA:
replaceSymbol<UndefinedData>(S, Name, Flags, F);
+ break;
+ }
return S;
}
@@ -231,8 +272,9 @@
if (S->isDefined()) {
DEBUG(dbgs() << "resolved by existing\n");
- checkSymbolTypes(*S, *F, IsFunction, Type);
+ checkSymbolTypes(*S, *F, Type, FunctionType, GlobalType);
}
+
return S;
}