Files
llvm/lld/wasm/SymbolTable.cpp
Rui Ueyama e3498ec562 [WebAssembly] Separate addUndefined into addUndefined{Function,Data,Global}.
Previously, one function adds all types of undefined symbols. That
doesn't fit to the wasm's undefined symbol semantics well because
different types of undefined symbols are very different in wasm.
As a result, separate control flows merge in this addUndefined function
and then separate again for each type. That wasn't easy to read.

This patch separates the function into three functions. Now it is pretty
clear what we are doing for each undefined symbol type.

Differential Revision: https://reviews.llvm.org/D43697

llvm-svn: 326271
2018-02-28 00:09:22 +00:00

330 lines
10 KiB
C++

//===- SymbolTable.cpp ----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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"
#include "llvm/ADT/SetVector.h"
#define DEBUG_TYPE "lld"
using namespace llvm;
using namespace llvm::wasm;
using namespace lld;
using namespace lld::wasm;
SymbolTable *lld::wasm::Symtab;
void SymbolTable::addFile(InputFile *File) {
log("Processing: " + toString(File));
File->parse();
if (auto *F = dyn_cast<ObjFile>(File))
ObjectFiles.push_back(F);
}
void SymbolTable::reportRemainingUndefines() {
SetVector<Symbol *> Undefs;
for (Symbol *Sym : SymVector) {
if (Sym->isUndefined() && !Sym->isWeak() &&
Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) {
Undefs.insert(Sym);
}
}
if (Undefs.empty())
return;
for (ObjFile *File : ObjectFiles)
for (Symbol *Sym : File->getSymbols())
if (Undefs.count(Sym))
error(toString(File) + ": undefined symbol: " + toString(*Sym));
for (Symbol *Sym : Undefs)
if (!Sym->getFile())
error("undefined symbol: " + toString(*Sym));
}
Symbol *SymbolTable::find(StringRef Name) {
auto It = SymMap.find(CachedHashStringRef(Name));
if (It == SymMap.end())
return nullptr;
return It->second;
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
if (Sym)
return {Sym, false};
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
SymVector.emplace_back(Sym);
return {Sym, true};
}
static void reportTypeError(const Symbol *Existing, const InputFile *File,
StringRef Type) {
error("symbol type mismatch: " + toString(*Existing) + "\n>>> defined as " +
toString(Existing->getWasmType()) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " + Type + " in " +
toString(File));
}
static void checkFunctionType(const Symbol *Existing, const InputFile *File,
const WasmSignature *NewSig) {
if (!isa<FunctionSymbol>(Existing)) {
reportTypeError(Existing, File, "Function");
return;
}
if (!Config->CheckSignatures)
return;
const WasmSignature *OldSig =
cast<FunctionSymbol>(Existing)->getFunctionType();
if (OldSig && *NewSig != *OldSig) {
error("Function type mismatch: " + Existing->getName() +
"\n>>> defined as " + toString(*OldSig) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " +
toString(*NewSig) + " in " + toString(File));
}
}
// 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 checkGlobalType(const Symbol *Existing, const InputFile *File,
const WasmGlobalType *NewType) {
if (!isa<GlobalSymbol>(Existing)) {
reportTypeError(Existing, File, "Global");
return;
}
const WasmGlobalType *OldType = cast<GlobalSymbol>(Existing)->getGlobalType();
if (*NewType != *OldType) {
error("Global type mismatch: " + Existing->getName() + "\n>>> defined as " +
toString(*OldType) + " in " + toString(Existing->getFile()) +
"\n>>> defined as " + toString(*NewType) + " in " + toString(File));
}
}
static void checkDataType(const Symbol *Existing, const InputFile *File) {
if (!isa<DataSymbol>(Existing))
reportTypeError(Existing, File, "Data");
}
DefinedFunction *SymbolTable::addSyntheticFunction(StringRef Name,
const WasmSignature *Type,
uint32_t Flags) {
DEBUG(dbgs() << "addSyntheticFunction: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
assert(WasInserted);
return replaceSymbol<DefinedFunction>(S, Name, Flags, Type);
}
DefinedData *SymbolTable::addSyntheticDataSymbol(StringRef Name,
uint32_t Flags) {
DEBUG(dbgs() << "addSyntheticDataSymbol: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
assert(WasInserted);
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) {
// If existing symbol is undefined, replace it.
if (!Existing->isDefined()) {
DEBUG(dbgs() << "resolving existing undefined symbol: "
<< Existing->getName() << "\n");
return true;
}
// Now we have two defined symbols. If the new one is weak, we can ignore it.
if ((NewFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
DEBUG(dbgs() << "existing symbol takes precedence\n");
return false;
}
// If the existing symbol is weak, we should replace it.
if (Existing->isWeak()) {
DEBUG(dbgs() << "replacing existing weak symbol\n");
return true;
}
// Neither symbol is week. They conflict.
error("duplicate symbol: " + toString(*Existing) + "\n>>> defined in " +
toString(Existing->getFile()) + "\n>>> defined in " +
toString(NewFile));
return true;
}
Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags,
InputFile *File,
InputFunction *Function) {
DEBUG(dbgs() << "addDefinedFunction: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted || S->isLazy()) {
replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
return S;
}
checkFunctionType(S, File, &Function->Signature);
if (shouldReplace(S, File, Flags))
replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
return S;
}
Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags,
InputFile *File, 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 || S->isLazy()) {
replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size);
return S;
}
checkDataType(S, File);
if (shouldReplace(S, File, Flags))
replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size);
return S;
}
Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
InputFile *File, InputGlobal *Global) {
DEBUG(dbgs() << "addDefinedGlobal:" << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted || S->isLazy()) {
replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global);
return S;
}
checkGlobalType(S, File, &Global->getType());
if (shouldReplace(S, File, Flags))
replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global);
return S;
}
Symbol *SymbolTable::addUndefinedFunction(StringRef Name, uint32_t Flags,
InputFile *File,
const WasmSignature *Sig) {
DEBUG(dbgs() << "addUndefinedFunction: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted)
replaceSymbol<UndefinedFunction>(S, Name, Flags, File, Sig);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
cast<ArchiveFile>(Lazy->getFile())->addMember(&Lazy->getArchiveSymbol());
else if (S->isDefined())
checkFunctionType(S, File, Sig);
return S;
}
Symbol *SymbolTable::addUndefinedData(StringRef Name, uint32_t Flags,
InputFile *File) {
DEBUG(dbgs() << "addUndefinedData: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted)
replaceSymbol<UndefinedData>(S, Name, Flags, File);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
cast<ArchiveFile>(Lazy->getFile())->addMember(&Lazy->getArchiveSymbol());
else if (S->isDefined())
checkDataType(S, File);
return S;
}
Symbol *SymbolTable::addUndefinedGlobal(StringRef Name, uint32_t Flags,
InputFile *File,
const WasmGlobalType *Type) {
DEBUG(dbgs() << "addUndefinedGlobal: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted)
replaceSymbol<UndefinedGlobal>(S, Name, Flags, File, Type);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
cast<ArchiveFile>(Lazy->getFile())->addMember(&Lazy->getArchiveSymbol());
else if (S->isDefined())
checkGlobalType(S, File, Type);
return S;
}
void SymbolTable::addLazy(ArchiveFile *File, const Archive::Symbol *Sym) {
DEBUG(dbgs() << "addLazy: " << Sym->getName() << "\n");
StringRef Name = Sym->getName();
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceSymbol<LazySymbol>(S, Name, File, *Sym);
return;
}
// If there is an existing undefined symbol, load a new one from the archive.
if (S->isUndefined()) {
DEBUG(dbgs() << "replacing existing undefined\n");
File->addMember(Sym);
}
}
bool SymbolTable::addComdat(StringRef Name, ObjFile *F) {
DEBUG(dbgs() << "addComdat: " << Name << "\n");
ObjFile *&File = ComdatMap[CachedHashStringRef(Name)];
if (File) {
DEBUG(dbgs() << "COMDAT already defined\n");
return false;
}
File = F;
return true;
}
ObjFile *SymbolTable::findComdat(StringRef Name) const {
auto It = ComdatMap.find(CachedHashStringRef(Name));
return It == ComdatMap.end() ? nullptr : It->second;
}