Skip to content

Commit

Permalink
[lld-link] Support /map option, matching link.exe 's /map output format
Browse files Browse the repository at this point in the history
Added support for /map and /map:[filepath].
The output was derived from Microsoft's Link.exe output when using that same option.
Note that /MAPINFO support was not added.

The previous implementation of MapFile.cpp/.h was meant for /lldmap, and was renamed to LLDMapFile.cpp/.h
MapFile.cpp/.h is now for /MAP
However, a small fix was added to lldmap, replacing a std::sort with std::stable_sort to enforce reproducibility.

Differential Revision: https://reviews.llvm.org/D70557
  • Loading branch information
sylvain-audi committed Mar 24, 2020
1 parent 177dd63 commit b91905a
Show file tree
Hide file tree
Showing 11 changed files with 521 additions and 72 deletions.
1 change: 1 addition & 0 deletions lld/COFF/CMakeLists.txt
Expand Up @@ -14,6 +14,7 @@ add_lld_library(lldCOFF
DriverUtils.cpp
ICF.cpp
InputFiles.cpp
LLDMapFile.cpp
LTO.cpp
MapFile.cpp
MarkLive.cpp
Expand Down
3 changes: 3 additions & 0 deletions lld/COFF/Config.h
Expand Up @@ -182,6 +182,9 @@ struct Configuration {
llvm::StringMap<int> order;

// Used for /lldmap.
std::string lldmapFile;

// Used for /map.
std::string mapFile;

// Used for /thinlto-index-only:
Expand Down
18 changes: 13 additions & 5 deletions lld/COFF/Driver.cpp
Expand Up @@ -707,14 +707,15 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
return debugTypes;
}

static std::string getMapFile(const opt::InputArgList &args) {
auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
static std::string getMapFile(const opt::InputArgList &args,
opt::OptSpecifier os, opt::OptSpecifier osFile) {
auto *arg = args.getLastArg(os, osFile);
if (!arg)
return "";
if (arg->getOption().getID() == OPT_lldmap_file)
if (arg->getOption().getID() == osFile.getID())
return arg->getValue();

assert(arg->getOption().getID() == OPT_lldmap);
assert(arg->getOption().getID() == os.getID());
StringRef outFile = config->outputFile;
return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
Expand Down Expand Up @@ -1564,7 +1565,14 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
if (config->mingw || config->debugDwarf)
config->warnLongSectionNames = false;

config->mapFile = getMapFile(args);
config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
config->mapFile = getMapFile(args, OPT_map, OPT_map_file);

if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
warn("/lldmap and /map have the same output file '" + config->mapFile +
"'.\n>>> ignoring /lldmap");
config->lldmapFile.clear();
}

if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
Expand Down
123 changes: 123 additions & 0 deletions lld/COFF/LLDMapFile.cpp
@@ -0,0 +1,123 @@
//===- LLDMapFile.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 /lldmap option. It shows lists in order and
// hierarchically the output sections, input sections, input files and
// symbol:
//
// Address Size Align Out File Symbol
// 00201000 00000015 4 .text
// 00201000 0000000e 4 test.o:(.text)
// 0020100e 00000000 0 local
// 00201005 00000000 0 f(int)
//
//===----------------------------------------------------------------------===//

#include "LLDMapFile.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Threads.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;
using namespace llvm::object;
using namespace lld;
using namespace lld::coff;

using SymbolMapTy =
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;

static constexpr char indent8[] = " "; // 8 spaces
static constexpr char indent16[] = " "; // 16 spaces

// Print out the first three columns of a line.
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
uint64_t align) {
os << format("%08llx %08llx %5lld ", addr, size, align);
}

// Returns a list of all symbols that we want to print out.
static std::vector<DefinedRegular *> getSymbols() {
std::vector<DefinedRegular *> v;
for (ObjFile *file : ObjFile::instances)
for (Symbol *b : file->getSymbols())
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
if (sym && !sym->getCOFFSymbol().isSectionDefinition())
v.push_back(sym);
return v;
}

// Returns a map from sections to their symbols.
static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
SymbolMapTy ret;
for (DefinedRegular *s : syms)
ret[s->getChunk()].push_back(s);

// Sort symbols by address.
for (auto &it : ret) {
SmallVectorImpl<DefinedRegular *> &v = it.second;
std::stable_sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
return a->getRVA() < b->getRVA();
});
}
return ret;
}

// Construct a map from symbols to their stringified representations.
static DenseMap<DefinedRegular *, std::string>
getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
std::vector<std::string> str(syms.size());
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
writeHeader(os, syms[i]->getRVA(), 0, 0);
os << indent16 << toString(*syms[i]);
});

DenseMap<DefinedRegular *, 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::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
if (config->lldmapFile.empty())
return;

std::error_code ec;
raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
if (ec)
fatal("cannot open " + config->lldmapFile + ": " + ec.message());

// Collect symbol info that we want to print out.
std::vector<DefinedRegular *> syms = getSymbols();
SymbolMapTy sectionSyms = getSectionSyms(syms);
DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);

// Print out the header line.
os << "Address Size Align Out In Symbol\n";

// Print out file contents.
for (OutputSection *sec : outputSections) {
writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
os << sec->name << '\n';

for (Chunk *c : sec->chunks) {
auto *sc = dyn_cast<SectionChunk>(c);
if (!sc)
continue;

writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
<< ")\n";
for (DefinedRegular *sym : sectionSyms[sc])
os << symStr[sym] << '\n';
}
}
}
21 changes: 21 additions & 0 deletions lld/COFF/LLDMapFile.h
@@ -0,0 +1,21 @@
//===- LLDMapFile.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_COFF_LLDMAPFILE_H
#define LLD_COFF_LLDMAPFILE_H

#include "llvm/ADT/ArrayRef.h"

namespace lld {
namespace coff {
class OutputSection;
void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
}
}

#endif

0 comments on commit b91905a

Please sign in to comment.