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