| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file implements the -Map option. It shows lists in order and |
| 10 | // hierarchically the output sections, input sections, input files and |
| 11 | // symbol: |
| 12 | // |
| 13 | // Address Size Align Out In Symbol |
| 14 | // 00201000 00000015 4 .text |
| 15 | // 00201000 0000000e 4 test.o:(.text) |
| 16 | // 0020100e 00000000 0 local |
| 17 | // 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 | |
| 34 | using namespace llvm; |
| 35 | using namespace llvm::object; |
| 36 | using namespace lld; |
| 37 | using namespace lld::elf; |
| 38 | |
| 39 | using SymbolMapTy = DenseMap<const SectionBase *, |
| 40 | SmallVector<std::pair<Defined *, uint64_t>, 0>>; |
| 41 | |
| 42 | static constexpr char indent8[] = " " ; // 8 spaces |
| 43 | static constexpr char indent16[] = " " ; // 16 spaces |
| 44 | |
| 45 | // Print out the first three columns of a line. |
| 46 | static void (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(Fmt: "%16llx %16llx %8llx %5lld " , Vals: vma, Vals: lma, Vals: size, Vals: align); |
| 50 | else |
| 51 | os << format(Fmt: "%8llx %8llx %8llx %5lld " , Vals: vma, Vals: lma, Vals: size, Vals: align); |
| 52 | } |
| 53 | |
| 54 | // Returns a list of all symbols that we want to print out. |
| 55 | static 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>(Val: b)) |
| 60 | if (!dr->isSection() && dr->section && dr->section->isLive() && |
| 61 | (dr->file == file || dr->hasFlag(bit: NEEDS_COPY) || |
| 62 | (isa<SyntheticSection>(Val: dr->section) && |
| 63 | cast<SyntheticSection>(Val: dr->section)->bss))) |
| 64 | v.push_back(x: dr); |
| 65 | return v; |
| 66 | } |
| 67 | |
| 68 | // Returns a map from sections to their symbols. |
| 69 | static SymbolMapTy getSectionSyms(Ctx &ctx, ArrayRef<Defined *> syms) { |
| 70 | SymbolMapTy ret; |
| 71 | for (Defined *dr : syms) |
| 72 | ret[dr->section].emplace_back(Args&: dr, Args: dr->getVA(ctx)); |
| 73 | |
| 74 | // Sort symbols by address. We want to print out symbols in the |
| 75 | // order in the output file rather than the order they appeared |
| 76 | // 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(C&: it.second, P: [&](std::pair<Defined *, uint64_t> a) { |
| 82 | return !set.insert(Ptr: a.first).second; |
| 83 | }); |
| 84 | |
| 85 | llvm::stable_sort(Range&: it.second, C: 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, so |
| 92 | // we do that in batch using parallel-for. |
| 93 | static DenseMap<Symbol *, std::string> |
| 94 | getSymbolStrings(Ctx &ctx, ArrayRef<Defined *> syms) { |
| 95 | auto strs = std::make_unique<std::string[]>(num: syms.size()); |
| 96 | parallelFor(Begin: 0, End: syms.size(), Fn: [&](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(offset: 0) : 0; |
| 101 | writeHeader(ctx, os, vma, lma, size: syms[i]->getSize(), align: 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 contiguous |
| 115 | // both in input file and output file. Such pieces are squashed before |
| 116 | // being displayed to make output compact. |
| 117 | static 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(x: 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, vma: osec->addr + p.outputOff, lma: osec->getLMA() + p.outputOff, |
| 144 | size: p.size, align: 1); |
| 145 | os << indent8 << toStr(ctx, f: p.sec->file) << ":(" << p.sec->name << "+0x" |
| 146 | << Twine::utohexstr(Val: p.inputOff) + ")\n" ; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | static 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(Str: "VMA" , Width: w) << ' ' << right_justify(Str: "LMA" , Width: 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>(Val: cmd)) { |
| 164 | if (assign->provide && !assign->sym) |
| 165 | continue; |
| 166 | uint64_t lma = osec ? osec->getLMA() + assign->addr - osec->getVA(offset: 0) : 0; |
| 167 | writeHeader(ctx, os, vma: assign->addr, lma, size: assign->size, align: 1); |
| 168 | os << assign->commandString << '\n'; |
| 169 | continue; |
| 170 | } |
| 171 | if (isa<SectionClassDesc>(Val: cmd)) |
| 172 | continue; |
| 173 | |
| 174 | osec = &cast<OutputDesc>(Val: cmd)->osec; |
| 175 | writeHeader(ctx, os, vma: osec->addr, lma: osec->getLMA(), size: osec->size, |
| 176 | align: 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>(Val: subCmd)) { |
| 182 | for (InputSection *isec : isd->sections) { |
| 183 | if (auto *ehSec = dyn_cast<EhFrameSection>(Val: isec)) { |
| 184 | printEhFrame(ctx, os, sec: ehSec); |
| 185 | continue; |
| 186 | } |
| 187 | |
| 188 | writeHeader(ctx, os, vma: isec->getVA(), lma: osec->getLMA() + isec->outSecOff, |
| 189 | size: isec->getSize(), align: isec->addralign); |
| 190 | os << indent8 << toStr(ctx, isec) << '\n'; |
| 191 | for (Symbol *sym : llvm::make_first_range(c&: sectionSyms[isec])) |
| 192 | os << symStr[sym] << '\n'; |
| 193 | } |
| 194 | continue; |
| 195 | } |
| 196 | |
| 197 | if (auto *data = dyn_cast<ByteCommand>(Val: subCmd)) { |
| 198 | writeHeader(ctx, os, vma: osec->addr + data->offset, |
| 199 | lma: osec->getLMA() + data->offset, size: data->size, align: 1); |
| 200 | os << indent8 << data->commandString << '\n'; |
| 201 | continue; |
| 202 | } |
| 203 | |
| 204 | if (auto *assign = dyn_cast<SymbolAssignment>(Val: subCmd)) { |
| 205 | if (assign->provide && !assign->sym) |
| 206 | continue; |
| 207 | writeHeader(ctx, os, vma: assign->addr, |
| 208 | lma: osec->getLMA() + assign->addr - osec->getVA(offset: 0), |
| 209 | size: assign->size, align: 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 symbol |
| 220 | // followed by files that uses that symbol. Here is an example. |
| 221 | // |
| 222 | // strlen /lib/x86_64-linux-gnu/libc.so.6 |
| 223 | // tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o |
| 224 | // lib/libLLVMSupport.a(PrettyStackTrace.cpp.o) |
| 225 | // |
| 226 | // In this case, strlen is defined by libc.so.6 and used by other two |
| 227 | // files. |
| 228 | static 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>(Val: sym)) |
| 234 | map[sym].insert(X: file); |
| 235 | if (auto *d = dyn_cast<Defined>(Val: sym)) |
| 236 | if (!d->isLocal()) |
| 237 | map[d].insert(X: file); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | auto print = [&](StringRef a, StringRef b) { |
| 242 | os << left_justify(Str: a, Width: 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, f: sym->file)); |
| 255 | for (InputFile *file : files) |
| 256 | if (file != sym->file) |
| 257 | print("" , toStr(ctx, f: file)); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | void 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 | |