summaryrefslogtreecommitdiff
path: root/wasm/Driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wasm/Driver.cpp')
-rw-r--r--wasm/Driver.cpp619
1 files changed, 0 insertions, 619 deletions
diff --git a/wasm/Driver.cpp b/wasm/Driver.cpp
deleted file mode 100644
index fab4c0c4ed8b..000000000000
--- a/wasm/Driver.cpp
+++ /dev/null
@@ -1,619 +0,0 @@
-//===- Driver.cpp ---------------------------------------------------------===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#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/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/ArgList.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/Process.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));
- 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, StringRef Default) {
- auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
- if (!Arg)
- return Default;
- if (Arg->getOption().getID() == OPT_no_entry)
- return "";
- return Arg->getValue();
-}
-
-static const uint8_t UnreachableFn[] = {
- 0x03 /* ULEB length */, 0x00 /* ULEB num locals */,
- 0x00 /* opcode unreachable */, 0x0b /* opcode end */
-};
-
-// 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.
-static void handleWeakUndefines() {
- for (Symbol *Sym : Symtab->getSymbols()) {
- if (!Sym->isUndefined() || !Sym->isWeak())
- continue;
- auto *FuncSym = dyn_cast<FunctionSymbol>(Sym);
- if (!FuncSym)
- continue;
-
- // 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->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.
- 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).
- Func->setTableIndex(0);
- Symtab->SyntheticFunctions.emplace_back(Func);
- // Hide our dummy to prevent export.
- uint32_t Flags = WASM_SYMBOL_VISIBILITY_HIDDEN;
- replaceSymbol<DefinedFunction>(Sym, Sym->getName(), Flags, nullptr, Func);
- }
-}
-
-// 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);
- 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->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);
- 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);
-}
-
-// 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, 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";
- 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);
-}
-
-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;
- }
-
- // 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;
-
- // 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());
-
- // Handle the `--export <sym>` options
- // This works like --undefined but also exports the symbol if its found
- for (auto *Arg : Args.filtered(OPT_export)) {
- Symbol *Sym = handleUndefined(Arg->getValue());
- if (Sym && Sym->isDefined())
- Sym->ForceExport = true;
- else if (!Config->AllowUndefined)
- error(Twine("symbol exported via --export not found: ") +
- Arg->getValue());
- }
-
- Symbol *EntrySym = nullptr;
- if (!Config->Relocatable) {
- // Add synthetic dummies for weak undefined functions.
- handleWeakUndefines();
-
- 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);
- }
-
- // 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;
-
- if (EntrySym)
- EntrySym->setHidden(false);
-
- if (errorCount())
- return;
-
- // Do size optimizations: garbage collection
- markLive();
-
- // Write the result to the file.
- writeResult();
-}