diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt --- a/lld/MachO/CMakeLists.txt +++ b/lld/MachO/CMakeLists.txt @@ -24,6 +24,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 @@ -88,6 +88,7 @@ uint32_t timeTraceGranularity; std::string progName; 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 @@ -826,6 +826,7 @@ for (const Arg *arg : args.filtered(OPT_U)) symtab->addDynamicLookup(arg->getValue()); + config->mapFile = args.getLastArgValue(OPT_map); config->outputFile = args.getLastArgValue(OPT_o, "a.out"); config->headerPad = args::getHex(args, OPT_headerpad, /*Default=*/32); config->headerPadMaxInstallNames = 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,151 @@ +//===- 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" +#include "llvm/Support/Parallel.h" + +using namespace llvm; +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 (Symbol *sym : file->symbols) { + if (sym == nullptr) + continue; + 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()); + parallelForEachN(0, syms.size(), [&](size_t i) { + raw_string_ostream os(str[i]); + os << toString(*syms[i]); + }); + + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = std::move(str[i]); + return ret; +} + +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", + getArchitectureName(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/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -500,7 +500,6 @@ def map : Separate<["-"], "map">, MetaVarName<"">, HelpText<"Writes all symbols and their addresses to ">, - Flags<[HelpHidden]>, Group; def dependency_info : Separate<["-"], "dependency_info">, MetaVarName<"">, 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" @@ -926,6 +927,7 @@ createLoadCommands(); finalizeAddressses(); finalizeLinkEditSegment(); + writeMapFile(); openFile(); if (errorCount()) return; diff --git a/lld/test/MachO/map-file.s b/lld/test/MachO/map-file.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/map-file.s @@ -0,0 +1,50 @@ +# REQUIRES: x86 +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o + +# RUN: %lld -map %t/map %t/test.o %t/foo.o -o %t/test-map +# RUN: llvm-objdump --syms --section-headers %t/test-map > %t/objdump +# RUN: cat %t/objdump %t/map > %t/out +# RUN: FileCheck %s < %t/out + +#--- foo.s +.section __TEXT,obj +.globl _foo +_foo: + +#--- test.s +.comm _number, 1 +.globl _main +_main: + ret + +# CHECK: Sections: +# CHECK-NEXT: Idx Name Size VMA Type +# CHECK-NEXT: 0 __text {{[0-9a-f]+}} [[#%x,TEXT:]] TEXT +# CHECK-NEXT: 1 obj {{[0-9a-f]+}} [[#%x,DATA:]] DATA +# CHECK-NEXT: 2 __common {{[0-9a-f]+}} [[#%x,BSS:]] BSS + +# CHECK: SYMBOL TABLE: +# CHECK-NEXT: [[#%x,MAIN:]] g F __TEXT,__text _main +# CHECK-NEXT: [[#%x,NUMBER:]] g O __DATA,__common _number +# CHECK-NEXT: [[#%x,FOO:]] g O __TEXT,obj _foo + +# CHECK-NEXT: # Path: {{.*}}{{/|\\}}map-file.s.tmp/test-map +# CHECK-NEXT: # Arch: x86_64 +# CHECK-NEXT: # Object files: +# CHECK-NEXT: [ 0] linker synthesized +# CHECK-NEXT: [ 1] {{.*}}{{/|\\}}map-file.s.tmp/test.o +# CHECK-NEXT: [ 2] {{.*}}{{/|\\}}map-file.s.tmp/foo.o + +# CHECK-NEXT: # Sections: +# CHECK-NEXT: # Address Size Segment Section +# CHECK-NEXT: 0x[[#TEXT]] 0x{{[0-9a-f]+}} __TEXT __text +# CHECK-NEXT: 0x[[#DATA]] 0x{{[0-9a-f]+}} __TEXT obj +# CHECK-NEXT: 0x[[#BSS]] 0x{{[0-9a-f]+}} __DATA __common + +# CHECK-NEXT: # Symbols: +# CHECK-NEXT: # Address File Name +# CHECK-NEXT: 0x[[#NUMBER]] [ 1] _number +# CHECK-NEXT: 0x[[#MAIN]] [ 1] _main +# CHECK-NEXT: 0x[[#FOO]] [ 2] _foo