summaryrefslogtreecommitdiff
path: root/wasm/Driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wasm/Driver.cpp')
-rw-r--r--wasm/Driver.cpp288
1 files changed, 184 insertions, 104 deletions
diff --git a/wasm/Driver.cpp b/wasm/Driver.cpp
index 329b5ae80a9c..fab4c0c4ed8b 100644
--- a/wasm/Driver.cpp
+++ b/wasm/Driver.cpp
@@ -31,6 +31,7 @@
#define DEBUG_TYPE "lld"
using namespace llvm;
+using namespace llvm::object;
using namespace llvm::sys;
using namespace llvm::wasm;
@@ -78,7 +79,7 @@ private:
bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
- errorHandler().LogName = sys::path::filename(Args[0]);
+ errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
errorHandler().ErrorOS = &Error;
errorHandler().ColorDiagnostics = Error.has_colors();
errorHandler().ErrorLimitExceededMsg =
@@ -186,8 +187,7 @@ static void readImportFile(StringRef Filename) {
// 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::vector<MemoryBufferRef> static getArchiveMembers(MemoryBufferRef MB) {
std::unique_ptr<Archive> File =
CHECK(Archive::create(MB),
MB.getBufferIdentifier() + ": failed to parse archive");
@@ -205,8 +205,8 @@ std::vector<MemoryBufferRef> static getArchiveMembers(
V.push_back(MBRef);
}
if (Err)
- fatal(MB.getBufferIdentifier() + ": Archive::children failed: " +
- toString(std::move(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())
@@ -306,17 +306,14 @@ static void handleWeakUndefines() {
// It is possible for undefined functions not to have a signature (eg. if
// added via "--undefined"), but weak undefined ones do have a signature.
- assert(FuncSym->FunctionType);
- const WasmSignature &Sig = *FuncSym->FunctionType;
+ assert(FuncSym->Signature);
+ const WasmSignature &Sig = *FuncSym->Signature;
// Add a synthetic dummy for weak undefined functions. These dummies will
// be GC'd if not used as the target of any "call" instructions.
- Optional<std::string> SymName = demangleItanium(Sym->getName());
- StringRef DebugName =
- Saver.save("undefined function " +
- (SymName ? StringRef(*SymName) : Sym->getName()));
- SyntheticFunction *Func =
- make<SyntheticFunction>(Sig, Sym->getName(), DebugName);
+ std::string SymName = toString(*Sym);
+ StringRef DebugName = Saver.save("undefined function " + SymName);
+ auto *Func = make<SyntheticFunction>(Sig, Sym->getName(), DebugName);
Func->setBody(UnreachableFn);
// 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).
@@ -328,51 +325,24 @@ static void handleWeakUndefines() {
}
}
-// Force Sym to be entered in the output. Used for -u or equivalent.
-static Symbol *addUndefined(StringRef Name) {
- Symbol *S = Symtab->addUndefinedFunction(Name, 0, nullptr, nullptr);
-
- // Since symbol S may not be used inside the program, LTO may
- // eliminate it. Mark the symbol as "used" to prevent it.
- S->IsUsedInRegularObj = true;
-
- return S;
-}
-
-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(), ArgsArr[0], "LLVM Linker", false);
- return;
- }
-
- // Handle --version
- if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
- outs() << getLLDVersion() << "\n";
- return;
- }
-
- // 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);
-
+// 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(opt::InputArgList &Args) {
Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
+ 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->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
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);
@@ -384,10 +354,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
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);
@@ -404,8 +376,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->MaxMemory = args::getInteger(Args, OPT_max_memory, 0);
Config->ZStackSize =
args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize);
+}
- Config->CompressRelocTargets = Config->Optimize > 0 && !Config->Relocatable;
+// 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));
@@ -414,13 +392,8 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (Config->ThinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
- if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
- readImportFile(Arg->getValue());
-
- if (!Args.hasArg(OPT_INPUT)) {
- error("no input files");
- return;
- }
+ if (Config->Pie && Config->Shared)
+ error("-shared and -pie may not be used together");
if (Config->OutputFile.empty())
error("no output file specified");
@@ -433,46 +406,151 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
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");
}
+}
- Symbol *EntrySym = nullptr;
- if (!Config->Relocatable) {
+// 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, 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"));
+
+ // 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";
- InputGlobal *StackPointer = make<InputGlobal>(Global, nullptr);
+ auto *StackPointer = make<InputGlobal>(Global, nullptr);
StackPointer->Live = true;
-
- static WasmSignature NullSignature = {{}, WASM_TYPE_NORESULT};
-
- // Add synthetic symbols before any others
- WasmSym::CallCtors = Symtab->addSyntheticFunction(
- "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
- make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
+ // 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::DsoHandle = Symtab->addSyntheticDataSymbol(
- "__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
WasmSym::DataEnd = Symtab->addSyntheticDataSymbol("__data_end", 0);
- // For now, since we don't actually use the start function as the
- // wasm start symbol, we don't need to care about it signature.
- if (!Config->Entry.empty())
- EntrySym = addUndefined(Config->Entry);
+ // 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);
+}
+
+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 the `--undefined <sym>` options.
- for (auto *Arg : Args.filtered(OPT_undefined))
- addUndefined(Arg->getValue());
+ // Handle --version
+ if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
+ outs() << getLLDVersion() << "\n";
+ return;
}
+ // 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);
+
+ setConfigs(Args);
+ checkOptions(Args);
+
+ if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
+ readImportFile(Arg->getValue());
+
+ if (!Args.hasArg(OPT_INPUT)) {
+ error("no input files");
+ return;
+ }
+
+ 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->ExportDynamic = true;
+ Config->AllowUndefined = true;
+ }
+
+ if (!Config->Relocatable)
+ createSyntheticSymbols();
+
createFiles(Args);
if (errorCount())
return;
@@ -484,44 +562,46 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (errorCount())
return;
- // Add synthetic dummies for weak undefined functions.
- if (!Config->Relocatable)
- handleWeakUndefines();
+ // Handle the `--undefined <sym>` options.
+ for (auto *Arg : Args.filtered(OPT_undefined))
+ handleUndefined(Arg->getValue());
- // Handle --export.
+ // Handle the `--export <sym>` options
+ // This works like --undefined but also exports the symbol if its found
for (auto *Arg : Args.filtered(OPT_export)) {
- StringRef Name = Arg->getValue();
- Symbol *Sym = Symtab->find(Name);
+ Symbol *Sym = handleUndefined(Arg->getValue());
if (Sym && Sym->isDefined())
Sym->ForceExport = true;
else if (!Config->AllowUndefined)
- error("symbol exported via --export not found: " + Name);
+ error(Twine("symbol exported via --export not found: ") +
+ Arg->getValue());
}
- // Do link-time optimization if given files are LLVM bitcode files.
- // This compiles bitcode files into real object files.
- Symtab->addCombinedLTOObject();
- if (errorCount())
- return;
+ Symbol *EntrySym = nullptr;
+ if (!Config->Relocatable) {
+ // Add synthetic dummies for weak undefined functions.
+ handleWeakUndefines();
- // Make sure we have resolved all symbols.
- if (!Config->Relocatable && !Config->AllowUndefined) {
- Symtab->reportRemainingUndefines();
- } else {
- // Even when using --allow-undefined we still want to report the absence of
- // our initial set of undefined symbols (i.e. the entry point and symbols
- // specified via --undefined).
- // Part of the reason for this is that these function don't have signatures
- // so which means they cannot be written as wasm function imports.
- for (auto *Arg : Args.filtered(OPT_undefined)) {
- Symbol *Sym = Symtab->find(Arg->getValue());
- if (!Sym->isDefined())
- error("symbol forced with --undefined not found: " + Sym->getName());
+ if (!Config->Shared && !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 (EntrySym && !EntrySym->isDefined())
- error("entry symbol not defined (pass --no-entry to supress): " +
- EntrySym->getName());
+
+ // Make sure we have resolved all symbols.
+ if (!Config->AllowUndefined)
+ Symtab->reportRemainingUndefines();
}
+
+ if (errorCount())
+ return;
+
+ // Do link-time optimization if given files are LLVM bitcode files.
+ // This compiles bitcode files into real object files.
+ Symtab->addCombinedLTOObject();
if (errorCount())
return;