[lld] cc2da55 - [lld][WebAssembly] Add initial support for -Map/--print-map
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 16:12:57 PDT 2020
Author: Sam Clegg
Date: 2020-09-12T16:10:51-07:00
New Revision: cc2da5554b5ee5d5939222af263699a9d0bf2049
URL: https://github.com/llvm/llvm-project/commit/cc2da5554b5ee5d5939222af263699a9d0bf2049
DIFF: https://github.com/llvm/llvm-project/commit/cc2da5554b5ee5d5939222af263699a9d0bf2049.diff
LOG: [lld][WebAssembly] Add initial support for -Map/--print-map
Differential Revision: https://reviews.llvm.org/D77187
Added:
lld/test/wasm/map-file.s
lld/wasm/MapFile.cpp
lld/wasm/MapFile.h
Modified:
lld/test/ELF/map-file.s
lld/test/wasm/early-exit-for-bad-paths.s
lld/wasm/CMakeLists.txt
lld/wasm/Config.h
lld/wasm/Driver.cpp
lld/wasm/InputChunks.h
lld/wasm/Options.td
lld/wasm/OutputSections.cpp
lld/wasm/OutputSections.h
lld/wasm/Symbols.h
lld/wasm/Writer.cpp
Removed:
################################################################################
diff --git a/lld/test/ELF/map-file.s b/lld/test/ELF/map-file.s
index 1cd3b9087cbe..55b6b9e67281 100644
--- a/lld/test/ELF/map-file.s
+++ b/lld/test/ELF/map-file.s
@@ -11,7 +11,7 @@
# RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t -M | FileCheck --match-full-lines --strict-whitespace %s
# RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t --print-map | FileCheck --match-full-lines -strict-whitespace %s
# RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t -Map=%t.map
-# RUN: FileCheck -strict-whitespace %s < %t.map
+# RUN: FileCheck -match-full-lines -strict-whitespace %s < %t.map
.global _start
_start:
diff --git a/lld/test/wasm/early-exit-for-bad-paths.s b/lld/test/wasm/early-exit-for-bad-paths.s
index 2866bfa62f86..21cec318e449 100644
--- a/lld/test/wasm/early-exit-for-bad-paths.s
+++ b/lld/test/wasm/early-exit-for-bad-paths.s
@@ -4,10 +4,16 @@
# RUN: FileCheck %s -check-prefixes=NO-DIR-OUTPUT,CHECK
# RUN: not wasm-ld %t.o -o %s/dir_is_a_file 2>&1 | \
# RUN: FileCheck %s -check-prefixes=DIR-IS-OUTPUT,CHECK
-# TODO(sbc): check similar check for -Map file once we add that option
+
+# RUN: not wasm-ld %t.o -o %t -Map=does_not_exist/output 2>&1 | \
+# RUN: FileCheck %s -check-prefixes=NO-DIR-MAP,CHECK
+# RUN: not wasm-ld %t.o -o %t -Map=%s/dir_is_a_file 2>&1 | \
+# RUN: FileCheck %s -check-prefixes=DIR-IS-MAP,CHECK
# NO-DIR-OUTPUT: error: cannot open output file does_not_exist/output:
# DIR-IS-OUTPUT: error: cannot open output file {{.*}}/dir_is_a_file:
+# NO-DIR-MAP: error: cannot open map file does_not_exist/output:
+# DIR-IS-MAP: error: cannot open map file {{.*}}/dir_is_a_file:
# We should exit before doing the actual link. If an undefined symbol error is
# discovered we haven't bailed out early as expected.
diff --git a/lld/test/wasm/map-file.s b/lld/test/wasm/map-file.s
new file mode 100644
index 000000000000..c2ec089ccb13
--- /dev/null
+++ b/lld/test/wasm/map-file.s
@@ -0,0 +1,47 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t1.o
+# RUN: wasm-ld %t1.o -o %t -M | FileCheck --match-full-lines --strict-whitespace %s
+# RUN: wasm-ld %t1.o -o %t -print-map | FileCheck --match-full-lines --strict-whitespace %s
+# RUN: wasm-ld %t1.o -o %t -Map=%t.map
+# RUN: FileCheck --match-full-lines --strict-whitespace %s < %t.map
+
+bar:
+ .functype bar () -> ()
+ i32.const somedata
+ end_function
+
+ .globl _start
+_start:
+ .functype _start () -> ()
+ call bar
+ end_function
+
+.section .data.somedata,"",@
+somedata:
+ .int32 123
+.size somedata, 4
+
+.section .debug_info,"",@
+ .int32 bar
+
+# CHECK: Addr Off Size Out In Symbol
+# CHECK-NEXT: - 8 6 TYPE
+# CHECK-NEXT: - e 5 FUNCTION
+# CHECK-NEXT: - 13 7 TABLE
+# CHECK-NEXT: - 1a 5 MEMORY
+# CHECK-NEXT: - 1f a GLOBAL
+# CHECK-NEXT: - 29 15 EXPORT
+# CHECK-NEXT: - 3e 15 CODE
+# CHECK-NEXT: - 3f 9 {{.*}}{{/|\\}}map-file.s.tmp1.o:(bar)
+# CHECK-NEXT: - 3f 9 bar
+# CHECK-NEXT: - 48 9 {{.*}}{{/|\\}}map-file.s.tmp1.o:(_start)
+# CHECK-NEXT: - 48 9 _start
+# CHECK-NEXT: - 53 d DATA
+# CHECK-NEXT: 400 54 4 .data
+# CHECK-NEXT: 400 5a 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.data.somedata)
+# CHECK-NEXT: 400 5a 4 somedata
+# CHECK-NEXT: - 60 12 CUSTOM(.debug_info)
+# CHECK-NEXT: - 72 17 CUSTOM(name)
+
+# RUN: not wasm-ld %t1.o -o /dev/null -Map=/ 2>&1 \
+# RUN: | FileCheck -check-prefix=FAIL %s
+# FAIL: wasm-ld: error: cannot open map file /
diff --git a/lld/wasm/CMakeLists.txt b/lld/wasm/CMakeLists.txt
index cd46f0a826ac..37902ededa0c 100644
--- a/lld/wasm/CMakeLists.txt
+++ b/lld/wasm/CMakeLists.txt
@@ -7,6 +7,7 @@ add_lld_library(lldWasm
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
index e8d018f09bf6..cd6d57333a21 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -58,6 +58,7 @@ struct Configuration {
llvm::StringRef 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
index 7307aaa3f7be..09318421574c 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -344,6 +344,7 @@ static void readConfigs(opt::InputArgList &args) {
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->mapFile = args.getLastArgValue(OPT_Map);
config->optimize = args::getInteger(args, OPT_O, 0);
config->outputFile = args.getLastArgValue(OPT_o);
config->relocatable = args.hasArg(OPT_relocatable);
@@ -410,6 +411,9 @@ static void readConfigs(opt::InputArgList &args) {
for (StringRef s : arg->getValues())
config->features->push_back(std::string(s));
}
+
+ if (args.hasArg(OPT_print_map))
+ config->mapFile = "-";
}
// Some Config members do not directly correspond to any particular
@@ -795,7 +799,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// find that it failed because there was a mistake in their command-line.
if (auto e = tryCreateFile(config->outputFile))
error("cannot open output file " + config->outputFile + ": " + e.message());
- // TODO(sbc): add check for map file too once we add support for that.
+ if (auto e = tryCreateFile(config->mapFile))
+ error("cannot open map file " + config->mapFile + ": " + e.message());
if (errorCount())
return;
diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index cadff6883fa4..be91b19ed452 100644
--- a/lld/wasm/InputChunks.h
+++ b/lld/wasm/InputChunks.h
@@ -57,6 +57,8 @@ class InputChunk {
void writeRelocations(llvm::raw_ostream &os) const;
ObjFile *file;
+ OutputSection *outputSec = nullptr;
+ // Offset withing the output section
int32_t outputOffset = 0;
// Signals that the section is part of the output. The garbage collector,
@@ -214,8 +216,6 @@ class InputSection : public InputChunk {
StringRef getDebugName() const override { return StringRef(); }
uint32_t getComdat() const override { return UINT32_MAX; }
- OutputSection *outputSec = nullptr;
-
protected:
ArrayRef<uint8_t> data() const override { return section.Content; }
diff --git a/lld/wasm/MapFile.cpp b/lld/wasm/MapFile.cpp
new file mode 100644
index 000000000000..a08d2a97d74a
--- /dev/null
+++ b/lld/wasm/MapFile.cpp
@@ -0,0 +1,148 @@
+//===- 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 -Map option. It shows lists in order and
+// hierarchically the output sections, input sections, input files and
+// symbol:
+//
+// Addr Off Size Out In Symbol
+// - 00000015 10 .text
+// - 0000000e 10 test.o:(.text)
+// - 00000000 5 local
+// - 00000000 5 f(int)
+//
+//===----------------------------------------------------------------------===//
+
+#include "MapFile.h"
+#include "InputFiles.h"
+#include "OutputSections.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "lld/Common/Strings.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace lld;
+using namespace lld::wasm;
+
+using SymbolMapTy = DenseMap<const InputChunk *, SmallVector<Symbol *, 4>>;
+
+// Print out the first three columns of a line.
+static void writeHeader(raw_ostream &os, int64_t vma, uint64_t lma,
+ uint64_t size) {
+ // Not all entries in the map has a virtual memory address (e.g. functions)
+ if (vma == -1)
+ os << format(" - %8llx %8llx ", lma, size);
+ else
+ os << format("%8llx %8llx %8llx ", vma, lma, size);
+}
+
+// Returns a list of all symbols that we want to print out.
+static std::vector<Symbol *> getSymbols() {
+ std::vector<Symbol *> v;
+ for (InputFile *file : symtab->objectFiles)
+ for (Symbol *b : file->getSymbols())
+ if (auto *dr = dyn_cast<Symbol>(b))
+ if ((!isa<SectionSymbol>(dr)) && dr->isLive() &&
+ (dr->getFile() == file))
+ v.push_back(dr);
+ return v;
+}
+
+// Returns a map from sections to their symbols.
+static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> syms) {
+ SymbolMapTy ret;
+ for (Symbol *dr : syms)
+ ret[dr->getChunk()].push_back(dr);
+ return ret;
+}
+
+// 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<Symbol *, std::string>
+getSymbolStrings(ArrayRef<Symbol *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN(0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ auto &chunk = *syms[i]->getChunk();
+ uint64_t fileOffset = chunk.outputSec->getOffset() + chunk.outputOffset;
+ uint64_t vma = -1;
+ uint64_t size = 0;
+ if (auto *DD = dyn_cast<DefinedData>(syms[i])) {
+ vma = DD->getVirtualAddress();
+ size = DD->getSize();
+ fileOffset += DD->offset;
+ }
+ if (auto *DF = dyn_cast<DefinedFunction>(syms[i])) {
+ size = DF->function->getSize();
+ }
+ writeHeader(os, vma, fileOffset, size);
+ os.indent(16) << toString(*syms[i]);
+ });
+
+ DenseMap<Symbol *, std::string> 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(ArrayRef<OutputSection *> outputSections) {
+ if (config->mapFile.empty())
+ return;
+
+ // Open a map file for writing.
+ std::error_code ec;
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
+ if (ec) {
+ error("cannot open " + config->mapFile + ": " + ec.message());
+ return;
+ }
+
+ // Collect symbol info that we want to print out.
+ std::vector<Symbol *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<Symbol *, std::string> symStr = getSymbolStrings(syms);
+
+ // Print out the header line.
+ os << " Addr Off Size Out In Symbol\n";
+
+ for (OutputSection *osec : outputSections) {
+ writeHeader(os, -1, osec->getOffset(), osec->getSize());
+ os << toString(*osec) << '\n';
+ if (auto *code = dyn_cast<CodeSection>(osec)) {
+ for (auto *chunk : code->functions) {
+ writeHeader(os, -1, chunk->outputSec->getOffset() + chunk->outputOffset,
+ chunk->getSize());
+ os.indent(8) << toString(chunk) << '\n';
+ for (Symbol *sym : sectionSyms[chunk])
+ os << symStr[sym] << '\n';
+ }
+ } else if (auto *data = dyn_cast<DataSection>(osec)) {
+ for (auto *oseg : data->segments) {
+ writeHeader(os, oseg->startVA, data->getOffset() + oseg->sectionOffset,
+ oseg->size);
+ os << oseg->name << '\n';
+ for (auto *chunk : oseg->inputSegments) {
+ writeHeader(os, oseg->startVA + chunk->outputSegmentOffset,
+ chunk->outputSec->getOffset() + chunk->outputOffset,
+ chunk->getSize());
+ os.indent(8) << toString(chunk) << '\n';
+ for (Symbol *sym : sectionSyms[chunk])
+ os << symStr[sym] << '\n';
+ }
+ }
+ }
+ }
+}
diff --git a/lld/wasm/MapFile.h b/lld/wasm/MapFile.h
new file mode 100644
index 000000000000..ef2cc783a6c2
--- /dev/null
+++ b/lld/wasm/MapFile.h
@@ -0,0 +1,21 @@
+//===- 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
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lld {
+namespace wasm {
+class OutputSection;
+void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections);
+} // namespace wasm
+} // namespace lld
+
+#endif
diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index 16c784f74828..27d54c5cdc64 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -66,6 +66,8 @@ def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
+defm Map: Eq<"Map", "Print a link map to the specified file">;
+
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
@@ -84,6 +86,9 @@ defm print_gc_sections: B<"print-gc-sections",
"List removed unused sections",
"Do not list removed unused sections">;
+def print_map: F<"print-map">,
+ HelpText<"Print a link map to the standard output">;
+
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
@@ -181,6 +186,7 @@ def: JoinedOrSeparate<["-"], "e">, Alias<entry>;
def: J<"entry=">, Alias<entry>;
def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
def: Flag<["-"], "i">, Alias<initial_memory>;
+def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
def: Flag<["-"], "r">, Alias<relocatable>;
def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;
def: Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">;
diff --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp
index a936562992dd..dbdabddb9320 100644
--- a/lld/wasm/OutputSections.cpp
+++ b/lld/wasm/OutputSections.cpp
@@ -87,6 +87,7 @@ void CodeSection::finalizeContents() {
bodySize = codeSectionHeader.size();
for (InputFunction *func : functions) {
+ func->outputSec = this;
func->outputOffset = bodySize;
func->calculateSize();
bodySize += func->getSize();
@@ -166,9 +167,11 @@ void DataSection::finalizeContents() {
log("Data segment: size=" + Twine(segment->size) + ", startVA=" +
Twine::utohexstr(segment->startVA) + ", name=" + segment->name);
- for (InputSegment *inputSeg : segment->inputSegments)
+ for (InputSegment *inputSeg : segment->inputSegments) {
+ inputSeg->outputSec = this;
inputSeg->outputOffset = segment->sectionOffset + segment->header.size() +
inputSeg->outputSegmentOffset;
+ }
}
createHeader(bodySize);
@@ -227,8 +230,8 @@ void CustomSection::finalizeContents() {
os.flush();
for (InputSection *section : inputSections) {
- section->outputOffset = payloadSize;
section->outputSec = this;
+ section->outputOffset = payloadSize;
payloadSize += section->getSize();
}
diff --git a/lld/wasm/OutputSections.h b/lld/wasm/OutputSections.h
index 1fcb5723df98..444116dac7d8 100644
--- a/lld/wasm/OutputSections.h
+++ b/lld/wasm/OutputSections.h
@@ -40,6 +40,7 @@ class OutputSection {
void createHeader(size_t bodySize);
virtual bool isNeeded() const { return true; }
virtual size_t getSize() const = 0;
+ virtual size_t getOffset() { return offset; }
virtual void writeTo(uint8_t *buf) = 0;
virtual void finalizeContents() = 0;
virtual uint32_t getNumRelocations() const { return 0; }
@@ -60,6 +61,10 @@ class CodeSection : public OutputSection {
explicit CodeSection(ArrayRef<InputFunction *> functions)
: OutputSection(llvm::wasm::WASM_SEC_CODE), functions(functions) {}
+ static bool classof(const OutputSection *sec) {
+ return sec->type == llvm::wasm::WASM_SEC_CODE;
+ }
+
size_t getSize() const override { return header.size() + bodySize; }
void writeTo(uint8_t *buf) override;
uint32_t getNumRelocations() const override;
@@ -67,8 +72,9 @@ class CodeSection : public OutputSection {
bool isNeeded() const override { return functions.size() > 0; }
void finalizeContents() override;
-protected:
ArrayRef<InputFunction *> functions;
+
+protected:
std::string codeSectionHeader;
size_t bodySize = 0;
};
@@ -78,6 +84,10 @@ class DataSection : public OutputSection {
explicit DataSection(ArrayRef<OutputSegment *> segments)
: OutputSection(llvm::wasm::WASM_SEC_DATA), segments(segments) {}
+ static bool classof(const OutputSection *sec) {
+ return sec->type == llvm::wasm::WASM_SEC_DATA;
+ }
+
size_t getSize() const override { return header.size() + bodySize; }
void writeTo(uint8_t *buf) override;
uint32_t getNumRelocations() const override;
@@ -85,8 +95,9 @@ class DataSection : public OutputSection {
bool isNeeded() const override;
void finalizeContents() override;
-protected:
ArrayRef<OutputSegment *> segments;
+
+protected:
std::string dataSectionHeader;
size_t bodySize = 0;
};
@@ -103,6 +114,11 @@ class CustomSection : public OutputSection {
CustomSection(std::string name, ArrayRef<InputSection *> inputSections)
: OutputSection(llvm::wasm::WASM_SEC_CUSTOM, name),
inputSections(inputSections) {}
+
+ static bool classof(const OutputSection *sec) {
+ return sec->type == llvm::wasm::WASM_SEC_CUSTOM;
+ }
+
size_t getSize() const override {
return header.size() + nameData.size() + payloadSize;
}
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 73f555217f26..eed481a0b44d 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -284,9 +284,9 @@ class DefinedData : public DataSymbol {
uint64_t getSize() const { return size; }
InputSegment *segment = nullptr;
+ uint32_t offset = 0;
protected:
- uint64_t offset = 0;
uint64_t size = 0;
};
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index fb4b79c5f634..82b1aec8d1e9 100644
--- 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"
@@ -1137,6 +1138,9 @@ void Writer::run() {
log("-- finalizeSections");
finalizeSections();
+ log("-- writeMapFile");
+ writeMapFile(outputSections);
+
log("-- openFile");
openFile();
if (errorCount())
More information about the llvm-commits
mailing list