diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -23,6 +23,7 @@ Symbols.cpp SyntheticSections.cpp Target.cpp + MapFile.cpp Writer.cpp LINK_COMPONENTS diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -84,6 +84,7 @@ uint32_t dylibCompatibilityVersion = 0; uint32_t dylibCurrentVersion = 0; llvm::StringRef installName; + llvm::StringRef mapFile; llvm::StringRef outputFile; llvm::StringRef ltoObjPath; bool demangle = false; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -827,6 +827,7 @@ for (auto *arg : args.filtered(OPT_U)) symtab->addDynamicLookup(arg->getValue()); + config->mapFile = args.getLastArgValue(OPT_map); config->outputFile = args.getLastArgValue(OPT_o, "a.out"); config->installName = args.getLastArgValue(OPT_install_name, config->outputFile); diff --git a/lld/MachO/MapFile.h b/lld/MachO/MapFile.h new file mode 100644 --- /dev/null +++ b/lld/MachO/MapFile.h @@ -0,0 +1,18 @@ +//===- 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_MACHO_MAPFILE_H +#define LLD_MACHO_MAPFILE_H + +namespace lld { +namespace macho { +void writeMapFile(); +} // namespace macho +} // namespace lld + +#endif diff --git a/lld/MachO/MapFile.cpp b/lld/MachO/MapFile.cpp new file mode 100644 --- /dev/null +++ b/lld/MachO/MapFile.cpp @@ -0,0 +1,153 @@ +//===- 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 outputFile, arch, input files, output sections and +// symbol: +// +// # Path: test +// # Arch: x86_84 +// # Object files: +// [ 0] linker synthesized +// [ 1] a.o +// # Sections: +// # Address Size Segment Section +// 0x1000005C0 0x0000004C __TEXT __text +// # Symbols: +// # Address File Name +// 0x1000005C0 [ 1] _main +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "Config.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "OutputSection.h" +#include "OutputSegment.h" +#include "Symbols.h" +#include "Target.h" + +using namespace llvm; +using namespace llvm::MachO; +using namespace llvm::sys; +using namespace lld; +using namespace lld::macho; + +using SymbolMapTy = DenseMap>; + +// Returns a map from sections to their symbols. +static SymbolMapTy getSectionSyms(ArrayRef syms) { + SymbolMapTy ret; + for (Defined *dr : syms) + ret[dr->isec].push_back(dr); + + // Sort symbols by address. We want to print out symbols in the + // order in the output file rather than the order they appeared + // in the input files. + for (auto &it : ret) + llvm::stable_sort(it.second, [](Defined *a, Defined *b) { + return a->getVA() < b->getVA(); + }); + return ret; +} + +// Returns a list of all symbols that we want to print out. +static std::vector getSymbols() { + std::vector v; + for (InputFile *file : inputFiles) + if (isa(file)) + for (lld::macho::Symbol *sym : file->symbols) + if (auto *d = dyn_cast(sym)) + if (d->isec && d->getFile() == file) + v.push_back(d); + 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()); + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = toString(*syms[i]); + return ret; +} + +static llvm::StringRef getArchName(Architecture Arch) { + switch (Arch) { + case AK_x86_64: + return "x86_84"; + case AK_arm64: + return "arm64"; + default: + fatal("missing or unsupported arch"); + } +} + +void macho::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::OF_None); + if (ec) { + error("cannot open " + config->mapFile + ": " + ec.message()); + return; + } + + // Dump output path + os << format("# Path: %s\n", config->outputFile.str().c_str()); + + // Dump output architecure + os << format("# Arch: %s\n", getArchName(config->target.Arch).str().c_str()); + + // Dump table of object files + os << "# Object files:\n"; + os << format("[%3u] %s\n", 0, (const char *)"linker synthesized"); + uint32_t fileIndex = 1; + DenseMap readerToFileOrdinal; + for (InputFile *file : inputFiles) { + if (isa(file)) { + os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str()); + readerToFileOrdinal[file] = fileIndex++; + } + } + + // Collect symbol info that we want to print out. + std::vector syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap symStr = getSymbolStrings(syms); + + // Dump table of sections + os << "# Sections:\n"; + os << "# Address\tSize \tSegment\tSection\n"; + for (OutputSegment *seg : outputSegments) + for (OutputSection *osec : seg->getSections()) { + if (osec->isHidden()) + continue; + + os << format("0x%08llX\t0x%08llX\t%s\t%s\n", osec->addr, osec->getSize(), + seg->name.str().c_str(), osec->name.str().c_str()); + } + + // Dump table of symbols + os << "# Symbols:\n"; + os << "# Address\t File Name\n"; + for (InputSection *isec : inputSections) { + for (macho::Symbol *sym : sectionSyms[isec]) { + os << format("0x%08llX\t[%3u] %s\n", sym->getVA(), + readerToFileOrdinal[sym->getFile()], symStr[sym].c_str()); + } + } + + // TODO: when we implement -dead_strip, we should dump dead stripped symbols +} diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -10,6 +10,7 @@ #include "Config.h" #include "InputFiles.h" #include "InputSection.h" +#include "MapFile.h" #include "MergedOutputSection.h" #include "OutputSection.h" #include "OutputSegment.h" @@ -886,6 +887,8 @@ // addresses and offsets. assignAddresses(linkEditSegment); + writeMapFile(); + openFile(); if (errorCount()) return;