diff --git a/lld/wasm/CMakeLists.txt b/lld/wasm/CMakeLists.txt --- a/lld/wasm/CMakeLists.txt +++ b/lld/wasm/CMakeLists.txt @@ -11,6 +11,7 @@ InputChunks.cpp InputFiles.cpp LTO.cpp + MapFile.cpp MarkLive.cpp OutputSections.cpp Relocations.cpp diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -55,6 +55,7 @@ unsigned ThinLTOJobs; llvm::StringRef Entry; + llvm::StringRef MapFile; llvm::StringRef OutputFile; llvm::StringRef ThinLTOCacheDir; diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -348,6 +348,9 @@ Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize); + if (Args.hasArg(OPT_print_map)) + Config->MapFile = "-"; + if (auto *Arg = Args.getLastArg(OPT_features)) { Config->Features = llvm::Optional>(std::vector()); diff --git a/lld/wasm/MapFile.h b/lld/wasm/MapFile.h new file mode 100644 --- /dev/null +++ b/lld/wasm/MapFile.h @@ -0,0 +1,19 @@ +//===- MapFile.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_MAPFILE_H +#define LLD_WASM_MAPFILE_H + +namespace lld { +namespace wasm { +void writeMapFile(); +void writeCrossReferenceTable(); +} // namespace wasm +} // namespace lld + +#endif diff --git a/lld/wasm/MapFile.cpp b/lld/wasm/MapFile.cpp new file mode 100644 --- /dev/null +++ b/lld/wasm/MapFile.cpp @@ -0,0 +1,97 @@ +//===- MapFile.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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the -print-map option. It shows a list of global data +// symbols in the generated wasm file in increasing order of linear memory +// address: +// +// Address Size Align Symbol +// 400 fa0 1 p +// 13a0 4 1 q +// 13a8 40 1 __libc +// 13f4 4 1 __stdin_used +// 13f4 4 1 __stdout_used +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Strings.h" +#include "lld/Common/Threads.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::wasm; + +// Print out the first three columns of a line. +static void writeHeader(raw_ostream &OS, uint32_t VMA, uint32_t Size) { + OS << format("%8llx %8llx ", VMA, Size); +} + +// Returns a list of all symbols that we want to print out. +static std::vector getSymbols() { + std::vector V; + for (Symbol *B : Symtab->getSymbols()) + if (auto *DR = dyn_cast(B)) + if (DR->isLive()) + V.push_back(DR); + return V; +} + +// Construct a map from symbols to their stringified representations. +// Demangling symbols (which is what toString() does) is slow, so +// we do that in batch using parallel-for. +static DenseMap +getSymbolStrings(ArrayRef Syms) { + std::vector Str(Syms.size()); + parallelForEachN(0, Syms.size(), [&](size_t I) { + raw_string_ostream OS(Str[I]); + uint32_t VMA = Syms[I]->getVirtualAddress(); + writeHeader(OS, VMA, Syms[I]->getSize()); + OS << toString(*Syms[I]); + }); + + DenseMap Ret; + for (size_t I = 0, E = Syms.size(); I < E; ++I) + Ret[Syms[I]] = std::move(Str[I]); + return Ret; +} + +void lld::wasm::writeMapFile() { + if (Config->MapFile.empty()) + return; + + // Open a map file for writing. + std::error_code EC; + raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); + if (EC) { + error("cannot open " + Config->MapFile + ": " + EC.message()); + return; + } + + // Collect symbol info that we want to print out. + std::vector Syms = getSymbols(); + DenseMap SymStr = getSymbolStrings(Syms); + + std::sort(Syms.begin(), Syms.end(), [](DefinedData *A, DefinedData *B) { + return A->getVirtualAddress() < B->getVirtualAddress(); + }); + + // Print out the header line. + OS << " Address Size Symbol\n"; + + for (DefinedData *Sym : Syms) { + OS << SymStr[Sym] << '\n'; + } +} diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -64,6 +64,9 @@ def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">; +def print_map: F<"print-map">, + HelpText<"Print a link map to the standard output">; + def no_threads: F<"no-threads">, HelpText<"Do not run the linker multi-threaded">; @@ -174,6 +177,7 @@ def: J<"entry=">, Alias; def: Flag<["-"], "E">, Alias, HelpText<"Alias for --export-dynamic">; def: Flag<["-"], "i">, Alias; +def: Flag<["-"], "M">, Alias; def: Flag<["-"], "m">, Alias; def: Flag<["-"], "r">, Alias; def: Flag<["-"], "s">, Alias, HelpText<"Alias for --strip-all">; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -11,6 +11,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "MapFile.h" #include "OutputSections.h" #include "OutputSegment.h" #include "Relocations.h" @@ -823,6 +824,8 @@ if (errorCount()) return; + writeMapFile(); + if (Error E = Buffer->commit()) fatal("failed to write the output file: " + toString(std::move(E))); }