293 lines · cpp
1//===- MapFile.cpp --------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the -map option, which maps address ranges to their10// respective contents, plus the input file these contents were originally from.11// The contents (typically symbols) are listed in address order. Dead-stripped12// contents are included as well.13//14// # Path: test15// # Arch: x86_8416// # Object files:17// [ 0] linker synthesized18// [ 1] a.o19// # Sections:20// # Address Size Segment Section21// 0x1000005C0 0x0000004C __TEXT __text22// # Symbols:23// # Address Size File Name24// 0x1000005C0 0x00000001 [ 1] _main25// # Dead Stripped Symbols:26// # Size File Name27// <<dead>> 0x00000001 [ 1] _foo28//29//===----------------------------------------------------------------------===//30 31#include "MapFile.h"32#include "ConcatOutputSection.h"33#include "Config.h"34#include "InputFiles.h"35#include "InputSection.h"36#include "OutputSegment.h"37#include "Symbols.h"38#include "SyntheticSections.h"39#include "Target.h"40#include "lld/Common/ErrorHandler.h"41#include "llvm/ADT/DenseMap.h"42#include "llvm/Support/Parallel.h"43#include "llvm/Support/TimeProfiler.h"44 45using namespace llvm;46using namespace llvm::sys;47using namespace lld;48using namespace lld::macho;49 50struct CStringInfo {51 uint32_t fileIndex;52 StringRef str;53};54 55struct MapInfo {56 SmallVector<InputFile *> files;57 SmallVector<Defined *> deadSymbols;58 DenseMap<const OutputSection *,59 SmallVector<std::pair<uint64_t /*addr*/, CStringInfo>>>60 liveCStringsForSection;61 SmallVector<CStringInfo> deadCStrings;62};63 64static MapInfo gatherMapInfo() {65 MapInfo info;66 for (InputFile *file : inputFiles) {67 bool isReferencedFile = false;68 69 if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) {70 uint32_t fileIndex = info.files.size() + 1;71 72 // Gather the dead symbols. We don't have to bother with the live ones73 // because we will pick them up as we iterate over the OutputSections74 // later.75 for (Symbol *sym : file->symbols) {76 if (auto *d = dyn_cast_or_null<Defined>(sym))77 // Only emit the prevailing definition of a symbol. Also, don't emit78 // the symbol if it is part of a cstring section (we use the literal79 // value instead, similar to ld64)80 if (d->isec() && d->getFile() == file &&81 !isa<CStringInputSection>(d->isec())) {82 isReferencedFile = true;83 if (!d->isLive())84 info.deadSymbols.push_back(d);85 }86 }87 88 // Gather all the cstrings (both live and dead). A CString(Output)Section89 // doesn't provide us a way of figuring out which InputSections its90 // cstring contents came from, so we need to build up that mapping here.91 for (const Section *sec : file->sections) {92 for (const Subsection &subsec : sec->subsections) {93 if (auto isec = dyn_cast<CStringInputSection>(subsec.isec)) {94 auto &liveCStrings = info.liveCStringsForSection[isec->parent];95 for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {96 if (piece.live)97 liveCStrings.push_back({isec->parent->addr + piece.outSecOff,98 {fileIndex, isec->getStringRef(i)}});99 else100 info.deadCStrings.push_back({fileIndex, isec->getStringRef(i)});101 isReferencedFile = true;102 }103 } else {104 break;105 }106 }107 }108 } else if (const auto *dylibFile = dyn_cast<DylibFile>(file)) {109 isReferencedFile = dylibFile->isReferenced();110 }111 112 if (isReferencedFile)113 info.files.push_back(file);114 }115 116 // cstrings are not stored in sorted order in their OutputSections, so we sort117 // them here.118 for (auto &liveCStrings : info.liveCStringsForSection)119 parallelSort(liveCStrings.second, llvm::less_first());120 return info;121}122 123// We use this instead of `toString(const InputFile *)` as we don't want to124// include the dylib install name in our output.125static void printFileName(raw_fd_ostream &os, const InputFile *f) {126 if (f->archiveName.empty())127 os << f->getName();128 else129 os << f->archiveName << "(" << path::filename(f->getName()) + ")";130}131 132// For printing the contents of the __stubs and __la_symbol_ptr sections.133static void printStubsEntries(134 raw_fd_ostream &os,135 const DenseMap<lld::macho::InputFile *, uint32_t> &readerToFileOrdinal,136 const OutputSection *osec, size_t entrySize) {137 for (const Symbol *sym : in.stubs->getEntries())138 os << format("0x%08llX\t0x%08zX\t[%3u] %s\n",139 osec->addr + sym->stubsIndex * entrySize, entrySize,140 readerToFileOrdinal.lookup(sym->getFile()),141 sym->getName().str().data());142}143 144static void printNonLazyPointerSection(raw_fd_ostream &os,145 NonLazyPointerSectionBase *osec) {146 // ld64 considers stubs to belong to particular files, but considers GOT147 // entries to be linker-synthesized. Not sure why they made that decision, but148 // I think we can follow suit unless there's demand for better symbol-to-file149 // associations.150 for (const Symbol *sym : osec->getEntries())151 os << format("0x%08llX\t0x%08zX\t[ 0] non-lazy-pointer-to-local: %s\n",152 osec->addr + sym->gotIndex * target->wordSize,153 target->wordSize, sym->getName().str().data());154}155 156static uint64_t getSymSizeForMap(Defined *sym) {157 if (sym->identicalCodeFoldingKind == Symbol::ICFFoldKind::Body)158 return 0;159 return sym->size;160}161 162void macho::writeMapFile() {163 if (config->mapFile.empty())164 return;165 166 TimeTraceScope timeScope("Write map file");167 168 // Open a map file for writing.169 std::error_code ec;170 raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);171 if (ec) {172 error("cannot open " + config->mapFile + ": " + ec.message());173 return;174 }175 176 os << format("# Path: %s\n", config->outputFile.str().c_str());177 os << format("# Arch: %s\n",178 getArchitectureName(config->arch()).str().c_str());179 180 MapInfo info = gatherMapInfo();181 182 os << "# Object files:\n";183 os << format("[%3u] %s\n", 0, (const char *)"linker synthesized");184 uint32_t fileIndex = 1;185 DenseMap<lld::macho::InputFile *, uint32_t> readerToFileOrdinal;186 for (InputFile *file : info.files) {187 os << format("[%3u] ", fileIndex);188 printFileName(os, file);189 os << "\n";190 readerToFileOrdinal[file] = fileIndex++;191 }192 193 os << "# Sections:\n";194 os << "# Address\tSize \tSegment\tSection\n";195 for (OutputSegment *seg : outputSegments)196 for (OutputSection *osec : seg->getSections()) {197 if (osec->isHidden())198 continue;199 200 os << format("0x%08llX\t0x%08llX\t%s\t%s\n", osec->addr, osec->getSize(),201 seg->name.str().c_str(), osec->name.str().c_str());202 }203 204 // Helper lambda that prints all symbols from one ConcatInputSection.205 auto printOne = [&](const ConcatInputSection *isec) {206 for (Defined *sym : isec->symbols) {207 if (!(isPrivateLabel(sym->getName()) && getSymSizeForMap(sym) == 0)) {208 os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(),209 getSymSizeForMap(sym),210 readerToFileOrdinal.lookup(sym->getFile()),211 sym->getName().str().data());212 }213 }214 };215 // Shared function to print one or two arrays of ConcatInputSection in216 // ascending outSecOff order. The second array is optional; if provided, we217 // interleave the printing in sorted order without allocating a merged temp218 // array.219 auto printIsecArrSyms = [&](ArrayRef<ConcatInputSection *> arr1,220 ArrayRef<ConcatInputSection *> arr2 = {}) {221 // Print both arrays in sorted order, interleaving as necessary.222 while (!arr1.empty() || !arr2.empty()) {223 if (!arr1.empty() && (arr2.empty() || arr1.front()->outSecOff <=224 arr2.front()->outSecOff)) {225 printOne(arr1.front());226 arr1 = arr1.drop_front();227 } else if (!arr2.empty()) {228 printOne(arr2.front());229 arr2 = arr2.drop_front();230 }231 }232 };233 234 os << "# Symbols:\n";235 os << "# Address\tSize \tFile Name\n";236 for (const OutputSegment *seg : outputSegments) {237 for (const OutputSection *osec : seg->getSections()) {238 if (auto *textOsec = dyn_cast<TextOutputSection>(osec)) {239 printIsecArrSyms(textOsec->inputs, textOsec->getThunks());240 } else if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {241 printIsecArrSyms(concatOsec->inputs);242 } else if (is_contained(in.cStringSections, osec)) {243 const auto &liveCStrings = info.liveCStringsForSection.lookup(osec);244 uint64_t lastAddr = 0; // strings will never start at address 0, so this245 // is a sentinel value246 for (const auto &[addr, info] : liveCStrings) {247 uint64_t size = 0;248 if (addr != lastAddr)249 size = info.str.size() + 1; // include null terminator250 lastAddr = addr;251 os << format("0x%08llX\t0x%08llX\t[%3u] literal string: ", addr, size,252 info.fileIndex);253 os.write_escaped(info.str) << "\n";254 }255 } else if (osec == (void *)in.unwindInfo) {256 os << format("0x%08llX\t0x%08llX\t[ 0] compact unwind info\n",257 osec->addr, osec->getSize());258 } else if (osec == in.stubs) {259 printStubsEntries(os, readerToFileOrdinal, osec, target->stubSize);260 } else if (osec == in.lazyPointers) {261 printStubsEntries(os, readerToFileOrdinal, osec, target->wordSize);262 } else if (osec == in.stubHelper) {263 // yes, ld64 calls it "helper helper"...264 os << format("0x%08llX\t0x%08llX\t[ 0] helper helper\n", osec->addr,265 osec->getSize());266 } else if (osec == in.got) {267 printNonLazyPointerSection(os, in.got);268 } else if (osec == in.tlvPointers) {269 printNonLazyPointerSection(os, in.tlvPointers);270 } else if (osec == in.objcMethList) {271 printIsecArrSyms(in.objcMethList->getInputs());272 }273 // TODO print other synthetic sections274 }275 }276 277 if (config->deadStrip) {278 os << "# Dead Stripped Symbols:\n";279 os << "# \tSize \tFile Name\n";280 for (Defined *sym : info.deadSymbols) {281 assert(!sym->isLive());282 os << format("<<dead>>\t0x%08llX\t[%3u] %s\n", getSymSizeForMap(sym),283 readerToFileOrdinal[sym->getFile()],284 sym->getName().str().data());285 }286 for (CStringInfo &cstrInfo : info.deadCStrings) {287 os << format("<<dead>>\t0x%08zX\t[%3u] literal string: ",288 cstrInfo.str.size() + 1, cstrInfo.fileIndex);289 os.write_escaped(cstrInfo.str) << "\n";290 }291 }292}293