Page MenuHomePhabricator

No OneTemporary

File Metadata

Created
Fri, Jan 24, 3:56 PM
diff --git a/lld/test/wasm/signature-mismatch-unknown.ll b/lld/test/wasm/signature-mismatch-unknown.ll
new file mode 100644
index 0000000..65bb315
--- /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/Driver.cpp b/lld/wasm/Driver.cpp
index 4ac5aff..5d27ac4 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1,754 +1,754 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lld/Common/Driver.h"
#include "Config.h"
#include "InputChunks.h"
#include "InputGlobal.h"
#include "MarkLive.h"
#include "SymbolTable.h"
#include "Writer.h"
#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Reproduce.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/TargetSelect.h"
#define DEBUG_TYPE "lld"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::sys;
using namespace llvm::wasm;
using namespace lld;
using namespace lld::wasm;
Configuration *lld::wasm::Config;
namespace {
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
#include "Options.inc"
#undef OPTION
};
// This function is called on startup. We need this for LTO since
// LTO calls LLVM functions to compile bitcode files to native code.
// Technically this can be delayed until we read bitcode files, but
// we don't bother to do lazily because the initialization is fast.
static void initLLVM() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
}
class LinkerDriver {
public:
void link(ArrayRef<const char *> ArgsArr);
private:
void createFiles(opt::InputArgList &Args);
void addFile(StringRef Path);
void addLibrary(StringRef Name);
// True if we are in --whole-archive and --no-whole-archive.
bool InWholeArchive = false;
std::vector<InputFile *> Files;
};
} // anonymous namespace
bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
errorHandler().ErrorOS = &Error;
errorHandler().ColorDiagnostics = Error.has_colors();
errorHandler().ErrorLimitExceededMsg =
"too many errors emitted, stopping now (use "
"-error-limit=0 to see all errors)";
Config = make<Configuration>();
Symtab = make<SymbolTable>();
initLLVM();
LinkerDriver().link(Args);
// Exit immediately if we don't need to return to the caller.
// This saves time because the overhead of calling destructors
// for all globally-allocated objects is not negligible.
if (CanExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
return !errorCount();
}
// Create prefix string literals used in Options.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "Options.inc"
#undef PREFIX
// Create table mapping all options defined in Options.td
static const opt::OptTable::Info OptInfo[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
#include "Options.inc"
#undef OPTION
};
namespace {
class WasmOptTable : public llvm::opt::OptTable {
public:
WasmOptTable() : OptTable(OptInfo) {}
opt::InputArgList parse(ArrayRef<const char *> Argv);
};
} // namespace
// Set color diagnostics according to -color-diagnostics={auto,always,never}
// or -no-color-diagnostics flags.
static void handleColorDiagnostics(opt::InputArgList &Args) {
auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
if (!Arg)
return;
if (Arg->getOption().getID() == OPT_color_diagnostics) {
errorHandler().ColorDiagnostics = true;
} else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
errorHandler().ColorDiagnostics = false;
} else {
StringRef S = Arg->getValue();
if (S == "always")
errorHandler().ColorDiagnostics = true;
else if (S == "never")
errorHandler().ColorDiagnostics = false;
else if (S != "auto")
error("unknown option: --color-diagnostics=" + S);
}
}
// Find a file by concatenating given paths.
static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
SmallString<128> S;
path::append(S, Path1, Path2);
if (fs::exists(S))
return S.str().str();
return None;
}
opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> Argv) {
SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
unsigned MissingIndex;
unsigned MissingCount;
// Expand response files (arguments in the form of @<filename>)
cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Vec);
opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
handleColorDiagnostics(Args);
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
error("unknown argument: " + Arg->getSpelling());
return Args;
}
// Currently we allow a ".imports" to live alongside a library. This can
// be used to specify a list of symbols which can be undefined at link
// time (imported from the environment. For example libc.a include an
// import file that lists the syscall functions it relies on at runtime.
// In the long run this information would be better stored as a symbol
// attribute/flag in the object file itself.
// See: https://github.com/WebAssembly/tool-conventions/issues/35
static void readImportFile(StringRef Filename) {
if (Optional<MemoryBufferRef> Buf = readFile(Filename))
for (StringRef Sym : args::getLines(*Buf))
Config->AllowUndefinedSymbols.insert(Sym);
}
// Returns slices of MB by parsing MB as an archive file.
// Each slice consists of a member file in the archive.
std::vector<MemoryBufferRef> static getArchiveMembers(MemoryBufferRef MB) {
std::unique_ptr<Archive> File =
CHECK(Archive::create(MB),
MB.getBufferIdentifier() + ": failed to parse archive");
std::vector<MemoryBufferRef> V;
Error Err = Error::success();
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
Archive::Child C =
CHECK(COrErr, MB.getBufferIdentifier() +
": could not get the child of the archive");
MemoryBufferRef MBRef =
CHECK(C.getMemoryBufferRef(),
MB.getBufferIdentifier() +
": could not get the buffer for a child of the archive");
V.push_back(MBRef);
}
if (Err)
fatal(MB.getBufferIdentifier() +
": Archive::children failed: " + toString(std::move(Err)));
// Take ownership of memory buffers created for members of thin archives.
for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers())
make<std::unique_ptr<MemoryBuffer>>(std::move(MB));
return V;
}
void LinkerDriver::addFile(StringRef Path) {
Optional<MemoryBufferRef> Buffer = readFile(Path);
if (!Buffer.hasValue())
return;
MemoryBufferRef MBRef = *Buffer;
switch (identify_magic(MBRef.getBuffer())) {
case file_magic::archive: {
// Handle -whole-archive.
if (InWholeArchive) {
for (MemoryBufferRef &M : getArchiveMembers(MBRef))
Files.push_back(createObjectFile(M, Path));
return;
}
SmallString<128> ImportFile = Path;
path::replace_extension(ImportFile, ".imports");
if (fs::exists(ImportFile))
readImportFile(ImportFile.str());
Files.push_back(make<ArchiveFile>(MBRef));
return;
}
case file_magic::bitcode:
case file_magic::wasm_object:
Files.push_back(createObjectFile(MBRef));
break;
default:
error("unknown file type: " + MBRef.getBufferIdentifier());
}
}
// Add a given library by searching it from input search paths.
void LinkerDriver::addLibrary(StringRef Name) {
for (StringRef Dir : Config->SearchPaths) {
if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a")) {
addFile(*S);
return;
}
}
error("unable to find library -l" + Name);
}
void LinkerDriver::createFiles(opt::InputArgList &Args) {
for (auto *Arg : Args) {
switch (Arg->getOption().getUnaliasedOption().getID()) {
case OPT_l:
addLibrary(Arg->getValue());
break;
case OPT_INPUT:
addFile(Arg->getValue());
break;
case OPT_whole_archive:
InWholeArchive = true;
break;
case OPT_no_whole_archive:
InWholeArchive = false;
break;
}
}
}
static StringRef getEntry(opt::InputArgList &Args) {
auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
if (!Arg) {
if (Args.hasArg(OPT_relocatable))
return "";
if (Args.hasArg(OPT_shared))
return "__wasm_call_ctors";
return "_start";
}
if (Arg->getOption().getID() == OPT_no_entry)
return "";
return Arg->getValue();
}
// Initializes Config members by the command line options.
static void readConfigs(opt::InputArgList &Args) {
Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
Config->CheckFeatures =
Args.hasFlag(OPT_check_features, OPT_no_check_features, true);
Config->CompressRelocations = Args.hasArg(OPT_compress_relocations);
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
Config->Entry = getEntry(Args);
Config->ExportAll = Args.hasArg(OPT_export_all);
Config->ExportDynamic = Args.hasFlag(OPT_export_dynamic,
OPT_no_export_dynamic, false);
Config->ExportTable = Args.hasArg(OPT_export_table);
errorHandler().FatalWarnings =
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
Config->ImportMemory = Args.hasArg(OPT_import_memory);
Config->SharedMemory = Args.hasArg(OPT_shared_memory);
Config->ImportTable = Args.hasArg(OPT_import_table);
Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
Config->Optimize = args::getInteger(Args, OPT_O, 0);
Config->OutputFile = Args.getLastArgValue(OPT_o);
Config->Relocatable = Args.hasArg(OPT_relocatable);
Config->GcSections =
Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, !Config->Relocatable);
Config->MergeDataSegments =
Args.hasFlag(OPT_merge_data_segments, OPT_no_merge_data_segments,
!Config->Relocatable);
Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false);
Config->PrintGcSections =
Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
Config->SearchPaths = args::getStrings(Args, OPT_L);
Config->Shared = Args.hasArg(OPT_shared);
Config->StripAll = Args.hasArg(OPT_strip_all);
Config->StripDebug = Args.hasArg(OPT_strip_debug);
Config->StackFirst = Args.hasArg(OPT_stack_first);
Config->Trace = Args.hasArg(OPT_trace);
Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
Config->ThinLTOCachePolicy = CHECK(
parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
errorHandler().Verbose = Args.hasArg(OPT_verbose);
LLVM_DEBUG(errorHandler().Verbose = true);
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
Config->InitialMemory = args::getInteger(Args, OPT_initial_memory, 0);
Config->GlobalBase = args::getInteger(Args, OPT_global_base, 1024);
Config->MaxMemory = args::getInteger(Args, OPT_max_memory, 0);
Config->ZStackSize =
args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize);
if (auto *Arg = Args.getLastArg(OPT_features)) {
Config->Features =
llvm::Optional<std::vector<std::string>>(std::vector<std::string>());
for (StringRef S : Arg->getValues())
Config->Features->push_back(S);
}
}
// Some Config members do not directly correspond to any particular
// command line options, but computed based on other Config values.
// This function initialize such members. See Config.h for the details
// of these values.
static void setConfigs() {
Config->Pic = Config->Pie || Config->Shared;
if (Config->Pic) {
if (Config->ExportTable)
error("-shared/-pie is incompatible with --export-table");
Config->ImportTable = true;
}
if (Config->Shared) {
Config->ImportMemory = true;
Config->ExportDynamic = true;
Config->AllowUndefined = true;
}
}
// Some command line options or some combinations of them are not allowed.
// This function checks for such errors.
static void checkOptions(opt::InputArgList &Args) {
if (!Config->StripDebug && !Config->StripAll && Config->CompressRelocations)
error("--compress-relocations is incompatible with output debug"
" information. Please pass --strip-debug or --strip-all");
if (Config->LTOO > 3)
error("invalid optimization level for LTO: " + Twine(Config->LTOO));
if (Config->LTOPartitions == 0)
error("--lto-partitions: number of threads must be > 0");
if (Config->ThinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
if (Config->Pie && Config->Shared)
error("-shared and -pie may not be used together");
if (Config->OutputFile.empty())
error("no output file specified");
if (Config->ImportTable && Config->ExportTable)
error("--import-table and --export-table may not be used together");
if (Config->Relocatable) {
if (!Config->Entry.empty())
error("entry point specified for relocatable output file");
if (Config->GcSections)
error("-r and --gc-sections may not be used together");
if (Config->CompressRelocations)
error("-r -and --compress-relocations may not be used together");
if (Args.hasArg(OPT_undefined))
error("-r -and --undefined may not be used together");
if (Config->Pie)
error("-r and -pie may not be used together");
}
}
// Force Sym to be entered in the output. Used for -u or equivalent.
static Symbol *handleUndefined(StringRef Name) {
Symbol *Sym = Symtab->find(Name);
if (!Sym)
return nullptr;
// Since symbol S may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
Sym->IsUsedInRegularObj = true;
if (auto *LazySym = dyn_cast<LazySymbol>(Sym))
LazySym->fetch();
return Sym;
}
static UndefinedGlobal *
createUndefinedGlobal(StringRef Name, llvm::wasm::WasmGlobalType *Type) {
auto *Sym =
cast<UndefinedGlobal>(Symtab->addUndefinedGlobal(Name, Name,
DefaultModule, 0,
nullptr, Type));
Config->AllowUndefinedSymbols.insert(Sym->getName());
Sym->IsUsedInRegularObj = true;
return Sym;
}
// Create ABI-defined synthetic symbols
static void createSyntheticSymbols() {
static WasmSignature NullSignature = {{}, {}};
static llvm::wasm::WasmGlobalType GlobalTypeI32 = {WASM_TYPE_I32, false};
static llvm::wasm::WasmGlobalType MutableGlobalTypeI32 = {WASM_TYPE_I32,
true};
if (!Config->Relocatable) {
WasmSym::CallCtors = Symtab->addSyntheticFunction(
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
if (Config->Pic) {
// For PIC code we create a synthetic function call __wasm_apply_relocs
// and add this as the first call in __wasm_call_ctors.
// We also unconditionally export
WasmSym::ApplyRelocs = Symtab->addSyntheticFunction(
"__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(NullSignature, "__wasm_apply_relocs"));
}
}
// The __stack_pointer is imported in the shared library case, and exported
// in the non-shared (executable) case.
if (Config->Shared) {
WasmSym::StackPointer =
createUndefinedGlobal("__stack_pointer", &MutableGlobalTypeI32);
} else {
llvm::wasm::WasmGlobal Global;
Global.Type = {WASM_TYPE_I32, true};
Global.InitExpr.Value.Int32 = 0;
Global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
Global.SymbolName = "__stack_pointer";
auto *StackPointer = make<InputGlobal>(Global, nullptr);
StackPointer->Live = true;
// For non-PIC code
// TODO(sbc): Remove WASM_SYMBOL_VISIBILITY_HIDDEN when the mutable global
// spec proposal is implemented in all major browsers.
// See: https://github.com/WebAssembly/mutable-global
WasmSym::StackPointer = Symtab->addSyntheticGlobal(
"__stack_pointer", WASM_SYMBOL_VISIBILITY_HIDDEN, StackPointer);
WasmSym::HeapBase = Symtab->addSyntheticDataSymbol("__heap_base", 0);
WasmSym::DataEnd = Symtab->addSyntheticDataSymbol("__data_end", 0);
// These two synthetic symbols exist purely for the embedder so we always
// want to export them.
WasmSym::HeapBase->ForceExport = true;
WasmSym::DataEnd->ForceExport = true;
}
if (Config->Pic) {
// For PIC code, we import two global variables (__memory_base and
// __table_base) from the environment and use these as the offset at
// which to load our static data and function table.
// See:
// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
WasmSym::MemoryBase =
createUndefinedGlobal("__memory_base", &GlobalTypeI32);
WasmSym::TableBase = createUndefinedGlobal("__table_base", &GlobalTypeI32);
WasmSym::MemoryBase->markLive();
WasmSym::TableBase->markLive();
}
WasmSym::DsoHandle = Symtab->addSyntheticDataSymbol(
"__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
}
// Reconstructs command line arguments so that so that you can re-run
// the same command with the same inputs. This is for --reproduce.
static std::string createResponseFile(const opt::InputArgList &Args) {
SmallString<0> Data;
raw_svector_ostream OS(Data);
// Copy the command line to the output while rewriting paths.
for (auto *Arg : Args) {
switch (Arg->getOption().getUnaliasedOption().getID()) {
case OPT_reproduce:
break;
case OPT_INPUT:
OS << quote(relativeToRoot(Arg->getValue())) << "\n";
break;
case OPT_o:
// If -o path contains directories, "lld @response.txt" will likely
// fail because the archive we are creating doesn't contain empty
// directories for the output path (-o doesn't create directories).
// Strip directories to prevent the issue.
OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n";
break;
default:
OS << toString(*Arg) << "\n";
}
}
return Data.str();
}
// The --wrap option is a feature to rename symbols so that you can write
// wrappers for existing functions. If you pass `-wrap=foo`, all
// occurrences of symbol `foo` are resolved to `wrap_foo` (so, you are
// expected to write `wrap_foo` function as a wrapper). The original
// symbol becomes accessible as `real_foo`, so you can call that from your
// wrapper.
//
// This data structure is instantiated for each -wrap option.
struct WrappedSymbol {
Symbol *Sym;
Symbol *Real;
Symbol *Wrap;
};
static Symbol *addUndefined(StringRef Name) {
- return Symtab->addUndefinedFunction(Name, "", "", 0, nullptr, nullptr);
+ return Symtab->addUndefinedFunction(Name, "", "", 0, nullptr, nullptr, false);
}
// Handles -wrap option.
//
// This function instantiates wrapper symbols. At this point, they seem
// like they are not being used at all, so we explicitly set some flags so
// that LTO won't eliminate them.
static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
std::vector<WrappedSymbol> V;
DenseSet<StringRef> Seen;
for (auto *Arg : Args.filtered(OPT_wrap)) {
StringRef Name = Arg->getValue();
if (!Seen.insert(Name).second)
continue;
Symbol *Sym = Symtab->find(Name);
if (!Sym)
continue;
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
V.push_back({Sym, Real, Wrap});
// We want to tell LTO not to inline symbols to be overwritten
// because LTO doesn't know the final symbol contents after renaming.
Real->CanInline = false;
Sym->CanInline = false;
// Tell LTO not to eliminate these symbols.
Sym->IsUsedInRegularObj = true;
Wrap->IsUsedInRegularObj = true;
Real->IsUsedInRegularObj = false;
}
return V;
}
// Do renaming for -wrap by updating pointers to symbols.
//
// When this function is executed, only InputFiles and symbol table
// contain pointers to symbol objects. We visit them to replace pointers,
// so that wrapped symbols are swapped as instructed by the command line.
static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
DenseMap<Symbol *, Symbol *> Map;
for (const WrappedSymbol &W : Wrapped) {
Map[W.Sym] = W.Wrap;
Map[W.Real] = W.Sym;
}
// Update pointers in input files.
parallelForEach(Symtab->ObjectFiles, [&](InputFile *File) {
MutableArrayRef<Symbol *> Syms = File->getMutableSymbols();
for (size_t I = 0, E = Syms.size(); I != E; ++I)
if (Symbol *S = Map.lookup(Syms[I]))
Syms[I] = S;
});
// Update pointers in the symbol table.
for (const WrappedSymbol &W : Wrapped)
Symtab->wrap(W.Sym, W.Real, W.Wrap);
}
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
WasmOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
// Handle --help
if (Args.hasArg(OPT_help)) {
Parser.PrintHelp(outs(),
(std::string(ArgsArr[0]) + " [options] file...").c_str(),
"LLVM Linker", false);
return;
}
// Handle --version
if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
outs() << getLLDVersion() << "\n";
return;
}
// Handle --reproduce
if (auto *Arg = Args.getLastArg(OPT_reproduce)) {
StringRef Path = Arg->getValue();
Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
TarWriter::create(Path, path::stem(Path));
if (ErrOrWriter) {
Tar = std::move(*ErrOrWriter);
Tar->append("response.txt", createResponseFile(Args));
Tar->append("version.txt", getLLDVersion() + "\n");
} else {
error("--reproduce: " + toString(ErrOrWriter.takeError()));
}
}
// Parse and evaluate -mllvm options.
std::vector<const char *> V;
V.push_back("wasm-ld (LLVM option parsing)");
for (auto *Arg : Args.filtered(OPT_mllvm))
V.push_back(Arg->getValue());
cl::ParseCommandLineOptions(V.size(), V.data());
errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
readConfigs(Args);
setConfigs();
checkOptions(Args);
if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
readImportFile(Arg->getValue());
if (!Args.hasArg(OPT_INPUT)) {
error("no input files");
return;
}
// Handle --trace-symbol.
for (auto *Arg : Args.filtered(OPT_trace_symbol))
Symtab->trace(Arg->getValue());
if (!Config->Relocatable)
createSyntheticSymbols();
createFiles(Args);
if (errorCount())
return;
// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table.
for (InputFile *F : Files)
Symtab->addFile(F);
if (errorCount())
return;
// Handle the `--undefined <sym>` options.
for (auto *Arg : Args.filtered(OPT_undefined))
handleUndefined(Arg->getValue());
Symbol *EntrySym = nullptr;
if (!Config->Relocatable && !Config->Entry.empty()) {
EntrySym = handleUndefined(Config->Entry);
if (EntrySym && EntrySym->isDefined())
EntrySym->ForceExport = true;
else
error("entry symbol not defined (pass --no-entry to supress): " +
Config->Entry);
}
if (errorCount())
return;
// Handle the `--export <sym>` options
// This works like --undefined but also exports the symbol if its found
for (auto *Arg : Args.filtered(OPT_export))
handleUndefined(Arg->getValue());
// Create wrapped symbols for -wrap option.
std::vector<WrappedSymbol> Wrapped = addWrappedSymbols(Args);
// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
Symtab->addCombinedLTOObject();
if (errorCount())
return;
// Resolve any variant symbols that were created due to signature
// mismatchs.
Symtab->handleSymbolVariants();
if (errorCount())
return;
// Apply symbol renames for -wrap.
if (!Wrapped.empty())
wrapSymbols(Wrapped);
for (auto *Arg : Args.filtered(OPT_export)) {
Symbol *Sym = Symtab->find(Arg->getValue());
if (Sym && Sym->isDefined())
Sym->ForceExport = true;
else if (!Config->AllowUndefined)
error(Twine("symbol exported via --export not found: ") +
Arg->getValue());
}
if (!Config->Relocatable) {
// Add synthetic dummies for weak undefined functions. Must happen
// after LTO otherwise functions may not yet have signatures.
Symtab->handleWeakUndefines();
}
if (EntrySym)
EntrySym->setHidden(false);
if (errorCount())
return;
// Do size optimizations: garbage collection
markLive();
// Write the result to the file.
writeResult();
}
diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index 185a865..a9d6abf 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -1,539 +1,549 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
#include "Config.h"
#include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h"
#include "SymbolTable.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Reproduce.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "lld"
using namespace lld;
using namespace lld::wasm;
using namespace llvm;
using namespace llvm::object;
using namespace llvm::wasm;
std::unique_ptr<llvm::TarWriter> lld::wasm::Tar;
Optional<MemoryBufferRef> lld::wasm::readFile(StringRef Path) {
log("Loading: " + Path);
auto MBOrErr = MemoryBuffer::getFile(Path);
if (auto EC = MBOrErr.getError()) {
error("cannot open " + Path + ": " + EC.message());
return None;
}
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
MemoryBufferRef MBRef = MB->getMemBufferRef();
make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership
if (Tar)
Tar->append(relativeToRoot(Path), MBRef.getBuffer());
return MBRef;
}
InputFile *lld::wasm::createObjectFile(MemoryBufferRef MB,
StringRef ArchiveName) {
file_magic Magic = identify_magic(MB.getBuffer());
if (Magic == file_magic::wasm_object) {
std::unique_ptr<Binary> Bin = check(createBinary(MB));
auto *Obj = cast<WasmObjectFile>(Bin.get());
if (Obj->isSharedObject())
return make<SharedFile>(MB);
return make<ObjFile>(MB, ArchiveName);
}
if (Magic == file_magic::bitcode)
return make<BitcodeFile>(MB, ArchiveName);
fatal("unknown file type: " + MB.getBufferIdentifier());
}
void ObjFile::dumpInfo() const {
log("info for: " + toString(this) +
"\n Symbols : " + Twine(Symbols.size()) +
"\n Function Imports : " + Twine(WasmObj->getNumImportedFunctions()) +
"\n Global Imports : " + Twine(WasmObj->getNumImportedGlobals()) +
"\n Event Imports : " + Twine(WasmObj->getNumImportedEvents()));
}
// Relocations contain either symbol or type indices. This function takes a
// relocation and returns relocated index (i.e. translates from the input
// symbol/type space to the output symbol/type space).
uint32_t ObjFile::calcNewIndex(const WasmRelocation &Reloc) const {
if (Reloc.Type == R_WASM_TYPE_INDEX_LEB) {
assert(TypeIsUsed[Reloc.Index]);
return TypeMap[Reloc.Index];
}
const Symbol *Sym = Symbols[Reloc.Index];
if (auto *SS = dyn_cast<SectionSymbol>(Sym))
Sym = SS->getOutputSectionSymbol();
return Sym->getOutputSymbolIndex();
}
// Relocations can contain addend for combined sections. This function takes a
// relocation and returns updated addend by offset in the output section.
uint32_t ObjFile::calcNewAddend(const WasmRelocation &Reloc) const {
switch (Reloc.Type) {
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_FUNCTION_OFFSET_I32:
return Reloc.Addend;
case R_WASM_SECTION_OFFSET_I32:
return getSectionSymbol(Reloc.Index)->Section->OutputOffset + Reloc.Addend;
default:
llvm_unreachable("unexpected relocation type");
}
}
// Calculate the value we expect to find at the relocation location.
// This is used as a sanity check before applying a relocation to a given
// location. It is useful for catching bugs in the compiler and linker.
uint32_t ObjFile::calcExpectedValue(const WasmRelocation &Reloc) const {
switch (Reloc.Type) {
case R_WASM_TABLE_INDEX_I32:
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB: {
const WasmSymbol &Sym = WasmObj->syms()[Reloc.Index];
return TableEntries[Sym.Info.ElementIndex];
}
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_REL_SLEB: {
const WasmSymbol &Sym = WasmObj->syms()[Reloc.Index];
if (Sym.isUndefined())
return 0;
const WasmSegment &Segment =
WasmObj->dataSegments()[Sym.Info.DataRef.Segment];
return Segment.Data.Offset.Value.Int32 + Sym.Info.DataRef.Offset +
Reloc.Addend;
}
case R_WASM_FUNCTION_OFFSET_I32: {
const WasmSymbol &Sym = WasmObj->syms()[Reloc.Index];
InputFunction *F =
Functions[Sym.Info.ElementIndex - WasmObj->getNumImportedFunctions()];
return F->getFunctionInputOffset() + F->getFunctionCodeOffset() +
Reloc.Addend;
}
case R_WASM_SECTION_OFFSET_I32:
return Reloc.Addend;
case R_WASM_TYPE_INDEX_LEB:
return Reloc.Index;
case R_WASM_FUNCTION_INDEX_LEB:
case R_WASM_GLOBAL_INDEX_LEB:
case R_WASM_EVENT_INDEX_LEB: {
const WasmSymbol &Sym = WasmObj->syms()[Reloc.Index];
return Sym.Info.ElementIndex;
}
default:
llvm_unreachable("unknown relocation type");
}
}
// Translate from the relocation's index into the final linked output value.
uint32_t ObjFile::calcNewValue(const WasmRelocation &Reloc) const {
const Symbol* Sym = nullptr;
if (Reloc.Type != R_WASM_TYPE_INDEX_LEB) {
Sym = Symbols[Reloc.Index];
// We can end up with relocations against non-live symbols. For example
// in debug sections.
if ((isa<FunctionSymbol>(Sym) || isa<DataSymbol>(Sym)) && !Sym->isLive())
return 0;
// Special handling for undefined data symbols. Most relocations against
// such symbols cannot be resolved.
if (isa<DataSymbol>(Sym) && Sym->isUndefined()) {
if (Sym->isWeak() || Config->Relocatable)
return 0;
// R_WASM_MEMORY_ADDR_I32 relocations in PIC code are turned into runtime
// fixups in __wasm_apply_relocs
if (Config->Pic && Reloc.Type == R_WASM_MEMORY_ADDR_I32)
return 0;
if (Reloc.Type != R_WASM_GLOBAL_INDEX_LEB) {
llvm_unreachable(
("invalid relocation against undefined data symbol: " + toString(*Sym))
.c_str());
}
}
}
switch (Reloc.Type) {
case R_WASM_TABLE_INDEX_I32:
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
if (Config->Pic && !getFunctionSymbol(Reloc.Index)->hasTableIndex())
return 0;
return getFunctionSymbol(Reloc.Index)->getTableIndex();
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
return cast<DefinedData>(Sym)->getVirtualAddress() + Reloc.Addend;
case R_WASM_TYPE_INDEX_LEB:
return TypeMap[Reloc.Index];
case R_WASM_FUNCTION_INDEX_LEB:
return getFunctionSymbol(Reloc.Index)->getFunctionIndex();
case R_WASM_GLOBAL_INDEX_LEB:
if (auto GS = dyn_cast<GlobalSymbol>(Sym))
return GS->getGlobalIndex();
return Sym->getGOTIndex();
case R_WASM_EVENT_INDEX_LEB:
return getEventSymbol(Reloc.Index)->getEventIndex();
case R_WASM_FUNCTION_OFFSET_I32: {
auto *F = cast<DefinedFunction>(Sym);
return F->Function->OutputOffset + F->Function->getFunctionCodeOffset() +
Reloc.Addend;
}
case R_WASM_SECTION_OFFSET_I32:
return getSectionSymbol(Reloc.Index)->Section->OutputOffset + Reloc.Addend;
default:
llvm_unreachable("unknown relocation type");
}
}
template <class T>
static void setRelocs(const std::vector<T *> &Chunks,
const WasmSection *Section) {
if (!Section)
return;
ArrayRef<WasmRelocation> Relocs = Section->Relocations;
assert(std::is_sorted(Relocs.begin(), Relocs.end(),
[](const WasmRelocation &R1, const WasmRelocation &R2) {
return R1.Offset < R2.Offset;
}));
assert(std::is_sorted(
Chunks.begin(), Chunks.end(), [](InputChunk *C1, InputChunk *C2) {
return C1->getInputSectionOffset() < C2->getInputSectionOffset();
}));
auto RelocsNext = Relocs.begin();
auto RelocsEnd = Relocs.end();
auto RelocLess = [](const WasmRelocation &R, uint32_t Val) {
return R.Offset < Val;
};
for (InputChunk *C : Chunks) {
auto RelocsStart = std::lower_bound(RelocsNext, RelocsEnd,
C->getInputSectionOffset(), RelocLess);
RelocsNext = std::lower_bound(
RelocsStart, RelocsEnd, C->getInputSectionOffset() + C->getInputSize(),
RelocLess);
C->setRelocations(ArrayRef<WasmRelocation>(RelocsStart, RelocsNext));
}
}
void ObjFile::parse(bool IgnoreComdats) {
// Parse a memory buffer as a wasm file.
LLVM_DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n");
std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), toString(this));
auto *Obj = dyn_cast<WasmObjectFile>(Bin.get());
if (!Obj)
fatal(toString(this) + ": not a wasm file");
if (!Obj->isRelocatableObject())
fatal(toString(this) + ": not a relocatable wasm file");
Bin.release();
WasmObj.reset(Obj);
// Build up a map of function indices to table indices for use when
// verifying the existing table index relocations
uint32_t TotalFunctions =
WasmObj->getNumImportedFunctions() + WasmObj->functions().size();
TableEntries.resize(TotalFunctions);
for (const WasmElemSegment &Seg : WasmObj->elements()) {
if (Seg.Offset.Opcode != WASM_OPCODE_I32_CONST)
fatal(toString(this) + ": invalid table elements");
uint32_t Offset = Seg.Offset.Value.Int32;
for (uint32_t Index = 0; Index < Seg.Functions.size(); Index++) {
uint32_t FunctionIndex = Seg.Functions[Index];
TableEntries[FunctionIndex] = Offset + Index;
}
}
- // 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<InputSection>(Section, this));
CustomSections.back()->setRelocations(Section.Relocations);
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());
TypeIsUsed.resize(getWasmObj()->types().size(), false);
ArrayRef<StringRef> Comdats = WasmObj->linkingData().Comdats;
for (unsigned I = 0; I < Comdats.size(); ++I)
if (IgnoreComdats)
KeptComdats.push_back(true);
else
KeptComdats.push_back(Symtab->addComdat(Comdats[I]));
// Populate `Segments`.
for (const WasmSegment &S : WasmObj->dataSegments())
Segments.emplace_back(make<InputSegment>(S, this));
setRelocs(Segments, DataSection);
// Populate `Functions`.
ArrayRef<WasmFunction> Funcs = WasmObj->functions();
ArrayRef<uint32_t> FuncTypes = WasmObj->functionTypes();
ArrayRef<WasmSignature> Types = WasmObj->types();
Functions.reserve(Funcs.size());
for (size_t I = 0, E = Funcs.size(); I != E; ++I)
Functions.emplace_back(
make<InputFunction>(Types[FuncTypes[I]], &Funcs[I], this));
setRelocs(Functions, CodeSection);
// Populate `Globals`.
for (const WasmGlobal &G : WasmObj->globals())
Globals.emplace_back(make<InputGlobal>(G, this));
// Populate `Events`.
for (const WasmEvent &E : WasmObj->events())
Events.emplace_back(make<InputEvent>(Types[E.Type.SigIndex], E, this));
// Populate `Symbols` based on the WasmSymbols in the object.
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]));
}
}
bool ObjFile::isExcludedByComdat(InputChunk *Chunk) const {
uint32_t C = Chunk->getComdat();
if (C == UINT32_MAX)
return false;
return !KeptComdats[C];
}
FunctionSymbol *ObjFile::getFunctionSymbol(uint32_t Index) const {
return cast<FunctionSymbol>(Symbols[Index]);
}
GlobalSymbol *ObjFile::getGlobalSymbol(uint32_t Index) const {
return cast<GlobalSymbol>(Symbols[Index]);
}
EventSymbol *ObjFile::getEventSymbol(uint32_t Index) const {
return cast<EventSymbol>(Symbols[Index]);
}
SectionSymbol *ObjFile::getSectionSymbol(uint32_t Index) const {
return cast<SectionSymbol>(Symbols[Index]);
}
DataSymbol *ObjFile::getDataSymbol(uint32_t Index) const {
return cast<DataSymbol>(Symbols[Index]);
}
Symbol *ObjFile::createDefined(const WasmSymbol &Sym) {
- if (!Sym.isDefined())
- return nullptr;
-
StringRef Name = Sym.Info.Name;
uint32_t Flags = Sym.Info.Flags;
switch (Sym.Info.Kind) {
case WASM_SYMBOL_TYPE_FUNCTION: {
InputFunction *Func =
Functions[Sym.Info.ElementIndex - WasmObj->getNumImportedFunctions()];
if (isExcludedByComdat(Func)) {
Func->Live = false;
return nullptr;
}
if (Sym.isBindingLocal())
return make<DefinedFunction>(Name, Flags, this, Func);
return Symtab->addDefinedFunction(Name, Flags, this, Func);
}
case WASM_SYMBOL_TYPE_DATA: {
InputSegment *Seg = Segments[Sym.Info.DataRef.Segment];
if (isExcludedByComdat(Seg)) {
Seg->Live = false;
return nullptr;
}
uint32_t Offset = Sym.Info.DataRef.Offset;
uint32_t Size = Sym.Info.DataRef.Size;
if (Sym.isBindingLocal())
return make<DefinedData>(Name, Flags, this, Seg, Offset, Size);
return Symtab->addDefinedData(Name, Flags, this, Seg, Offset, Size);
}
case WASM_SYMBOL_TYPE_GLOBAL: {
InputGlobal *Global =
Globals[Sym.Info.ElementIndex - WasmObj->getNumImportedGlobals()];
if (Sym.isBindingLocal())
return make<DefinedGlobal>(Name, Flags, this, Global);
return Symtab->addDefinedGlobal(Name, Flags, this, Global);
}
case WASM_SYMBOL_TYPE_SECTION: {
InputSection *Section = CustomSectionsByIndex[Sym.Info.ElementIndex];
assert(Sym.isBindingLocal());
return make<SectionSymbol>(Flags, Section, this);
}
case WASM_SYMBOL_TYPE_EVENT: {
InputEvent *Event =
Events[Sym.Info.ElementIndex - WasmObj->getNumImportedEvents()];
if (Sym.isBindingLocal())
return make<DefinedEvent>(Name, Flags, this, Event);
return Symtab->addDefinedEvent(Name, Flags, this, Event);
}
}
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;
switch (Sym.Info.Kind) {
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:
return Symtab->addUndefinedGlobal(Name, Sym.Info.ImportName,
Sym.Info.ImportModule, Flags, this,
Sym.GlobalType);
case WASM_SYMBOL_TYPE_SECTION:
llvm_unreachable("section symbols cannot be undefined");
}
llvm_unreachable("unknown symbol kind");
}
void ArchiveFile::parse(bool IgnoreComdats) {
// Parse a MemoryBufferRef as an archive file.
LLVM_DEBUG(dbgs() << "Parsing library: " << toString(this) << "\n");
File = CHECK(Archive::create(MB), toString(this));
// Read the symbol table to construct Lazy symbols.
int Count = 0;
for (const Archive::Symbol &Sym : File->symbols()) {
Symtab->addLazy(this, &Sym);
++Count;
}
LLVM_DEBUG(dbgs() << "Read " << Count << " symbols\n");
}
void ArchiveFile::addMember(const Archive::Symbol *Sym) {
const Archive::Child &C =
CHECK(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
// Don't try to load the same member twice (this can happen when members
// mutually reference each other).
if (!Seen.insert(C.getChildOffset()).second)
return;
LLVM_DEBUG(dbgs() << "loading lazy: " << Sym->getName() << "\n");
LLVM_DEBUG(dbgs() << "from archive: " << toString(this) << "\n");
MemoryBufferRef MB =
CHECK(C.getMemoryBufferRef(),
"could not get the buffer for the member defining symbol " +
Sym->getName());
InputFile *Obj = createObjectFile(MB, getName());
Symtab->addFile(Obj);
}
static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
switch (GvVisibility) {
case GlobalValue::DefaultVisibility:
return WASM_SYMBOL_VISIBILITY_DEFAULT;
case GlobalValue::HiddenVisibility:
case GlobalValue::ProtectedVisibility:
return WASM_SYMBOL_VISIBILITY_HIDDEN;
}
llvm_unreachable("unknown visibility");
}
static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
const lto::InputFile::Symbol &ObjSym,
BitcodeFile &F) {
StringRef Name = Saver.save(ObjSym.getName());
uint32_t Flags = ObjSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0;
Flags |= mapVisibility(ObjSym.getVisibility());
int C = ObjSym.getComdatIndex();
bool ExcludedByComdat = C != -1 && !KeptComdats[C];
if (ObjSym.isUndefined() || ExcludedByComdat) {
if (ObjSym.isExecutable())
return Symtab->addUndefinedFunction(Name, Name, DefaultModule, Flags, &F,
- nullptr);
+ nullptr, true);
return Symtab->addUndefinedData(Name, Flags, &F);
}
if (ObjSym.isExecutable())
return Symtab->addDefinedFunction(Name, Flags, &F, nullptr);
return Symtab->addDefinedData(Name, Flags, &F, nullptr, 0, 0);
}
void BitcodeFile::parse(bool IgnoreComdats) {
Obj = check(lto::InputFile::create(MemoryBufferRef(
MB.getBuffer(), Saver.save(ArchiveName + MB.getBufferIdentifier()))));
Triple T(Obj->getTargetTriple());
if (T.getArch() != Triple::wasm32) {
error(toString(MB.getBufferIdentifier()) + ": machine type must be wasm32");
return;
}
std::vector<bool> KeptComdats;
for (StringRef S : Obj->getComdatTable())
if (IgnoreComdats)
KeptComdats.push_back(true);
else
KeptComdats.push_back(Symtab->addComdat(S));
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
Symbols.push_back(createBitcodeSymbol(KeptComdats, ObjSym, *this));
}
// Returns a string in the format of "foo.o" or "foo.a(bar.o)".
std::string lld::toString(const wasm::InputFile *File) {
if (!File)
return "<internal>";
if (File->ArchiveName.empty())
return File->getName();
return (File->ArchiveName + "(" + File->getName() + ")").str();
}
diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h
index 7c8601e..64ac208 100644
--- a/lld/wasm/InputFiles.h
+++ b/lld/wasm/InputFiles.h
@@ -1,183 +1,190 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLD_WASM_INPUT_FILES_H
#define LLD_WASM_INPUT_FILES_H
#include "Symbols.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/MemoryBuffer.h"
#include <vector>
namespace llvm {
class TarWriter;
}
namespace lld {
namespace wasm {
class InputChunk;
class InputFunction;
class InputSegment;
class InputGlobal;
class InputEvent;
class InputSection;
// If --reproduce option is given, all input files are written
// to this tar archive.
extern std::unique_ptr<llvm::TarWriter> Tar;
class InputFile {
public:
enum Kind {
ObjectKind,
SharedKind,
ArchiveKind,
BitcodeKind,
};
virtual ~InputFile() {}
// Returns the filename.
StringRef getName() const { return MB.getBufferIdentifier(); }
// Reads a file (the constructor doesn't do that).
virtual void parse(bool IgnoreComdats = false) = 0;
Kind kind() const { return FileKind; }
// An archive file name if this file is created from an archive.
StringRef ArchiveName;
ArrayRef<Symbol *> getSymbols() const { return Symbols; }
MutableArrayRef<Symbol *> getMutableSymbols() { return Symbols; }
protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
MemoryBufferRef MB;
// List of all symbols referenced or defined by this file.
std::vector<Symbol *> 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<bool> SymbolIsCalledDirectly;
private:
const Kind FileKind;
};
// .a file (ar archive)
class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
void addMember(const llvm::object::Archive::Symbol *Sym);
void parse(bool IgnoreComdats) override;
private:
std::unique_ptr<llvm::object::Archive> File;
llvm::DenseSet<uint64_t> Seen;
};
// .o file (wasm object file)
class ObjFile : public InputFile {
public:
explicit ObjFile(MemoryBufferRef M, StringRef ArchiveName)
: InputFile(ObjectKind, M) {
this->ArchiveName = ArchiveName;
}
static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
void parse(bool IgnoreComdats) override;
// Returns the underlying wasm file.
const WasmObjectFile *getWasmObj() const { return WasmObj.get(); }
void dumpInfo() const;
uint32_t calcNewIndex(const WasmRelocation &Reloc) const;
uint32_t calcNewValue(const WasmRelocation &Reloc) const;
uint32_t calcNewAddend(const WasmRelocation &Reloc) const;
uint32_t calcExpectedValue(const WasmRelocation &Reloc) const;
Symbol *getSymbol(const WasmRelocation &Reloc) const {
return Symbols[Reloc.Index];
};
const WasmSection *CodeSection = nullptr;
const WasmSection *DataSection = nullptr;
// Maps input type indices to output type indices
std::vector<uint32_t> TypeMap;
std::vector<bool> TypeIsUsed;
// Maps function indices to table indices
std::vector<uint32_t> TableEntries;
std::vector<bool> KeptComdats;
std::vector<InputSegment *> Segments;
std::vector<InputFunction *> Functions;
std::vector<InputGlobal *> Globals;
std::vector<InputEvent *> Events;
std::vector<InputSection *> CustomSections;
llvm::DenseMap<uint32_t, InputSection *> CustomSectionsByIndex;
Symbol *getSymbol(uint32_t Index) const { return Symbols[Index]; }
FunctionSymbol *getFunctionSymbol(uint32_t Index) const;
DataSymbol *getDataSymbol(uint32_t Index) const;
GlobalSymbol *getGlobalSymbol(uint32_t Index) const;
SectionSymbol *getSectionSymbol(uint32_t Index) const;
EventSymbol *getEventSymbol(uint32_t Index) const;
private:
Symbol *createDefined(const WasmSymbol &Sym);
- Symbol *createUndefined(const WasmSymbol &Sym);
+ Symbol *createUndefined(const WasmSymbol &Sym, bool IsCalledDirectly);
bool isExcludedByComdat(InputChunk *Chunk) const;
std::unique_ptr<WasmObjectFile> WasmObj;
};
// .so file.
class SharedFile : public InputFile {
public:
explicit SharedFile(MemoryBufferRef M) : InputFile(SharedKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == SharedKind; }
void parse(bool IgnoreComdats) override {}
};
// .bc file
class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M, StringRef ArchiveName)
: InputFile(BitcodeKind, M) {
this->ArchiveName = ArchiveName;
}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
void parse(bool IgnoreComdats) override;
std::unique_ptr<llvm::lto::InputFile> Obj;
};
// Will report a fatal() error if the input buffer is not a valid bitcode
// or wasm object file.
InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "");
// Opens a given file.
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
} // namespace wasm
std::string toString(const wasm::InputFile *File);
} // namespace lld
#endif
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 5328e9e..ae42474 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -1,668 +1,673 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "SymbolTable.h"
#include "Config.h"
#include "InputChunks.h"
#include "InputEvent.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 llvm::object;
using namespace lld;
using namespace lld::wasm;
SymbolTable *lld::wasm::Symtab;
void SymbolTable::addFile(InputFile *File) {
log("Processing: " + toString(File));
if (Config->Trace)
message(toString(File));
File->parse();
// LLVM bitcode file
if (auto *F = dyn_cast<BitcodeFile>(File))
BitcodeFiles.push_back(F);
else if (auto *F = dyn_cast<ObjFile>(File))
ObjectFiles.push_back(F);
else if (auto *F = dyn_cast<SharedFile>(File))
SharedFiles.push_back(F);
}
// This function is where all the optimizations of link-time
// optimization happens. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
// This function compiles bitcode files into a few big native files
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that the program consists of are passed
// to the compiler at once, it can do whole-program optimization.
void SymbolTable::addCombinedLTOObject() {
if (BitcodeFiles.empty())
return;
// Compile bitcode files and replace bitcode symbols.
LTO.reset(new BitcodeCompiler);
for (BitcodeFile *F : BitcodeFiles)
LTO->add(*F);
for (StringRef Filename : LTO->compile()) {
auto *Obj = make<ObjFile>(MemoryBufferRef(Filename, "lto.tmp"), "");
Obj->parse(true);
ObjectFiles.push_back(Obj);
}
}
void SymbolTable::reportRemainingUndefines() {
for (const auto& Pair : SymMap) {
const Symbol *Sym = SymVector[Pair.second];
if (!Sym->isUndefined() || Sym->isWeak())
continue;
if (Config->AllowUndefinedSymbols.count(Sym->getName()) != 0)
continue;
if (!Sym->IsUsedInRegularObj)
continue;
error(toString(Sym->getFile()) + ": undefined symbol: " + toString(*Sym));
}
}
Symbol *SymbolTable::find(StringRef Name) {
auto It = SymMap.find(CachedHashStringRef(Name));
if (It == SymMap.end() || It->second == -1)
return nullptr;
return SymVector[It->second];
}
void SymbolTable::replace(StringRef Name, Symbol* Sym) {
auto It = SymMap.find(CachedHashStringRef(Name));
SymVector[It->second] = Sym;
}
std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) {
bool Trace = false;
auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()});
int &SymIndex = P.first->second;
bool IsNew = P.second;
if (SymIndex == -1) {
SymIndex = SymVector.size();
Trace = true;
IsNew = true;
}
if (!IsNew)
return {SymVector[SymIndex], false};
Symbol *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Sym->IsUsedInRegularObj = false;
Sym->CanInline = true;
Sym->Traced = Trace;
SymVector.emplace_back(Sym);
return {Sym, true};
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name,
const InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insertName(Name);
if (!File || File->kind() == InputFile::ObjectKind)
S->IsUsedInRegularObj = true;
return {S, WasInserted};
}
static void reportTypeError(const Symbol *Existing, const InputFile *File,
llvm::wasm::WasmSymbolType Type) {
error("symbol type mismatch: " + toString(*Existing) + "\n>>> defined as " +
toString(Existing->getWasmType()) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " + toString(Type) +
" in " + toString(File));
}
// Check the type of new symbol matches that of the symbol is replacing.
// Returns true if the function types match, false is there is a singature
// mismatch.
static bool signatureMatches(FunctionSymbol *Existing,
const WasmSignature *NewSig) {
if (!NewSig)
return true;
const WasmSignature *OldSig = Existing->Signature;
if (!OldSig) {
Existing->Signature = NewSig;
return true;
}
return *NewSig == *OldSig;
}
static void checkGlobalType(const Symbol *Existing, const InputFile *File,
const WasmGlobalType *NewType) {
if (!isa<GlobalSymbol>(Existing)) {
reportTypeError(Existing, File, WASM_SYMBOL_TYPE_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 checkEventType(const Symbol *Existing, const InputFile *File,
const WasmEventType *NewType,
const WasmSignature *NewSig) {
auto ExistingEvent = dyn_cast<EventSymbol>(Existing);
if (!isa<EventSymbol>(Existing)) {
reportTypeError(Existing, File, WASM_SYMBOL_TYPE_EVENT);
return;
}
const WasmEventType *OldType = cast<EventSymbol>(Existing)->getEventType();
const WasmSignature *OldSig = ExistingEvent->Signature;
if (NewType->Attribute != OldType->Attribute)
error("Event type mismatch: " + Existing->getName() + "\n>>> defined as " +
toString(*OldType) + " in " + toString(Existing->getFile()) +
"\n>>> defined as " + toString(*NewType) + " in " + toString(File));
if (*NewSig != *OldSig)
warn("Event signature mismatch: " + Existing->getName() +
"\n>>> defined as " + toString(*OldSig) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " +
toString(*NewSig) + " in " + toString(File));
}
static void checkDataType(const Symbol *Existing, const InputFile *File) {
if (!isa<DataSymbol>(Existing))
reportTypeError(Existing, File, WASM_SYMBOL_TYPE_DATA);
}
DefinedFunction *SymbolTable::addSyntheticFunction(StringRef Name,
uint32_t Flags,
InputFunction *Function) {
LLVM_DEBUG(dbgs() << "addSyntheticFunction: " << Name << "\n");
assert(!find(Name));
SyntheticFunctions.emplace_back(Function);
return replaceSymbol<DefinedFunction>(insertName(Name).first, Name,
Flags, nullptr, Function);
}
DefinedData *SymbolTable::addOptionalDataSymbol(StringRef Name, uint32_t Value,
uint32_t Flags) {
Symbol *S = find(Name);
if (!S || S->isDefined())
return nullptr;
LLVM_DEBUG(dbgs() << "addOptionalDataSymbol: " << Name << "\n");
auto *rtn = replaceSymbol<DefinedData>(S, Name, Flags);
rtn->setVirtualAddress(Value);
return rtn;
}
DefinedData *SymbolTable::addSyntheticDataSymbol(StringRef Name,
uint32_t Flags) {
LLVM_DEBUG(dbgs() << "addSyntheticDataSymbol: " << Name << "\n");
assert(!find(Name));
return replaceSymbol<DefinedData>(insertName(Name).first, Name, Flags);
}
DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef Name, uint32_t Flags,
InputGlobal *Global) {
LLVM_DEBUG(dbgs() << "addSyntheticGlobal: " << Name << " -> " << Global
<< "\n");
assert(!find(Name));
SyntheticGlobals.emplace_back(Global);
return replaceSymbol<DefinedGlobal>(insertName(Name).first, 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()) {
LLVM_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) {
LLVM_DEBUG(dbgs() << "existing symbol takes precedence\n");
return false;
}
// If the existing symbol is weak, we should replace it.
if (Existing->isWeak()) {
LLVM_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) {
LLVM_DEBUG(dbgs() << "addDefinedFunction: " << Name << " ["
<< (Function ? toString(Function->Signature) : "none")
<< "]\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
auto Replace = [&](Symbol* Sym) {
// If the new defined function doesn't have signture (i.e. bitcode
// functions) but the old symbol does, then preserve the old signature
const WasmSignature *OldSig = S->getSignature();
auto* NewSym = replaceSymbol<DefinedFunction>(Sym, Name, Flags, File, Function);
if (!NewSym->Signature)
NewSym->Signature = OldSig;
};
if (WasInserted || S->isLazy()) {
Replace(S);
return S;
}
auto ExistingFunction = dyn_cast<FunctionSymbol>(S);
if (!ExistingFunction) {
reportTypeError(S, File, WASM_SYMBOL_TYPE_FUNCTION);
return S;
}
- if (Function && !signatureMatches(ExistingFunction, &Function->Signature)) {
+ bool CheckSig = true;
+ if (auto UD = dyn_cast<UndefinedFunction>(ExistingFunction))
+ CheckSig = UD->IsCalledDirectly;
+
+ if (CheckSig && Function && !signatureMatches(ExistingFunction, &Function->Signature)) {
Symbol* Variant;
if (getFunctionVariant(S, &Function->Signature, File, &Variant))
// New variant, always replace
Replace(Variant);
else if (shouldReplace(S, File, Flags))
// Variant already exists, replace it after checking shouldReplace
Replace(Variant);
// This variant we found take the place in the symbol table as the primary
// variant.
replace(Name, Variant);
return Variant;
}
// Existing function with matching signature.
if (shouldReplace(S, File, Flags))
Replace(S);
return S;
}
Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags,
InputFile *File, InputSegment *Segment,
uint32_t Address, uint32_t Size) {
LLVM_DEBUG(dbgs() << "addDefinedData:" << Name << " addr:" << Address
<< "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
auto Replace = [&]() {
replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size);
};
if (WasInserted || S->isLazy()) {
Replace();
return S;
}
checkDataType(S, File);
if (shouldReplace(S, File, Flags))
Replace();
return S;
}
Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
InputFile *File, InputGlobal *Global) {
LLVM_DEBUG(dbgs() << "addDefinedGlobal:" << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
auto Replace = [&]() {
replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global);
};
if (WasInserted || S->isLazy()) {
Replace();
return S;
}
checkGlobalType(S, File, &Global->getType());
if (shouldReplace(S, File, Flags))
Replace();
return S;
}
Symbol *SymbolTable::addDefinedEvent(StringRef Name, uint32_t Flags,
InputFile *File, InputEvent *Event) {
LLVM_DEBUG(dbgs() << "addDefinedEvent:" << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
auto Replace = [&]() {
replaceSymbol<DefinedEvent>(S, Name, Flags, File, Event);
};
if (WasInserted || S->isLazy()) {
Replace();
return S;
}
checkEventType(S, File, &Event->getType(), &Event->Signature);
if (shouldReplace(S, File, Flags))
Replace();
return S;
}
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");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
if (S->Traced)
printTraceSymbolUndefined(Name, File);
auto Replace = [&]() {
replaceSymbol<UndefinedFunction>(S, Name, ImportName, ImportModule, Flags,
- File, Sig);
+ File, Sig, IsCalledDirectly);
};
if (WasInserted)
Replace();
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
Lazy->fetch();
else {
auto ExistingFunction = dyn_cast<FunctionSymbol>(S);
if (!ExistingFunction) {
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();
}
return S;
}
Symbol *SymbolTable::addUndefinedData(StringRef Name, uint32_t Flags,
InputFile *File) {
LLVM_DEBUG(dbgs() << "addUndefinedData: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
if (S->Traced)
printTraceSymbolUndefined(Name, File);
if (WasInserted)
replaceSymbol<UndefinedData>(S, Name, Flags, File);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
Lazy->fetch();
else if (S->isDefined())
checkDataType(S, File);
return S;
}
Symbol *SymbolTable::addUndefinedGlobal(StringRef Name, StringRef ImportName,
StringRef ImportModule, uint32_t Flags,
InputFile *File,
const WasmGlobalType *Type) {
LLVM_DEBUG(dbgs() << "addUndefinedGlobal: " << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
if (S->Traced)
printTraceSymbolUndefined(Name, File);
if (WasInserted)
replaceSymbol<UndefinedGlobal>(S, Name, ImportName, ImportModule, Flags,
File, Type);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
Lazy->fetch();
else if (S->isDefined())
checkGlobalType(S, File, Type);
return S;
}
void SymbolTable::addLazy(ArchiveFile *File, const Archive::Symbol *Sym) {
LLVM_DEBUG(dbgs() << "addLazy: " << Sym->getName() << "\n");
StringRef Name = Sym->getName();
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insertName(Name);
if (WasInserted) {
replaceSymbol<LazySymbol>(S, Name, 0, File, *Sym);
return;
}
if (!S->isUndefined())
return;
// The existing symbol is undefined, load a new one from the archive,
// unless the the existing symbol is weak in which case replace the undefined
// symbols with a LazySymbol.
if (S->isWeak()) {
const WasmSignature *OldSig = nullptr;
// In the case of an UndefinedFunction we need to preserve the expected
// signature.
if (auto *F = dyn_cast<UndefinedFunction>(S))
OldSig = F->Signature;
LLVM_DEBUG(dbgs() << "replacing existing weak undefined symbol\n");
auto NewSym = replaceSymbol<LazySymbol>(S, Name, WASM_SYMBOL_BINDING_WEAK,
File, *Sym);
NewSym->Signature = OldSig;
return;
}
LLVM_DEBUG(dbgs() << "replacing existing undefined\n");
File->addMember(Sym);
}
bool SymbolTable::addComdat(StringRef Name) {
return ComdatGroups.insert(CachedHashStringRef(Name)).second;
}
// The new signature doesn't match. Create a variant to the symbol with the
// signature encoded in the name and return that instead. These symbols are
// then unified later in handleSymbolVariants.
bool SymbolTable::getFunctionVariant(Symbol* Sym, const WasmSignature *Sig,
const InputFile *File, Symbol **Out) {
LLVM_DEBUG(dbgs() << "getFunctionVariant: " << Sym->getName() << " -> "
<< " " << toString(*Sig) << "\n");
Symbol *Variant = nullptr;
// Linear search through symbol variants. Should never be more than two
// or three entries here.
auto &Variants = SymVariants[CachedHashStringRef(Sym->getName())];
if (Variants.empty())
Variants.push_back(Sym);
for (Symbol* V : Variants) {
if (*V->getSignature() == *Sig) {
Variant = V;
break;
}
}
bool WasAdded = !Variant;
if (WasAdded) {
// Create a new variant;
LLVM_DEBUG(dbgs() << "added new variant\n");
Variant = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Variants.push_back(Variant);
} else {
LLVM_DEBUG(dbgs() << "variant already exists: " << toString(*Variant) << "\n");
assert(*Variant->getSignature() == *Sig);
}
*Out = Variant;
return WasAdded;
}
// Set a flag for --trace-symbol so that we can print out a log message
// if a new symbol with the same name is inserted into the symbol table.
void SymbolTable::trace(StringRef Name) {
SymMap.insert({CachedHashStringRef(Name), -1});
}
void SymbolTable::wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap) {
// Swap symbols as instructed by -wrap.
int &OrigIdx = SymMap[CachedHashStringRef(Sym->getName())];
int &RealIdx= SymMap[CachedHashStringRef(Real->getName())];
int &WrapIdx = SymMap[CachedHashStringRef(Wrap->getName())];
LLVM_DEBUG(dbgs() << "wrap: " << Sym->getName() << "\n");
// Anyone looking up __real symbols should get the original
RealIdx = OrigIdx;
// Anyone looking up the original should get the __wrap symbol
OrigIdx = WrapIdx;
}
static const uint8_t UnreachableFn[] = {
0x03 /* ULEB length */, 0x00 /* ULEB num locals */,
0x00 /* opcode unreachable */, 0x0b /* opcode end */
};
// Replace the given symbol body with an unreachable function.
// This is used by handleWeakUndefines in order to generate a callable
// equivalent of an undefined function and also handleSymbolVariants for
// undefined functions that don't match the signature of the definition.
InputFunction *SymbolTable::replaceWithUnreachable(Symbol *Sym,
const WasmSignature &Sig,
StringRef DebugName) {
auto *Func = make<SyntheticFunction>(Sig, Sym->getName(), DebugName);
Func->setBody(UnreachableFn);
SyntheticFunctions.emplace_back(Func);
replaceSymbol<DefinedFunction>(Sym, Sym->getName(), Sym->getFlags(), nullptr,
Func);
return Func;
}
// For weak undefined functions, there may be "call" instructions that reference
// the symbol. In this case, we need to synthesise a dummy/stub function that
// will abort at runtime, so that relocations can still provided an operand to
// the call instruction that passes Wasm validation.
void SymbolTable::handleWeakUndefines() {
for (Symbol *Sym : getSymbols()) {
if (!Sym->isUndefWeak())
continue;
const WasmSignature *Sig = Sym->getSignature();
if (!Sig) {
// It is possible for undefined functions not to have a signature (eg. if
// added via "--undefined"), but weak undefined ones do have a signature.
// Lazy symbols may not be functions and therefore Sig can still be null
// in some circumstantce.
assert(!isa<FunctionSymbol>(Sym));
continue;
}
// Add a synthetic dummy for weak undefined functions. These dummies will
// be GC'd if not used as the target of any "call" instructions.
StringRef DebugName = Saver.save("undefined:" + toString(*Sym));
InputFunction* Func = replaceWithUnreachable(Sym, *Sig, DebugName);
// Ensure it compares equal to the null pointer, and so that table relocs
// don't pull in the stub body (only call-operand relocs should do that).
Func->setTableIndex(0);
// Hide our dummy to prevent export.
Sym->setHidden(true);
}
}
static void reportFunctionSignatureMismatch(StringRef SymName,
FunctionSymbol *A,
FunctionSymbol *B, bool Error) {
std::string msg = ("function signature mismatch: " + SymName +
"\n>>> defined as " + toString(*A->Signature) + " in " +
toString(A->getFile()) + "\n>>> defined as " +
toString(*B->Signature) + " in " + toString(B->getFile()))
.str();
if (Error)
error(msg);
else
warn(msg);
}
// Remove any variant symbols that were created due to function signature
// mismatches.
void SymbolTable::handleSymbolVariants() {
for (auto Pair : SymVariants) {
// Push the initial symbol onto the list of variants.
StringRef SymName = Pair.first.val();
std::vector<Symbol *> &Variants = Pair.second;
#ifndef NDEBUG
LLVM_DEBUG(dbgs() << "symbol with (" << Variants.size()
<< ") variants: " << SymName << "\n");
for (auto *S: Variants) {
auto *F = cast<FunctionSymbol>(S);
LLVM_DEBUG(dbgs() << " variant: " + F->getName() << " "
<< toString(*F->Signature) << "\n");
}
#endif
// Find the one definition.
DefinedFunction *Defined = nullptr;
for (auto *Symbol : Variants) {
if (auto F = dyn_cast<DefinedFunction>(Symbol)) {
Defined = F;
break;
}
}
// If there are no definitions, and the undefined symbols disagree on
// the signature, there is not we can do since we don't know which one
// to use as the signature on the import.
if (!Defined) {
reportFunctionSignatureMismatch(SymName,
cast<FunctionSymbol>(Variants[0]),
cast<FunctionSymbol>(Variants[1]), true);
return;
}
for (auto *Symbol : Variants) {
if (Symbol != Defined) {
auto *F = cast<FunctionSymbol>(Symbol);
reportFunctionSignatureMismatch(SymName, F, Defined, false);
StringRef DebugName = Saver.save("unreachable:" + toString(*F));
replaceWithUnreachable(F, *F->Signature, DebugName);
}
}
}
}
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index d6cb13b..8c96c61 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -1,126 +1,127 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLD_WASM_SYMBOL_TABLE_H
#define LLD_WASM_SYMBOL_TABLE_H
#include "InputFiles.h"
#include "LTO.h"
#include "Symbols.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
namespace lld {
namespace wasm {
class InputSegment;
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
// files whose archive members are not yet loaded).
//
// We put all symbols of all files to a SymbolTable, and the
// SymbolTable selects the "best" symbols if there are name
// conflicts. For example, obviously, a defined symbol is better than
// an undefined symbol. Or, if there's a conflict between a lazy and a
// undefined, it'll read an archive member to read a real definition
// to replace the lazy symbol. The logic is implemented in the
// add*() functions, which are called by input files as they are parsed.
// There is one add* function per symbol type.
class SymbolTable {
public:
void wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap);
void addFile(InputFile *File);
void addCombinedLTOObject();
void reportRemainingUndefines();
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
Symbol *find(StringRef Name);
void replace(StringRef Name, Symbol* Sym);
void trace(StringRef Name);
Symbol *addDefinedFunction(StringRef Name, uint32_t Flags, InputFile *File,
InputFunction *Function);
Symbol *addDefinedData(StringRef Name, uint32_t Flags, InputFile *File,
InputSegment *Segment, uint32_t Address,
uint32_t Size);
Symbol *addDefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File,
InputGlobal *G);
Symbol *addDefinedEvent(StringRef Name, uint32_t Flags, InputFile *File,
InputEvent *E);
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,
InputFile *File, const WasmGlobalType *Type);
void addLazy(ArchiveFile *F, const llvm::object::Archive::Symbol *Sym);
bool addComdat(StringRef Name);
DefinedData *addSyntheticDataSymbol(StringRef Name, uint32_t Flags);
DefinedGlobal *addSyntheticGlobal(StringRef Name, uint32_t Flags,
InputGlobal *Global);
DefinedFunction *addSyntheticFunction(StringRef Name, uint32_t Flags,
InputFunction *Function);
DefinedData *addOptionalDataSymbol(StringRef Name, uint32_t Value,
uint32_t Flags);
void handleSymbolVariants();
void handleWeakUndefines();
std::vector<ObjFile *> ObjectFiles;
std::vector<SharedFile *> SharedFiles;
std::vector<BitcodeFile *> BitcodeFiles;
std::vector<InputFunction *> SyntheticFunctions;
std::vector<InputGlobal *> SyntheticGlobals;
private:
std::pair<Symbol *, bool> insert(StringRef Name, const InputFile *File);
std::pair<Symbol *, bool> insertName(StringRef Name);
bool getFunctionVariant(Symbol* Sym, const WasmSignature *Sig,
const InputFile *File, Symbol **Out);
InputFunction *replaceWithUnreachable(Symbol *Sym, const WasmSignature &Sig,
StringRef DebugName);
// Maps symbol names to index into the SymVector. -1 means that symbols
// is to not yet in the vector but it should have tracing enabled if it is
// ever added.
llvm::DenseMap<llvm::CachedHashStringRef, int> SymMap;
std::vector<Symbol *> SymVector;
// For certain symbols types, e.g. function symbols, we allow for muliple
// variants of the same symbol with different signatures.
llvm::DenseMap<llvm::CachedHashStringRef, std::vector<Symbol *>> SymVariants;
// Comdat groups define "link once" sections. If two comdat groups have the
// same name, only one of them is linked, and the other is ignored. This set
// is used to uniquify them.
llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
// For LTO.
std::unique_ptr<BitcodeCompiler> LTO;
};
extern SymbolTable *Symtab;
} // namespace wasm
} // namespace lld
#endif
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 3691b3f..98ff155 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -1,502 +1,504 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLD_WASM_SYMBOLS_H
#define LLD_WASM_SYMBOLS_H
#include "Config.h"
#include "lld/Common/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Wasm.h"
namespace lld {
namespace wasm {
// Shared string constants
// The default module name to use for symbol imports.
extern const char *DefaultModule;
// The name under which to import or export the wasm table.
extern const char *FunctionTableName;
using llvm::wasm::WasmSymbolType;
class InputFile;
class InputChunk;
class InputSegment;
class InputFunction;
class InputGlobal;
class InputEvent;
class InputSection;
class OutputSection;
#define INVALID_INDEX UINT32_MAX
// The base class for real symbol classes.
class Symbol {
public:
enum Kind {
DefinedFunctionKind,
DefinedDataKind,
DefinedGlobalKind,
DefinedEventKind,
SectionKind,
OutputSectionKind,
UndefinedFunctionKind,
UndefinedDataKind,
UndefinedGlobalKind,
LazyKind,
};
Kind kind() const { return SymbolKind; }
bool isDefined() const { return !isLazy() && !isUndefined(); }
bool isUndefined() const {
return SymbolKind == UndefinedFunctionKind ||
SymbolKind == UndefinedDataKind || SymbolKind == UndefinedGlobalKind;
}
bool isLazy() const { return SymbolKind == LazyKind; }
bool isLocal() const;
bool isWeak() const;
bool isHidden() const;
// True if this is an undefined weak symbol. This only works once
// all input files have been added.
bool isUndefWeak() const {
// See comment on lazy symbols for details.
return isWeak() && (isUndefined() || isLazy());
}
// Returns the symbol name.
StringRef getName() const { return Name; }
// Returns the file from which this symbol was created.
InputFile *getFile() const { return File; }
uint32_t getFlags() const { return Flags; }
InputChunk *getChunk() const;
// Indicates that the section or import for this symbol will be included in
// the final image.
bool isLive() const;
// Marks the symbol's InputChunk as Live, so that it will be included in the
// final image.
void markLive();
void setHidden(bool IsHidden);
// Get/set the index in the output symbol table. This is only used for
// relocatable output.
uint32_t getOutputSymbolIndex() const;
void setOutputSymbolIndex(uint32_t Index);
WasmSymbolType getWasmType() const;
bool isExported() const;
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
// unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
// are unreferenced except by other bitcode objects.
unsigned IsUsedInRegularObj : 1;
// True if ths symbol is explicity marked for export (i.e. via the -e/--export
// command line flag)
unsigned ForceExport : 1;
// False if LTO shouldn't inline whatever this symbol points to. If a symbol
// is overwritten after LTO, LTO shouldn't inline the symbol because it
// doesn't know the final contents of the symbol.
unsigned CanInline : 1;
// True if this symbol is specified by --trace-symbol option.
unsigned Traced : 1;
const WasmSignature* getSignature() const;
bool isInGOT() const { return GOTIndex != INVALID_INDEX; }
uint32_t getGOTIndex() const {
assert(GOTIndex != INVALID_INDEX);
return GOTIndex;
}
void setGOTIndex(uint32_t Index);
bool hasGOTIndex() const { return GOTIndex != INVALID_INDEX; }
protected:
Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
: IsUsedInRegularObj(false), ForceExport(false), CanInline(false),
Traced(false), Name(Name), SymbolKind(K), Flags(Flags), File(F),
Referenced(!Config->GcSections) {}
StringRef Name;
Kind SymbolKind;
uint32_t Flags;
InputFile *File;
uint32_t OutputSymbolIndex = INVALID_INDEX;
uint32_t GOTIndex = INVALID_INDEX;
bool Referenced;
};
class FunctionSymbol : public Symbol {
public:
static bool classof(const Symbol *S) {
return S->kind() == DefinedFunctionKind ||
S->kind() == UndefinedFunctionKind;
}
// Get/set the table index
void setTableIndex(uint32_t Index);
uint32_t getTableIndex() const;
bool hasTableIndex() const;
// Get/set the function index
uint32_t getFunctionIndex() const;
void setFunctionIndex(uint32_t Index);
bool hasFunctionIndex() const;
const WasmSignature *Signature;
protected:
FunctionSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
const WasmSignature *Sig)
: Symbol(Name, K, Flags, F), Signature(Sig) {}
uint32_t TableIndex = INVALID_INDEX;
uint32_t FunctionIndex = INVALID_INDEX;
};
class DefinedFunction : public FunctionSymbol {
public:
DefinedFunction(StringRef Name, uint32_t Flags, InputFile *F,
InputFunction *Function);
static bool classof(const Symbol *S) {
return S->kind() == DefinedFunctionKind;
}
InputFunction *Function;
};
class UndefinedFunction : public FunctionSymbol {
public:
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;
}
StringRef ImportName;
StringRef ImportModule;
+ bool IsCalledDirectly;
};
// Section symbols for output sections are different from those for input
// section. These are generated by the linker and point the OutputSection
// rather than an InputSection.
class OutputSectionSymbol : public Symbol {
public:
OutputSectionSymbol(const OutputSection *S)
: Symbol("", OutputSectionKind, llvm::wasm::WASM_SYMBOL_BINDING_LOCAL,
nullptr),
Section(S) {}
static bool classof(const Symbol *S) {
return S->kind() == OutputSectionKind;
}
const OutputSection *Section;
};
class SectionSymbol : public Symbol {
public:
SectionSymbol(uint32_t Flags, const InputSection *S, InputFile *F = nullptr)
: Symbol("", SectionKind, Flags, F), Section(S) {}
static bool classof(const Symbol *S) { return S->kind() == SectionKind; }
const OutputSectionSymbol *getOutputSectionSymbol() const;
const InputSection *Section;
};
class DataSymbol : public Symbol {
public:
static bool classof(const Symbol *S) {
return S->kind() == DefinedDataKind || S->kind() == UndefinedDataKind;
}
protected:
DataSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
: Symbol(Name, K, Flags, F) {}
};
class DefinedData : public DataSymbol {
public:
// Constructor for regular data symbols originating from input files.
DefinedData(StringRef Name, uint32_t Flags, InputFile *F,
InputSegment *Segment, uint32_t Offset, uint32_t Size)
: DataSymbol(Name, DefinedDataKind, Flags, F), Segment(Segment),
Offset(Offset), Size(Size) {}
// Constructor for linker synthetic data symbols.
DefinedData(StringRef Name, uint32_t Flags)
: DataSymbol(Name, DefinedDataKind, Flags, nullptr) {}
static bool classof(const Symbol *S) { return S->kind() == DefinedDataKind; }
// Returns the output virtual address of a defined data symbol.
uint32_t getVirtualAddress() const;
void setVirtualAddress(uint32_t VA);
// Returns the offset of a defined data symbol within its OutputSegment.
uint32_t getOutputSegmentOffset() const;
uint32_t getOutputSegmentIndex() const;
uint32_t getSize() const { return Size; }
InputSegment *Segment = nullptr;
protected:
uint32_t Offset = 0;
uint32_t Size = 0;
};
class UndefinedData : public DataSymbol {
public:
UndefinedData(StringRef Name, uint32_t Flags, InputFile *File = nullptr)
: DataSymbol(Name, UndefinedDataKind, Flags, File) {}
static bool classof(const Symbol *S) {
return S->kind() == UndefinedDataKind;
}
};
class GlobalSymbol : public Symbol {
public:
static bool classof(const Symbol *S) {
return S->kind() == DefinedGlobalKind || S->kind() == UndefinedGlobalKind;
}
const WasmGlobalType *getGlobalType() const { return GlobalType; }
// Get/set the global index
uint32_t getGlobalIndex() const;
void setGlobalIndex(uint32_t Index);
bool hasGlobalIndex() const;
protected:
GlobalSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
const WasmGlobalType *GlobalType)
: Symbol(Name, K, Flags, F), GlobalType(GlobalType) {}
const WasmGlobalType *GlobalType;
uint32_t GlobalIndex = INVALID_INDEX;
};
class DefinedGlobal : public GlobalSymbol {
public:
DefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File,
InputGlobal *Global);
static bool classof(const Symbol *S) {
return S->kind() == DefinedGlobalKind;
}
InputGlobal *Global;
};
class UndefinedGlobal : public GlobalSymbol {
public:
UndefinedGlobal(StringRef Name, StringRef ImportName, StringRef ImportModule,
uint32_t Flags, InputFile *File = nullptr,
const WasmGlobalType *Type = nullptr)
: GlobalSymbol(Name, UndefinedGlobalKind, Flags, File, Type),
ImportName(ImportName), ImportModule(ImportModule) {}
static bool classof(const Symbol *S) {
return S->kind() == UndefinedGlobalKind;
}
StringRef ImportName;
StringRef ImportModule;
};
// Wasm events are features that suspend the current execution and transfer the
// control flow to a corresponding handler. Currently the only supported event
// kind is exceptions.
//
// Event tags are values to distinguish different events. For exceptions, they
// can be used to distinguish different language's exceptions, i.e., all C++
// exceptions have the same tag. Wasm can generate code capable of doing
// different handling actions based on the tag of caught exceptions.
//
// A single EventSymbol object represents a single tag. C++ exception event
// symbol is a weak symbol generated in every object file in which exceptions
// are used, and has name '__cpp_exception' for linking.
class EventSymbol : public Symbol {
public:
static bool classof(const Symbol *S) { return S->kind() == DefinedEventKind; }
const WasmEventType *getEventType() const { return EventType; }
// Get/set the event index
uint32_t getEventIndex() const;
void setEventIndex(uint32_t Index);
bool hasEventIndex() const;
const WasmSignature *Signature;
protected:
EventSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
const WasmEventType *EventType, const WasmSignature *Sig)
: Symbol(Name, K, Flags, F), Signature(Sig), EventType(EventType) {}
const WasmEventType *EventType;
uint32_t EventIndex = INVALID_INDEX;
};
class DefinedEvent : public EventSymbol {
public:
DefinedEvent(StringRef Name, uint32_t Flags, InputFile *File,
InputEvent *Event);
static bool classof(const Symbol *S) { return S->kind() == DefinedEventKind; }
InputEvent *Event;
};
// LazySymbol represents a symbol that is not yet in the link, but we know where
// to find it if needed. If the resolver finds both Undefined and Lazy for the
// same name, it will ask the Lazy to load a file.
//
// A special complication is the handling of weak undefined symbols. They should
// not load a file, but we have to remember we have seen both the weak undefined
// and the lazy. We represent that with a lazy symbol with a weak binding. This
// means that code looking for undefined symbols normally also has to take lazy
// symbols into consideration.
class LazySymbol : public Symbol {
public:
LazySymbol(StringRef Name, uint32_t Flags, InputFile *File,
const llvm::object::Archive::Symbol &Sym)
: Symbol(Name, LazyKind, Flags, File), ArchiveSymbol(Sym) {}
static bool classof(const Symbol *S) { return S->kind() == LazyKind; }
void fetch();
// Lazy symbols can have a signature because they can replace an
// UndefinedFunction which which case we need to be able to preserve the
// signture.
// TODO(sbc): This repetition of the signature field is inelegant. Revisit
// the use of class hierarchy to represent symbol taxonomy.
const WasmSignature *Signature = nullptr;
private:
llvm::object::Archive::Symbol ArchiveSymbol;
};
// linker-generated symbols
struct WasmSym {
// __stack_pointer
// Global that holds the address of the top of the explicit value stack in
// linear memory.
static GlobalSymbol *StackPointer;
// __data_end
// Symbol marking the end of the data and bss.
static DefinedData *DataEnd;
// __heap_base
// Symbol marking the end of the data, bss and explicit stack. Any linear
// memory following this address is not used by the linked code and can
// therefore be used as a backing store for brk()/malloc() implementations.
static DefinedData *HeapBase;
// __wasm_call_ctors
// Function that directly calls all ctors in priority order.
static DefinedFunction *CallCtors;
// __wasm_apply_relocs
// Function that applies relocations to data segment post-instantiation.
static DefinedFunction *ApplyRelocs;
// __dso_handle
// Symbol used in calls to __cxa_atexit to determine current DLL
static DefinedData *DsoHandle;
// __table_base
// Used in PIC code for offset of indirect function table
static UndefinedGlobal *TableBase;
// __memory_base
// Used in PIC code for offset of global data
static UndefinedGlobal *MemoryBase;
};
// A buffer class that is large enough to hold any Symbol-derived
// object. We allocate memory using this class and instantiate a symbol
// using the placement new.
union SymbolUnion {
alignas(DefinedFunction) char A[sizeof(DefinedFunction)];
alignas(DefinedData) char B[sizeof(DefinedData)];
alignas(DefinedGlobal) char C[sizeof(DefinedGlobal)];
alignas(DefinedEvent) char D[sizeof(DefinedEvent)];
alignas(LazySymbol) char E[sizeof(LazySymbol)];
alignas(UndefinedFunction) char F[sizeof(UndefinedFunction)];
alignas(UndefinedData) char G[sizeof(UndefinedData)];
alignas(UndefinedGlobal) char H[sizeof(UndefinedGlobal)];
alignas(SectionSymbol) char I[sizeof(SectionSymbol)];
};
void printTraceSymbol(Symbol *Sym);
void printTraceSymbolUndefined(StringRef Name, const InputFile* File);
template <typename T, typename... ArgT>
T *replaceSymbol(Symbol *S, ArgT &&... Arg) {
static_assert(std::is_trivially_destructible<T>(),
"Symbol types must be trivially destructible");
static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
static_assert(alignof(T) <= alignof(SymbolUnion),
"SymbolUnion not aligned enough");
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
"Not a Symbol");
Symbol SymCopy = *S;
T *S2 = new (S) T(std::forward<ArgT>(Arg)...);
S2->IsUsedInRegularObj = SymCopy.IsUsedInRegularObj;
S2->ForceExport = SymCopy.ForceExport;
S2->CanInline = SymCopy.CanInline;
S2->Traced = SymCopy.Traced;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
if (S2->Traced)
printTraceSymbol(S2);
return S2;
}
} // namespace wasm
// Returns a symbol name for an error message.
std::string toString(const wasm::Symbol &Sym);
std::string toString(wasm::Symbol::Kind Kind);
std::string maybeDemangleSymbol(StringRef Name);
} // namespace lld
#endif

Event Timeline