[lld][WebAssembly] Add initial support for -Map/--print-map

Differential Revision: https://reviews.llvm.org/D77187
This commit is contained in:
Sam Clegg 2020-03-27 16:52:27 -07:00
parent c2f8bc986f
commit cc2da5554b
14 changed files with 268 additions and 10 deletions

View File

@ -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:

View File

@ -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.

47
lld/test/wasm/map-file.s Normal file
View File

@ -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 /

View File

@ -7,6 +7,7 @@ add_lld_library(lldWasm
InputChunks.cpp
InputFiles.cpp
LTO.cpp
MapFile.cpp
MarkLive.cpp
OutputSections.cpp
Relocations.cpp

View File

@ -58,6 +58,7 @@ struct Configuration {
llvm::StringRef thinLTOJobs;
llvm::StringRef entry;
llvm::StringRef mapFile;
llvm::StringRef outputFile;
llvm::StringRef thinLTOCacheDir;

View File

@ -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;

View File

@ -57,6 +57,8 @@ public:
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 @@ public:
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; }

148
lld/wasm/MapFile.cpp Normal file
View File

@ -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';
}
}
}
}
}

21
lld/wasm/MapFile.h Normal file
View File

@ -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

View File

@ -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">;

View File

@ -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();
}

View File

@ -40,6 +40,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
bool isNeeded() const override;
void finalizeContents() override;
protected:
ArrayRef<OutputSegment *> segments;
protected:
std::string dataSectionHeader;
size_t bodySize = 0;
};
@ -103,6 +114,11 @@ public:
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;
}

View File

@ -284,9 +284,9 @@ public:
uint64_t getSize() const { return size; }
InputSegment *segment = nullptr;
uint32_t offset = 0;
protected:
uint64_t offset = 0;
uint64_t size = 0;
};

View File

@ -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())