281 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. It shows lists in order and10// hierarchically the output sections, input sections, input files and11// symbol:12//13// Address Size Align Out In Symbol14// 00201000 00000015 4 .text15// 00201000 0000000e 4 test.o:(.text)16// 0020100e 00000000 0 local17// 00201005 00000000 0 f(int)18//19//===----------------------------------------------------------------------===//20 21#include "MapFile.h"22#include "InputFiles.h"23#include "LinkerScript.h"24#include "OutputSections.h"25#include "Symbols.h"26#include "SyntheticSections.h"27#include "llvm/ADT/MapVector.h"28#include "llvm/ADT/SetVector.h"29#include "llvm/ADT/SmallPtrSet.h"30#include "llvm/Support/Parallel.h"31#include "llvm/Support/TimeProfiler.h"32#include "llvm/Support/raw_ostream.h"33 34using namespace llvm;35using namespace llvm::object;36using namespace lld;37using namespace lld::elf;38 39using SymbolMapTy = DenseMap<const SectionBase *,40 SmallVector<std::pair<Defined *, uint64_t>, 0>>;41 42static constexpr char indent8[] = " "; // 8 spaces43static constexpr char indent16[] = " "; // 16 spaces44 45// Print out the first three columns of a line.46static void writeHeader(Ctx &ctx, raw_ostream &os, uint64_t vma, uint64_t lma,47 uint64_t size, uint64_t align) {48 if (ctx.arg.is64)49 os << format("%16llx %16llx %8llx %5lld ", vma, lma, size, align);50 else51 os << format("%8llx %8llx %8llx %5lld ", vma, lma, size, align);52}53 54// Returns a list of all symbols that we want to print out.55static std::vector<Defined *> getSymbols(Ctx &ctx) {56 std::vector<Defined *> v;57 for (ELFFileBase *file : ctx.objectFiles)58 for (Symbol *b : file->getSymbols())59 if (auto *dr = dyn_cast<Defined>(b))60 if (!dr->isSection() && dr->section && dr->section->isLive() &&61 (dr->file == file || dr->hasFlag(NEEDS_COPY) ||62 (isa<SyntheticSection>(dr->section) &&63 cast<SyntheticSection>(dr->section)->bss)))64 v.push_back(dr);65 return v;66}67 68// Returns a map from sections to their symbols.69static SymbolMapTy getSectionSyms(Ctx &ctx, ArrayRef<Defined *> syms) {70 SymbolMapTy ret;71 for (Defined *dr : syms)72 ret[dr->section].emplace_back(dr, dr->getVA(ctx));73 74 // Sort symbols by address. We want to print out symbols in the75 // order in the output file rather than the order they appeared76 // in the input files.77 SmallPtrSet<Defined *, 4> set;78 for (auto &it : ret) {79 // Deduplicate symbols which need a canonical PLT entry/copy relocation.80 set.clear();81 llvm::erase_if(it.second, [&](std::pair<Defined *, uint64_t> a) {82 return !set.insert(a.first).second;83 });84 85 llvm::stable_sort(it.second, llvm::less_second());86 }87 return ret;88}89 90// Construct a map from symbols to their stringified representations.91// Demangling symbols (which is what toStr(ctx, ) does) is slow, so92// we do that in batch using parallel-for.93static DenseMap<Symbol *, std::string>94getSymbolStrings(Ctx &ctx, ArrayRef<Defined *> syms) {95 auto strs = std::make_unique<std::string[]>(syms.size());96 parallelFor(0, syms.size(), [&](size_t i) {97 raw_string_ostream os(strs[i]);98 OutputSection *osec = syms[i]->getOutputSection();99 uint64_t vma = syms[i]->getVA(ctx);100 uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0;101 writeHeader(ctx, os, vma, lma, syms[i]->getSize(), 1);102 os << indent16 << toStr(ctx, *syms[i]);103 });104 105 DenseMap<Symbol *, std::string> ret;106 for (size_t i = 0, e = syms.size(); i < e; ++i)107 ret[syms[i]] = std::move(strs[i]);108 return ret;109}110 111// Print .eh_frame contents. Since the section consists of EhSectionPieces,112// we need a specialized printer for that section.113//114// .eh_frame tend to contain a lot of section pieces that are contiguous115// both in input file and output file. Such pieces are squashed before116// being displayed to make output compact.117static void printEhFrame(Ctx &ctx, raw_ostream &os, const EhFrameSection *sec) {118 std::vector<EhSectionPiece> pieces;119 120 auto add = [&](const EhSectionPiece &p) {121 // If P is adjacent to Last, squash the two.122 if (!pieces.empty()) {123 EhSectionPiece &last = pieces.back();124 if (last.sec == p.sec && last.inputOff + last.size == p.inputOff &&125 last.outputOff + last.size == (unsigned)p.outputOff) {126 last.size += p.size;127 return;128 }129 }130 pieces.push_back(p);131 };132 133 // Gather section pieces.134 for (const CieRecord *rec : sec->getCieRecords()) {135 add(*rec->cie);136 for (const EhSectionPiece *fde : rec->fdes)137 add(*fde);138 }139 140 // Print out section pieces.141 const OutputSection *osec = sec->getOutputSection();142 for (EhSectionPiece &p : pieces) {143 writeHeader(ctx, os, osec->addr + p.outputOff, osec->getLMA() + p.outputOff,144 p.size, 1);145 os << indent8 << toStr(ctx, p.sec->file) << ":(" << p.sec->name << "+0x"146 << Twine::utohexstr(p.inputOff) + ")\n";147 }148}149 150static void writeMapFile(Ctx &ctx, raw_fd_ostream &os) {151 // Collect symbol info that we want to print out.152 std::vector<Defined *> syms = getSymbols(ctx);153 SymbolMapTy sectionSyms = getSectionSyms(ctx, syms);154 DenseMap<Symbol *, std::string> symStr = getSymbolStrings(ctx, syms);155 156 // Print out the header line.157 int w = ctx.arg.is64 ? 16 : 8;158 os << right_justify("VMA", w) << ' ' << right_justify("LMA", w)159 << " Size Align Out In Symbol\n";160 161 OutputSection *osec = nullptr;162 for (SectionCommand *cmd : ctx.script->sectionCommands) {163 if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {164 if (assign->provide && !assign->sym)165 continue;166 uint64_t lma = osec ? osec->getLMA() + assign->addr - osec->getVA(0) : 0;167 writeHeader(ctx, os, assign->addr, lma, assign->size, 1);168 os << assign->commandString << '\n';169 continue;170 }171 if (isa<SectionClassDesc>(cmd))172 continue;173 174 osec = &cast<OutputDesc>(cmd)->osec;175 writeHeader(ctx, os, osec->addr, osec->getLMA(), osec->size,176 osec->addralign);177 os << osec->name << '\n';178 179 // Dump symbols for each input section.180 for (SectionCommand *subCmd : osec->commands) {181 if (auto *isd = dyn_cast<InputSectionDescription>(subCmd)) {182 for (InputSection *isec : isd->sections) {183 if (auto *ehSec = dyn_cast<EhFrameSection>(isec)) {184 printEhFrame(ctx, os, ehSec);185 continue;186 }187 188 writeHeader(ctx, os, isec->getVA(), osec->getLMA() + isec->outSecOff,189 isec->getSize(), isec->addralign);190 os << indent8 << toStr(ctx, isec) << '\n';191 for (Symbol *sym : llvm::make_first_range(sectionSyms[isec]))192 os << symStr[sym] << '\n';193 }194 continue;195 }196 197 if (auto *data = dyn_cast<ByteCommand>(subCmd)) {198 writeHeader(ctx, os, osec->addr + data->offset,199 osec->getLMA() + data->offset, data->size, 1);200 os << indent8 << data->commandString << '\n';201 continue;202 }203 204 if (auto *assign = dyn_cast<SymbolAssignment>(subCmd)) {205 if (assign->provide && !assign->sym)206 continue;207 writeHeader(ctx, os, assign->addr,208 osec->getLMA() + assign->addr - osec->getVA(0),209 assign->size, 1);210 os << indent8 << assign->commandString << '\n';211 continue;212 }213 }214 }215}216 217// Output a cross reference table to stdout. This is for --cref.218//219// For each global symbol, we print out a file that defines the symbol220// followed by files that uses that symbol. Here is an example.221//222// strlen /lib/x86_64-linux-gnu/libc.so.6223// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o224// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)225//226// In this case, strlen is defined by libc.so.6 and used by other two227// files.228static void writeCref(Ctx &ctx, raw_fd_ostream &os) {229 // Collect symbols and files.230 MapVector<Symbol *, SetVector<InputFile *>> map;231 for (ELFFileBase *file : ctx.objectFiles) {232 for (Symbol *sym : file->getSymbols()) {233 if (isa<SharedSymbol>(sym))234 map[sym].insert(file);235 if (auto *d = dyn_cast<Defined>(sym))236 if (!d->isLocal())237 map[d].insert(file);238 }239 }240 241 auto print = [&](StringRef a, StringRef b) {242 os << left_justify(a, 49) << ' ' << b << '\n';243 };244 245 // Print a blank line and a header. The format matches GNU ld.246 os << "\nCross Reference Table\n\n";247 print("Symbol", "File");248 249 // Print out a table.250 for (auto kv : map) {251 Symbol *sym = kv.first;252 SetVector<InputFile *> &files = kv.second;253 254 print(toStr(ctx, *sym), toStr(ctx, sym->file));255 for (InputFile *file : files)256 if (file != sym->file)257 print("", toStr(ctx, file));258 }259}260 261void elf::writeMapAndCref(Ctx &ctx) {262 if (ctx.arg.mapFile.empty() && !ctx.arg.cref)263 return;264 265 llvm::TimeTraceScope timeScope("Write map file");266 267 // Open a map file for writing.268 std::error_code ec;269 StringRef mapFile = ctx.arg.mapFile.empty() ? "-" : ctx.arg.mapFile;270 raw_fd_ostream os = ctx.openAuxiliaryFile(mapFile, ec);271 if (ec) {272 ErrAlways(ctx) << "cannot open " << mapFile << ": " << ec.message();273 return;274 }275 276 if (!ctx.arg.mapFile.empty())277 writeMapFile(ctx, os);278 if (ctx.arg.cref)279 writeCref(ctx, os);280}281