2017-11-18 02:14:09 +08:00
|
|
|
//===- 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"
|
2018-02-21 05:08:28 +08:00
|
|
|
#include "Config.h"
|
2018-03-10 00:43:05 +08:00
|
|
|
#include "InputChunks.h"
|
2018-02-23 13:08:53 +08:00
|
|
|
#include "InputGlobal.h"
|
2018-01-31 09:45:47 +08:00
|
|
|
#include "MarkLive.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#include "SymbolTable.h"
|
|
|
|
#include "Writer.h"
|
2017-11-29 03:58:45 +08:00
|
|
|
#include "lld/Common/Args.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#include "lld/Common/ErrorHandler.h"
|
2017-11-29 04:39:17 +08:00
|
|
|
#include "lld/Common/Memory.h"
|
2018-03-13 21:30:04 +08:00
|
|
|
#include "lld/Common/Strings.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
#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"
|
2018-05-31 02:07:52 +08:00
|
|
|
#include "llvm/Support/TargetSelect.h"
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-01-31 09:45:47 +08:00
|
|
|
#define DEBUG_TYPE "lld"
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
using namespace llvm;
|
|
|
|
using namespace llvm::sys;
|
|
|
|
using namespace llvm::wasm;
|
|
|
|
|
|
|
|
using namespace lld;
|
|
|
|
using namespace lld::wasm;
|
|
|
|
|
2018-02-24 04:13:38 +08:00
|
|
|
Configuration *lld::wasm::Config;
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-02-24 04:13:38 +08:00
|
|
|
namespace {
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
// 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
|
|
|
|
};
|
|
|
|
|
2018-05-31 02:07:52 +08:00
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
class LinkerDriver {
|
|
|
|
public:
|
|
|
|
void link(ArrayRef<const char *> ArgsArr);
|
|
|
|
|
|
|
|
private:
|
2018-02-24 04:13:38 +08:00
|
|
|
void createFiles(opt::InputArgList &Args);
|
2017-11-18 02:14:09 +08:00
|
|
|
void addFile(StringRef Path);
|
|
|
|
void addLibrary(StringRef Name);
|
2018-07-24 07:51:19 +08:00
|
|
|
|
|
|
|
// True if we are in --whole-archive and --no-whole-archive.
|
|
|
|
bool InWholeArchive = false;
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
std::vector<InputFile *> Files;
|
|
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
|
|
|
raw_ostream &Error) {
|
2018-08-27 14:18:10 +08:00
|
|
|
errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
|
2017-11-18 02:14:09 +08:00
|
|
|
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>();
|
|
|
|
|
2018-05-31 02:07:52 +08:00
|
|
|
initLLVM();
|
2017-11-18 02:14:09 +08:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2018-05-16 06:01:54 +08:00
|
|
|
namespace {
|
2018-02-24 04:13:38 +08:00
|
|
|
class WasmOptTable : public llvm::opt::OptTable {
|
|
|
|
public:
|
|
|
|
WasmOptTable() : OptTable(OptInfo) {}
|
|
|
|
opt::InputArgList parse(ArrayRef<const char *> Argv);
|
|
|
|
};
|
2018-05-16 06:01:54 +08:00
|
|
|
} // namespace
|
2018-02-24 04:13:38 +08:00
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
// 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;
|
2018-05-11 02:19:02 +08:00
|
|
|
if (Arg->getOption().getID() == OPT_color_diagnostics) {
|
2017-11-18 02:14:09 +08:00
|
|
|
errorHandler().ColorDiagnostics = true;
|
2018-05-11 02:19:02 +08:00
|
|
|
} else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
|
2017-11-18 02:14:09 +08:00
|
|
|
errorHandler().ColorDiagnostics = false;
|
2018-05-11 02:19:02 +08:00
|
|
|
} else {
|
2017-11-18 02:14:09 +08:00
|
|
|
StringRef S = Arg->getValue();
|
|
|
|
if (S == "always")
|
|
|
|
errorHandler().ColorDiagnostics = true;
|
2018-05-11 02:19:02 +08:00
|
|
|
else if (S == "never")
|
2017-11-18 02:14:09 +08:00
|
|
|
errorHandler().ColorDiagnostics = false;
|
2018-05-11 02:19:02 +08:00
|
|
|
else if (S != "auto")
|
|
|
|
error("unknown option: --color-diagnostics=" + S);
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-05-30 11:51:56 +08:00
|
|
|
|
|
|
|
// Expand response files (arguments in the form of @<filename>)
|
|
|
|
cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Vec);
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
|
|
|
|
|
|
|
|
handleColorDiagnostics(Args);
|
|
|
|
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
|
|
|
|
error("unknown argument: " + Arg->getSpelling());
|
|
|
|
return Args;
|
|
|
|
}
|
|
|
|
|
2018-01-12 06:31:35 +08:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2018-07-24 07:51:19 +08:00
|
|
|
// Returns slices of MB by parsing MB as an archive file.
|
|
|
|
// Each slice consists of a member file in the archive.
|
2018-08-30 05:03:16 +08:00
|
|
|
std::vector<MemoryBufferRef> static getArchiveMembers(MemoryBufferRef MB) {
|
2018-07-24 07:51:19 +08:00
|
|
|
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)
|
2018-08-30 05:03:16 +08:00
|
|
|
fatal(MB.getBufferIdentifier() +
|
|
|
|
": Archive::children failed: " + toString(std::move(Err)));
|
2018-07-24 07:51:19 +08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
void LinkerDriver::addFile(StringRef Path) {
|
|
|
|
Optional<MemoryBufferRef> Buffer = readFile(Path);
|
|
|
|
if (!Buffer.hasValue())
|
|
|
|
return;
|
|
|
|
MemoryBufferRef MBRef = *Buffer;
|
|
|
|
|
2018-05-31 02:07:52 +08:00
|
|
|
switch (identify_magic(MBRef.getBuffer())) {
|
|
|
|
case file_magic::archive: {
|
2018-07-24 07:51:19 +08:00
|
|
|
// Handle -whole-archive.
|
|
|
|
if (InWholeArchive) {
|
|
|
|
for (MemoryBufferRef &M : getArchiveMembers(MBRef))
|
|
|
|
Files.push_back(createObjectFile(M));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-12 06:31:35 +08:00
|
|
|
SmallString<128> ImportFile = Path;
|
|
|
|
path::replace_extension(ImportFile, ".imports");
|
|
|
|
if (fs::exists(ImportFile))
|
|
|
|
readImportFile(ImportFile.str());
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
Files.push_back(make<ArchiveFile>(MBRef));
|
2018-01-12 06:31:35 +08:00
|
|
|
return;
|
|
|
|
}
|
2018-05-31 02:07:52 +08:00
|
|
|
case file_magic::bitcode:
|
2018-07-24 07:51:19 +08:00
|
|
|
case file_magic::wasm_object:
|
|
|
|
Files.push_back(createObjectFile(MBRef));
|
2018-05-31 02:07:52 +08:00
|
|
|
break;
|
|
|
|
default:
|
2018-07-24 07:51:19 +08:00
|
|
|
error("unknown file type: " + MBRef.getBufferIdentifier());
|
2018-05-31 02:07:52 +08:00
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-07-24 07:51:19 +08:00
|
|
|
case OPT_whole_archive:
|
|
|
|
InWholeArchive = true;
|
|
|
|
break;
|
|
|
|
case OPT_no_whole_archive:
|
|
|
|
InWholeArchive = false;
|
|
|
|
break;
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-12 01:52:28 +08:00
|
|
|
static StringRef getEntry(opt::InputArgList &Args, StringRef Default) {
|
2017-12-09 01:58:25 +08:00
|
|
|
auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
|
|
|
|
if (!Arg)
|
2017-12-12 01:52:28 +08:00
|
|
|
return Default;
|
2017-12-09 01:58:25 +08:00
|
|
|
if (Arg->getOption().getID() == OPT_no_entry)
|
|
|
|
return "";
|
|
|
|
return Arg->getValue();
|
|
|
|
}
|
|
|
|
|
2018-03-10 01:06:38 +08:00
|
|
|
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.
|
2018-06-29 00:53:53 +08:00
|
|
|
assert(FuncSym->FunctionType);
|
|
|
|
const WasmSignature &Sig = *FuncSym->FunctionType;
|
2018-03-10 01:06:38 +08:00
|
|
|
|
|
|
|
// Add a synthetic dummy for weak undefined functions. These dummies will
|
|
|
|
// be GC'd if not used as the target of any "call" instructions.
|
2018-03-13 21:30:04 +08:00
|
|
|
Optional<std::string> SymName = demangleItanium(Sym->getName());
|
2018-04-21 01:09:18 +08:00
|
|
|
StringRef DebugName =
|
2018-03-13 21:30:04 +08:00
|
|
|
Saver.save("undefined function " +
|
|
|
|
(SymName ? StringRef(*SymName) : Sym->getName()));
|
2018-04-21 01:09:18 +08:00
|
|
|
SyntheticFunction *Func =
|
|
|
|
make<SyntheticFunction>(Sig, Sym->getName(), DebugName);
|
2018-03-10 01:06:38 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-31 02:07:52 +08:00
|
|
|
// Force Sym to be entered in the output. Used for -u or equivalent.
|
2018-08-04 08:04:06 +08:00
|
|
|
static Symbol *handleUndefined(StringRef Name) {
|
|
|
|
Symbol *Sym = Symtab->find(Name);
|
|
|
|
if (!Sym)
|
|
|
|
return nullptr;
|
2018-05-31 02:07:52 +08:00
|
|
|
|
|
|
|
// Since symbol S may not be used inside the program, LTO may
|
|
|
|
// eliminate it. Mark the symbol as "used" to prevent it.
|
2018-08-04 08:04:06 +08:00
|
|
|
Sym->IsUsedInRegularObj = true;
|
2018-05-31 02:07:52 +08:00
|
|
|
|
2018-08-04 08:04:06 +08:00
|
|
|
if (auto *LazySym = dyn_cast<LazySymbol>(Sym))
|
|
|
|
LazySym->fetch();
|
|
|
|
|
|
|
|
return Sym;
|
2018-05-31 02:07:52 +08:00
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|
|
|
WasmOptTable Parser;
|
|
|
|
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
|
|
|
|
|
|
|
|
// Handle --help
|
|
|
|
if (Args.hasArg(OPT_help)) {
|
2018-02-24 04:24:40 +08:00
|
|
|
Parser.PrintHelp(outs(), ArgsArr[0], "LLVM Linker", false);
|
2017-11-18 02:14:09 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-24 04:24:28 +08:00
|
|
|
// Handle --version
|
|
|
|
if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
|
|
|
|
outs() << getLLDVersion() << "\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
// Parse and evaluate -mllvm options.
|
|
|
|
std::vector<const char *> V;
|
2017-11-29 06:17:39 +08:00
|
|
|
V.push_back("wasm-ld (LLVM option parsing)");
|
2017-11-18 02:14:09 +08:00
|
|
|
for (auto *Arg : Args.filtered(OPT_mllvm))
|
|
|
|
V.push_back(Arg->getValue());
|
|
|
|
cl::ParseCommandLineOptions(V.size(), V.data());
|
|
|
|
|
2017-11-29 03:58:45 +08:00
|
|
|
errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
|
2017-11-18 02:14:09 +08:00
|
|
|
|
|
|
|
Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
|
2018-03-13 21:12:03 +08:00
|
|
|
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
|
2018-05-31 02:07:52 +08:00
|
|
|
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
|
2017-12-12 01:52:43 +08:00
|
|
|
Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
|
2018-06-07 09:27:07 +08:00
|
|
|
Config->ExportAll = Args.hasArg(OPT_export_all);
|
2018-03-31 00:06:14 +08:00
|
|
|
Config->ExportTable = Args.hasArg(OPT_export_table);
|
2018-05-05 09:23:07 +08:00
|
|
|
errorHandler().FatalWarnings =
|
|
|
|
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
|
2017-11-18 02:14:09 +08:00
|
|
|
Config->ImportMemory = Args.hasArg(OPT_import_memory);
|
2018-03-31 00:06:14 +08:00
|
|
|
Config->ImportTable = Args.hasArg(OPT_import_table);
|
2018-05-31 02:07:52 +08:00
|
|
|
Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
|
|
|
|
Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
|
2018-05-19 07:28:05 +08:00
|
|
|
Config->Optimize = args::getInteger(Args, OPT_O, 0);
|
2017-11-18 02:14:09 +08:00
|
|
|
Config->OutputFile = Args.getLastArgValue(OPT_o);
|
2017-12-12 01:52:43 +08:00
|
|
|
Config->Relocatable = Args.hasArg(OPT_relocatable);
|
2018-01-31 09:45:47 +08:00
|
|
|
Config->GcSections =
|
|
|
|
Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, !Config->Relocatable);
|
[WebAssembly] Add a flag to control merging data segments
Merging data segments produces smaller code sizes because each segment
has some boilerplate. Therefore, merging data segments is generally the
right approach, especially with wasm where binaries are typically
delivered over the network.
However, when analyzing wasm binaries, it can be helpful to get a
conservative picture of which functions are using which data
segments[0]. Perhaps there is a large data segment that you didn't
expect to be included in the wasm, introduced by some library you're
using, and you'd like to know which library it was. In this scenario,
merging data segments only makes the analysis worse.
Alternatively, perhaps you will remove some dead functions by-hand[1]
that can't be statically proven dead by the compiler or lld, and
removing these functions might make some data garbage collect-able, and
you'd like to run `--gc-sections` again so that this now-unused data can
be collected. If the segments were originally merged, then a single use
of the merged data segment will entrench all of the data.
[0] https://github.com/rustwasm/twiggy
[1] https://github.com/fitzgen/wasm-snip
Patch by Nick Fitzgerald!
Differential Revision: https://reviews.llvm.org/D46417
llvm-svn: 332013
2018-05-11 02:23:51 +08:00
|
|
|
Config->MergeDataSegments =
|
|
|
|
Args.hasFlag(OPT_merge_data_segments, OPT_no_merge_data_segments,
|
|
|
|
!Config->Relocatable);
|
2018-01-31 09:45:47 +08:00
|
|
|
Config->PrintGcSections =
|
|
|
|
Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
|
2018-05-31 02:07:52 +08:00
|
|
|
Config->SaveTemps = Args.hasArg(OPT_save_temps);
|
2017-11-29 03:58:45 +08:00
|
|
|
Config->SearchPaths = args::getStrings(Args, OPT_L);
|
2017-11-18 02:14:09 +08:00
|
|
|
Config->StripAll = Args.hasArg(OPT_strip_all);
|
|
|
|
Config->StripDebug = Args.hasArg(OPT_strip_debug);
|
2018-08-18 03:42:46 +08:00
|
|
|
Config->CompressRelocTargets = Args.hasArg(OPT_compress_relocations);
|
2018-05-04 01:21:53 +08:00
|
|
|
Config->StackFirst = Args.hasArg(OPT_stack_first);
|
2018-05-31 02:07:52 +08:00
|
|
|
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);
|
2017-11-18 02:14:09 +08:00
|
|
|
errorHandler().Verbose = Args.hasArg(OPT_verbose);
|
|
|
|
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
|
|
|
|
|
2017-11-29 03:58:45 +08:00
|
|
|
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);
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-08-18 03:42:46 +08:00
|
|
|
if (!Config->StripDebug && !Config->StripAll && Config->CompressRelocTargets)
|
|
|
|
error("--compress-relocations is incompatible with output debug"
|
|
|
|
" information. Please pass --strip-debug or --strip-all");
|
2018-05-19 07:28:05 +08:00
|
|
|
|
2018-05-31 02:07:52 +08:00
|
|
|
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");
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
|
2018-01-12 06:31:35 +08:00
|
|
|
readImportFile(Arg->getValue());
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-02-17 06:58:19 +08:00
|
|
|
if (!Args.hasArg(OPT_INPUT)) {
|
|
|
|
error("no input files");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
if (Config->OutputFile.empty())
|
|
|
|
error("no output file specified");
|
|
|
|
|
2018-03-31 00:06:14 +08:00
|
|
|
if (Config->ImportTable && Config->ExportTable)
|
|
|
|
error("--import-table and --export-table may not be used together");
|
|
|
|
|
2018-01-31 09:45:47 +08:00
|
|
|
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");
|
2018-08-18 03:42:46 +08:00
|
|
|
if (Config->CompressRelocTargets)
|
|
|
|
error("-r -and --compress-relocations may not be used together");
|
2018-01-31 09:45:47 +08:00
|
|
|
if (Args.hasArg(OPT_undefined))
|
|
|
|
error("-r -and --undefined may not be used together");
|
|
|
|
}
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-01-20 09:44:45 +08:00
|
|
|
Symbol *EntrySym = nullptr;
|
2017-11-18 02:14:09 +08:00
|
|
|
if (!Config->Relocatable) {
|
2018-04-06 03:37:48 +08:00
|
|
|
llvm::wasm::WasmGlobal Global;
|
|
|
|
Global.Type = {WASM_TYPE_I32, true};
|
|
|
|
Global.InitExpr.Value.Int32 = 0;
|
|
|
|
Global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
|
2018-04-21 01:28:12 +08:00
|
|
|
Global.SymbolName = "__stack_pointer";
|
|
|
|
InputGlobal *StackPointer = make<InputGlobal>(Global, nullptr);
|
2018-02-23 13:08:53 +08:00
|
|
|
StackPointer->Live = true;
|
2018-02-14 04:14:26 +08:00
|
|
|
|
2018-02-23 13:08:53 +08:00
|
|
|
static WasmSignature NullSignature = {{}, WASM_TYPE_NORESULT};
|
2018-03-10 00:43:05 +08:00
|
|
|
|
2018-02-14 04:14:26 +08:00
|
|
|
// Add synthetic symbols before any others
|
|
|
|
WasmSym::CallCtors = Symtab->addSyntheticFunction(
|
2018-03-10 00:43:05 +08:00
|
|
|
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
|
|
|
make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
|
2018-06-07 09:27:07 +08:00
|
|
|
// 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
|
2018-02-23 13:08:53 +08:00
|
|
|
WasmSym::StackPointer = Symtab->addSyntheticGlobal(
|
|
|
|
"__stack_pointer", WASM_SYMBOL_VISIBILITY_HIDDEN, StackPointer);
|
2018-02-28 08:37:03 +08:00
|
|
|
WasmSym::HeapBase = Symtab->addSyntheticDataSymbol("__heap_base", 0);
|
2018-02-23 13:08:53 +08:00
|
|
|
WasmSym::DsoHandle = Symtab->addSyntheticDataSymbol(
|
|
|
|
"__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
|
2018-02-28 08:37:03 +08:00
|
|
|
WasmSym::DataEnd = Symtab->addSyntheticDataSymbol("__data_end", 0);
|
2017-11-18 02:14:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2018-05-31 02:07:52 +08:00
|
|
|
if (errorCount())
|
|
|
|
return;
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-08-04 08:04:06 +08:00
|
|
|
// Handle the `--undefined <sym>` options.
|
|
|
|
for (auto *Arg : Args.filtered(OPT_undefined))
|
|
|
|
handleUndefined(Arg->getValue());
|
|
|
|
|
2018-08-07 03:45:12 +08:00
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
2018-08-04 08:04:06 +08:00
|
|
|
if (!Config->Relocatable) {
|
|
|
|
// Add synthetic dummies for weak undefined functions.
|
2018-03-10 01:06:38 +08:00
|
|
|
handleWeakUndefines();
|
|
|
|
|
2018-08-04 08:04:06 +08:00
|
|
|
if (!Config->Entry.empty()) {
|
|
|
|
EntrySym = handleUndefined(Config->Entry);
|
|
|
|
if (!EntrySym)
|
|
|
|
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;
|
|
|
|
|
2018-05-31 02:07:52 +08:00
|
|
|
// Do link-time optimization if given files are LLVM bitcode files.
|
|
|
|
// This compiles bitcode files into real object files.
|
|
|
|
Symtab->addCombinedLTOObject();
|
|
|
|
if (errorCount())
|
|
|
|
return;
|
|
|
|
|
2018-01-20 09:44:45 +08:00
|
|
|
if (EntrySym)
|
|
|
|
EntrySym->setHidden(false);
|
|
|
|
|
2017-12-09 01:58:25 +08:00
|
|
|
if (errorCount())
|
|
|
|
return;
|
2017-11-18 02:14:09 +08:00
|
|
|
|
2018-01-31 09:45:47 +08:00
|
|
|
// Do size optimizations: garbage collection
|
2018-06-22 23:13:10 +08:00
|
|
|
markLive();
|
2018-01-31 09:45:47 +08:00
|
|
|
|
2017-11-18 02:14:09 +08:00
|
|
|
// Write the result to the file.
|
|
|
|
writeResult();
|
|
|
|
}
|