Path: blob/main/contrib/llvm-project/lld/ELF/MapFile.cpp
34869 views
//===- MapFile.cpp --------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the -Map option. It shows lists in order and9// hierarchically the output sections, input sections, input files and10// symbol:11//12// Address Size Align Out In Symbol13// 00201000 00000015 4 .text14// 00201000 0000000e 4 test.o:(.text)15// 0020100e 00000000 0 local16// 00201005 00000000 0 f(int)17//18//===----------------------------------------------------------------------===//1920#include "MapFile.h"21#include "InputFiles.h"22#include "LinkerScript.h"23#include "OutputSections.h"24#include "Symbols.h"25#include "SyntheticSections.h"26#include "llvm/ADT/MapVector.h"27#include "llvm/ADT/SetVector.h"28#include "llvm/ADT/SmallPtrSet.h"29#include "llvm/Support/FileSystem.h"30#include "llvm/Support/Parallel.h"31#include "llvm/Support/TimeProfiler.h"32#include "llvm/Support/raw_ostream.h"3334using namespace llvm;35using namespace llvm::object;36using namespace lld;37using namespace lld::elf;3839using SymbolMapTy = DenseMap<const SectionBase *,40SmallVector<std::pair<Defined *, uint64_t>, 0>>;4142static constexpr char indent8[] = " "; // 8 spaces43static constexpr char indent16[] = " "; // 16 spaces4445// Print out the first three columns of a line.46static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,47uint64_t size, uint64_t align) {48if (config->is64)49os << format("%16llx %16llx %8llx %5lld ", vma, lma, size, align);50else51os << format("%8llx %8llx %8llx %5lld ", vma, lma, size, align);52}5354// Returns a list of all symbols that we want to print out.55static std::vector<Defined *> getSymbols() {56std::vector<Defined *> v;57for (ELFFileBase *file : ctx.objectFiles)58for (Symbol *b : file->getSymbols())59if (auto *dr = dyn_cast<Defined>(b))60if (!dr->isSection() && dr->section && dr->section->isLive() &&61(dr->file == file || dr->hasFlag(NEEDS_COPY) || dr->section->bss))62v.push_back(dr);63return v;64}6566// Returns a map from sections to their symbols.67static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {68SymbolMapTy ret;69for (Defined *dr : syms)70ret[dr->section].emplace_back(dr, dr->getVA());7172// Sort symbols by address. We want to print out symbols in the73// order in the output file rather than the order they appeared74// in the input files.75SmallPtrSet<Defined *, 4> set;76for (auto &it : ret) {77// Deduplicate symbols which need a canonical PLT entry/copy relocation.78set.clear();79llvm::erase_if(it.second, [&](std::pair<Defined *, uint64_t> a) {80return !set.insert(a.first).second;81});8283llvm::stable_sort(it.second, llvm::less_second());84}85return ret;86}8788// Construct a map from symbols to their stringified representations.89// Demangling symbols (which is what toString() does) is slow, so90// we do that in batch using parallel-for.91static DenseMap<Symbol *, std::string>92getSymbolStrings(ArrayRef<Defined *> syms) {93auto strs = std::make_unique<std::string[]>(syms.size());94parallelFor(0, syms.size(), [&](size_t i) {95raw_string_ostream os(strs[i]);96OutputSection *osec = syms[i]->getOutputSection();97uint64_t vma = syms[i]->getVA();98uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0;99writeHeader(os, vma, lma, syms[i]->getSize(), 1);100os << indent16 << toString(*syms[i]);101});102103DenseMap<Symbol *, std::string> ret;104for (size_t i = 0, e = syms.size(); i < e; ++i)105ret[syms[i]] = std::move(strs[i]);106return ret;107}108109// Print .eh_frame contents. Since the section consists of EhSectionPieces,110// we need a specialized printer for that section.111//112// .eh_frame tend to contain a lot of section pieces that are contiguous113// both in input file and output file. Such pieces are squashed before114// being displayed to make output compact.115static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {116std::vector<EhSectionPiece> pieces;117118auto add = [&](const EhSectionPiece &p) {119// If P is adjacent to Last, squash the two.120if (!pieces.empty()) {121EhSectionPiece &last = pieces.back();122if (last.sec == p.sec && last.inputOff + last.size == p.inputOff &&123last.outputOff + last.size == (unsigned)p.outputOff) {124last.size += p.size;125return;126}127}128pieces.push_back(p);129};130131// Gather section pieces.132for (const CieRecord *rec : sec->getCieRecords()) {133add(*rec->cie);134for (const EhSectionPiece *fde : rec->fdes)135add(*fde);136}137138// Print out section pieces.139const OutputSection *osec = sec->getOutputSection();140for (EhSectionPiece &p : pieces) {141writeHeader(os, osec->addr + p.outputOff, osec->getLMA() + p.outputOff,142p.size, 1);143os << indent8 << toString(p.sec->file) << ":(" << p.sec->name << "+0x"144<< Twine::utohexstr(p.inputOff) + ")\n";145}146}147148static void writeMapFile(raw_fd_ostream &os) {149// Collect symbol info that we want to print out.150std::vector<Defined *> syms = getSymbols();151SymbolMapTy sectionSyms = getSectionSyms(syms);152DenseMap<Symbol *, std::string> symStr = getSymbolStrings(syms);153154// Print out the header line.155int w = config->is64 ? 16 : 8;156os << right_justify("VMA", w) << ' ' << right_justify("LMA", w)157<< " Size Align Out In Symbol\n";158159OutputSection *osec = nullptr;160for (SectionCommand *cmd : script->sectionCommands) {161if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {162if (assign->provide && !assign->sym)163continue;164uint64_t lma = osec ? osec->getLMA() + assign->addr - osec->getVA(0) : 0;165writeHeader(os, assign->addr, lma, assign->size, 1);166os << assign->commandString << '\n';167continue;168}169170osec = &cast<OutputDesc>(cmd)->osec;171writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->addralign);172os << osec->name << '\n';173174// Dump symbols for each input section.175for (SectionCommand *subCmd : osec->commands) {176if (auto *isd = dyn_cast<InputSectionDescription>(subCmd)) {177for (InputSection *isec : isd->sections) {178if (auto *ehSec = dyn_cast<EhFrameSection>(isec)) {179printEhFrame(os, ehSec);180continue;181}182183writeHeader(os, isec->getVA(), osec->getLMA() + isec->outSecOff,184isec->getSize(), isec->addralign);185os << indent8 << toString(isec) << '\n';186for (Symbol *sym : llvm::make_first_range(sectionSyms[isec]))187os << symStr[sym] << '\n';188}189continue;190}191192if (auto *data = dyn_cast<ByteCommand>(subCmd)) {193writeHeader(os, osec->addr + data->offset,194osec->getLMA() + data->offset, data->size, 1);195os << indent8 << data->commandString << '\n';196continue;197}198199if (auto *assign = dyn_cast<SymbolAssignment>(subCmd)) {200if (assign->provide && !assign->sym)201continue;202writeHeader(os, assign->addr,203osec->getLMA() + assign->addr - osec->getVA(0),204assign->size, 1);205os << indent8 << assign->commandString << '\n';206continue;207}208}209}210}211212// Output a cross reference table to stdout. This is for --cref.213//214// For each global symbol, we print out a file that defines the symbol215// followed by files that uses that symbol. Here is an example.216//217// strlen /lib/x86_64-linux-gnu/libc.so.6218// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o219// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)220//221// In this case, strlen is defined by libc.so.6 and used by other two222// files.223static void writeCref(raw_fd_ostream &os) {224// Collect symbols and files.225MapVector<Symbol *, SetVector<InputFile *>> map;226for (ELFFileBase *file : ctx.objectFiles) {227for (Symbol *sym : file->getSymbols()) {228if (isa<SharedSymbol>(sym))229map[sym].insert(file);230if (auto *d = dyn_cast<Defined>(sym))231if (!d->isLocal())232map[d].insert(file);233}234}235236auto print = [&](StringRef a, StringRef b) {237os << left_justify(a, 49) << ' ' << b << '\n';238};239240// Print a blank line and a header. The format matches GNU ld.241os << "\nCross Reference Table\n\n";242print("Symbol", "File");243244// Print out a table.245for (auto kv : map) {246Symbol *sym = kv.first;247SetVector<InputFile *> &files = kv.second;248249print(toString(*sym), toString(sym->file));250for (InputFile *file : files)251if (file != sym->file)252print("", toString(file));253}254}255256void elf::writeMapAndCref() {257if (config->mapFile.empty() && !config->cref)258return;259260llvm::TimeTraceScope timeScope("Write map file");261262// Open a map file for writing.263std::error_code ec;264StringRef mapFile = config->mapFile.empty() ? "-" : config->mapFile;265raw_fd_ostream os = ctx.openAuxiliaryFile(mapFile, ec);266if (ec) {267error("cannot open " + mapFile + ": " + ec.message());268return;269}270271if (!config->mapFile.empty())272writeMapFile(os);273if (config->cref)274writeCref(os);275}276277278