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 | |