forked from OSchip/llvm-project
[lld][WebAssembly] Add initial support for -Map/--print-map
Differential Revision: https://reviews.llvm.org/D77187
This commit is contained in:
parent
c2f8bc986f
commit
cc2da5554b
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 /
|
|
@ -7,6 +7,7 @@ add_lld_library(lldWasm
|
|||
InputChunks.cpp
|
||||
InputFiles.cpp
|
||||
LTO.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
OutputSections.cpp
|
||||
Relocations.cpp
|
||||
|
|
|
@ -58,6 +58,7 @@ struct Configuration {
|
|||
llvm::StringRef thinLTOJobs;
|
||||
|
||||
llvm::StringRef entry;
|
||||
llvm::StringRef mapFile;
|
||||
llvm::StringRef outputFile;
|
||||
llvm::StringRef thinLTOCacheDir;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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">;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue