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 | // Addr Off Size Out In Symbol |
14 | // - 00000015 10 .text |
15 | // - 0000000e 10 test.o:(.text) |
16 | // - 00000000 5 local |
17 | // - 00000000 5 f(int) |
18 | // |
19 | //===----------------------------------------------------------------------===// |
20 | |
21 | #include "MapFile.h" |
22 | #include "InputElement.h" |
23 | #include "InputFiles.h" |
24 | #include "OutputSections.h" |
25 | #include "OutputSegment.h" |
26 | #include "SymbolTable.h" |
27 | #include "Symbols.h" |
28 | #include "SyntheticSections.h" |
29 | #include "lld/Common/Strings.h" |
30 | #include "llvm/ADT/MapVector.h" |
31 | #include "llvm/Support/Parallel.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::wasm; |
38 | |
39 | using SymbolMapTy = DenseMap<const InputChunk *, SmallVector<Symbol *, 4>>; |
40 | |
41 | // Print out the first three columns of a line. |
42 | static void (raw_ostream &os, int64_t vma, uint64_t lma, |
43 | uint64_t size) { |
44 | // Not all entries in the map has a virtual memory address (e.g. functions) |
45 | if (vma == -1) |
46 | os << format(Fmt: " - %8llx %8llx " , Vals: lma, Vals: size); |
47 | else |
48 | os << format(Fmt: "%8llx %8llx %8llx " , Vals: vma, Vals: lma, Vals: size); |
49 | } |
50 | |
51 | // Returns a list of all symbols that we want to print out. |
52 | static std::vector<Symbol *> getSymbols() { |
53 | std::vector<Symbol *> v; |
54 | for (InputFile *file : ctx.objectFiles) |
55 | for (Symbol *b : file->getSymbols()) |
56 | if (auto *dr = dyn_cast<Symbol>(Val: b)) |
57 | if ((!isa<SectionSymbol>(Val: dr)) && dr->isLive() && |
58 | (dr->getFile() == file)) |
59 | v.push_back(x: dr); |
60 | return v; |
61 | } |
62 | |
63 | // Returns a map from sections to their symbols. |
64 | static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> syms) { |
65 | SymbolMapTy ret; |
66 | for (Symbol *dr : syms) |
67 | ret[dr->getChunk()].push_back(Elt: dr); |
68 | return ret; |
69 | } |
70 | |
71 | // Construct a map from symbols to their stringified representations. |
72 | // Demangling symbols (which is what toString() does) is slow, so |
73 | // we do that in batch using parallel-for. |
74 | static DenseMap<Symbol *, std::string> |
75 | getSymbolStrings(ArrayRef<Symbol *> syms) { |
76 | std::vector<std::string> str(syms.size()); |
77 | parallelFor(Begin: 0, End: syms.size(), Fn: [&](size_t i) { |
78 | raw_string_ostream os(str[i]); |
79 | auto *chunk = syms[i]->getChunk(); |
80 | if (chunk == nullptr) |
81 | return; |
82 | uint64_t fileOffset = chunk->outputSec != nullptr |
83 | ? chunk->outputSec->getOffset() + chunk->outSecOff |
84 | : 0; |
85 | uint64_t vma = -1; |
86 | uint64_t size = 0; |
87 | if (auto *DD = dyn_cast<DefinedData>(Val: syms[i])) { |
88 | vma = DD->getVA(); |
89 | size = DD->getSize(); |
90 | fileOffset += DD->value; |
91 | } |
92 | if (auto *DF = dyn_cast<DefinedFunction>(Val: syms[i])) { |
93 | size = DF->function->getSize(); |
94 | } |
95 | writeHeader(os, vma, lma: fileOffset, size); |
96 | os.indent(NumSpaces: 16) << toString(sym: *syms[i]); |
97 | }); |
98 | |
99 | DenseMap<Symbol *, std::string> ret; |
100 | for (size_t i = 0, e = syms.size(); i < e; ++i) |
101 | ret[syms[i]] = std::move(str[i]); |
102 | return ret; |
103 | } |
104 | |
105 | void lld::wasm::writeMapFile(ArrayRef<OutputSection *> outputSections) { |
106 | if (config->mapFile.empty()) |
107 | return; |
108 | |
109 | // Open a map file for writing. |
110 | std::error_code ec; |
111 | raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); |
112 | if (ec) { |
113 | error(msg: "cannot open " + config->mapFile + ": " + ec.message()); |
114 | return; |
115 | } |
116 | |
117 | // Collect symbol info that we want to print out. |
118 | std::vector<Symbol *> syms = getSymbols(); |
119 | SymbolMapTy sectionSyms = getSectionSyms(syms); |
120 | DenseMap<Symbol *, std::string> symStr = getSymbolStrings(syms); |
121 | |
122 | // Print out the header line. |
123 | os << " Addr Off Size Out In Symbol\n" ; |
124 | |
125 | for (OutputSection *osec : outputSections) { |
126 | writeHeader(os, vma: -1, lma: osec->getOffset(), size: osec->getSize()); |
127 | os << toString(section: *osec) << '\n'; |
128 | if (auto *code = dyn_cast<CodeSection>(Val: osec)) { |
129 | for (auto *chunk : code->functions) { |
130 | writeHeader(os, vma: -1, lma: chunk->outputSec->getOffset() + chunk->outSecOff, |
131 | size: chunk->getSize()); |
132 | os.indent(NumSpaces: 8) << toString(chunk) << '\n'; |
133 | for (Symbol *sym : sectionSyms[chunk]) |
134 | os << symStr[sym] << '\n'; |
135 | } |
136 | } else if (auto *data = dyn_cast<DataSection>(Val: osec)) { |
137 | for (auto *oseg : data->segments) { |
138 | writeHeader(os, vma: oseg->startVA, lma: data->getOffset() + oseg->sectionOffset, |
139 | size: oseg->size); |
140 | os << oseg->name << '\n'; |
141 | for (auto *chunk : oseg->inputSegments) { |
142 | uint64_t offset = |
143 | chunk->outputSec != nullptr |
144 | ? chunk->outputSec->getOffset() + chunk->outSecOff |
145 | : 0; |
146 | writeHeader(os, vma: chunk->getVA(), lma: offset, size: chunk->getSize()); |
147 | os.indent(NumSpaces: 8) << toString(chunk) << '\n'; |
148 | for (Symbol *sym : sectionSyms[chunk]) |
149 | os << symStr[sym] << '\n'; |
150 | } |
151 | } |
152 | } else if (auto *globals = dyn_cast<GlobalSection>(Val: osec)) { |
153 | for (auto *global : globals->inputGlobals) { |
154 | writeHeader(os, vma: global->getAssignedIndex(), lma: 0, size: 0); |
155 | os.indent(NumSpaces: 8) << global->getName() << '\n'; |
156 | } |
157 | } |
158 | // TODO: other section/symbol types |
159 | } |
160 | } |
161 | |