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 in the same format as link.exe
10// (based on observations)
11//
12// Header (program name, timestamp info, preferred load address)
13//
14// Section list (Start = Section index:Base address):
15// Start Length Name Class
16// 0001:00001000 00000015H .text CODE
17//
18// Symbols list:
19// Address Publics by Value Rva + Base Lib:Object
20// 0001:00001000 main 0000000140001000 main.obj
21// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj
22//
23// entry point at 0001:00000360
24//
25// Static symbols
26//
27// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
28//===----------------------------------------------------------------------===//
29
30#include "MapFile.h"
31#include "COFFLinkerContext.h"
32#include "SymbolTable.h"
33#include "Symbols.h"
34#include "Writer.h"
35#include "lld/Common/Timer.h"
36#include "llvm/Support/Parallel.h"
37#include "llvm/Support/Path.h"
38#include "llvm/Support/TimeProfiler.h"
39#include "llvm/Support/raw_ostream.h"
40
41using namespace llvm;
42using namespace llvm::object;
43using namespace lld;
44using namespace lld::coff;
45
46// Print out the first two columns of a line.
47static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
48 os << format(Fmt: " %04x:%08llx", Vals: sec, Vals: addr);
49}
50
51// Write the time stamp with the format used by link.exe
52// It seems identical to strftime with "%c" on msvc build, but we need a
53// locale-agnostic version.
54static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
55 constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
56 "Thu", "Fri", "Sat"};
57 constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
58 "May", "Jun", "Jul", "Aug",
59 "Sep", "Oct", "Nov", "Dec"};
60 tm *time = localtime(timer: &tds);
61 os << format(Fmt: "%s %s %2d %02d:%02d:%02d %d", Vals: days[time->tm_wday],
62 Vals: months[time->tm_mon], Vals: time->tm_mday, Vals: time->tm_hour, Vals: time->tm_min,
63 Vals: time->tm_sec, Vals: time->tm_year + 1900);
64}
65
66static void sortUniqueSymbols(std::vector<Defined *> &syms,
67 uint64_t imageBase) {
68 // Build helper vector
69 using SortEntry = std::pair<Defined *, size_t>;
70 std::vector<SortEntry> v;
71 v.resize(new_size: syms.size());
72 for (size_t i = 0, e = syms.size(); i < e; ++i)
73 v[i] = SortEntry(syms[i], i);
74
75 // Remove duplicate symbol pointers
76 parallelSort(R&: v, Comp: std::less<SortEntry>());
77 auto end = llvm::unique(R&: v, P: [](const SortEntry &a, const SortEntry &b) {
78 return a.first == b.first;
79 });
80 v.erase(first: end, last: v.end());
81
82 // Sort by RVA then original order
83 parallelSort(R&: v, Comp: [imageBase](const SortEntry &a, const SortEntry &b) {
84 // Add config.imageBase to avoid comparing "negative" RVAs.
85 // This can happen with symbols of Absolute kind
86 uint64_t rvaa = imageBase + a.first->getRVA();
87 uint64_t rvab = imageBase + b.first->getRVA();
88 return rvaa < rvab || (rvaa == rvab && a.second < b.second);
89 });
90
91 syms.resize(new_size: v.size());
92 for (size_t i = 0, e = v.size(); i < e; ++i)
93 syms[i] = v[i].first;
94}
95
96// Returns the lists of all symbols that we want to print out.
97static void getSymbols(const COFFLinkerContext &ctx,
98 std::vector<Defined *> &syms,
99 std::vector<Defined *> &staticSyms) {
100
101 for (ObjFile *file : ctx.objFileInstances)
102 for (Symbol *b : file->getSymbols()) {
103 if (!b || !b->isLive())
104 continue;
105 if (auto *sym = dyn_cast<DefinedCOFF>(Val: b)) {
106 COFFSymbolRef symRef = sym->getCOFFSymbol();
107 if (!symRef.isSectionDefinition() &&
108 symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
109 if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
110 staticSyms.push_back(x: sym);
111 else
112 syms.push_back(x: sym);
113 }
114 } else if (auto *sym = dyn_cast<Defined>(Val: b)) {
115 syms.push_back(x: sym);
116 }
117 }
118
119 for (ImportFile *file : ctx.importFileInstances) {
120 if (!file->live)
121 continue;
122
123 if (file->impSym)
124 syms.push_back(x: file->impSym);
125 if (file->thunkSym && file->thunkSym->isLive())
126 syms.push_back(x: file->thunkSym);
127 if (file->auxThunkSym && file->auxThunkSym->isLive())
128 syms.push_back(x: file->auxThunkSym);
129 if (file->impchkThunk)
130 syms.push_back(x: file->impchkThunk->sym);
131 if (file->impECSym)
132 syms.push_back(x: file->impECSym);
133 if (file->auxImpCopySym)
134 syms.push_back(x: file->auxImpCopySym);
135 }
136
137 sortUniqueSymbols(syms, imageBase: ctx.config.imageBase);
138 sortUniqueSymbols(syms&: staticSyms, imageBase: ctx.config.imageBase);
139}
140
141// Construct a map from symbols to their stringified representations.
142static DenseMap<Defined *, std::string>
143getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
144 std::vector<std::string> str(syms.size());
145 parallelFor(Begin: (size_t)0, End: syms.size(), Fn: [&](size_t i) {
146 raw_string_ostream os(str[i]);
147 Defined *sym = syms[i];
148
149 uint16_t sectionIdx = 0;
150 uint64_t address = 0;
151 SmallString<128> fileDescr;
152
153 if (auto *absSym = dyn_cast<DefinedAbsolute>(Val: sym)) {
154 address = absSym->getVA();
155 fileDescr = "<absolute>";
156 } else if (isa<DefinedSynthetic>(Val: sym)) {
157 fileDescr = "<linker-defined>";
158 } else if (isa<DefinedCommon>(Val: sym)) {
159 fileDescr = "<common>";
160 } else if (Chunk *chunk = sym->getChunk()) {
161 address = sym->getRVA();
162 if (OutputSection *sec = ctx.getOutputSection(c: chunk))
163 address -= sec->header.VirtualAddress;
164
165 sectionIdx = chunk->getOutputSectionIdx();
166
167 InputFile *file;
168 if (auto *impSym = dyn_cast<DefinedImportData>(Val: sym))
169 file = impSym->file;
170 else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(Val: sym))
171 file = thunkSym->wrappedSym->file;
172 else
173 file = sym->getFile();
174
175 if (file) {
176 if (!file->parentName.empty()) {
177 fileDescr = sys::path::filename(path: file->parentName);
178 sys::path::replace_extension(path&: fileDescr, extension: "");
179 fileDescr += ":";
180 }
181 fileDescr += sys::path::filename(path: file->getName());
182 }
183 }
184 writeHeader(os, sec: sectionIdx, addr: address);
185 os << " ";
186 os << left_justify(Str: sym->getName(), Width: 26);
187 os << " ";
188 os << format_hex_no_prefix(N: (ctx.config.imageBase + sym->getRVA()), Width: 16);
189 if (!fileDescr.empty()) {
190 os << " "; // FIXME : Handle "f" and "i" flags sometimes generated
191 // by link.exe in those spaces
192 os << fileDescr;
193 }
194 });
195
196 DenseMap<Defined *, std::string> ret;
197 for (size_t i = 0, e = syms.size(); i < e; ++i)
198 ret[syms[i]] = std::move(str[i]);
199 return ret;
200}
201
202void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
203 if (ctx.config.mapFile.empty())
204 return;
205
206 llvm::TimeTraceScope timeScope("Map file");
207 std::error_code ec;
208 raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
209 if (ec)
210 Fatal(ctx) << "cannot open " << ctx.config.mapFile << ": " << ec.message();
211
212 ScopedTimer t1(ctx.totalMapTimer);
213
214 // Collect symbol info that we want to print out.
215 ScopedTimer t2(ctx.symbolGatherTimer);
216 std::vector<Defined *> syms;
217 std::vector<Defined *> staticSyms;
218 getSymbols(ctx, syms, staticSyms);
219 t2.stop();
220
221 ScopedTimer t3(ctx.symbolStringsTimer);
222 DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
223 DenseMap<Defined *, std::string> staticSymStr =
224 getSymbolStrings(ctx, syms: staticSyms);
225 t3.stop();
226
227 ScopedTimer t4(ctx.writeTimer);
228 SmallString<128> AppName = sys::path::filename(path: ctx.config.outputFile);
229 sys::path::replace_extension(path&: AppName, extension: "");
230
231 // Print out the file header
232 os << " " << AppName << "\n";
233 os << "\n";
234
235 os << " Timestamp is " << format_hex_no_prefix(N: ctx.config.timestamp, Width: 8)
236 << " (";
237 if (ctx.config.repro) {
238 os << "Repro mode";
239 } else {
240 writeFormattedTimestamp(os, tds: ctx.config.timestamp);
241 }
242 os << ")\n";
243
244 os << "\n";
245 os << " Preferred load address is "
246 << format_hex_no_prefix(N: ctx.config.imageBase, Width: 16) << "\n";
247 os << "\n";
248
249 // Print out section table.
250 os << " Start Length Name Class\n";
251
252 for (OutputSection *sec : ctx.outputSections) {
253 // Merge display of chunks with same sectionName
254 std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
255 for (Chunk *c : sec->chunks) {
256 auto *sc = dyn_cast<SectionChunk>(Val: c);
257 if (!sc)
258 continue;
259
260 if (ChunkRanges.empty() ||
261 c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
262 ChunkRanges.emplace_back(args&: sc, args&: sc);
263 } else {
264 ChunkRanges.back().second = sc;
265 }
266 }
267
268 const bool isCodeSection =
269 (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
270 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
271 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
272 StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
273
274 for (auto &cr : ChunkRanges) {
275 size_t size =
276 cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
277
278 auto address = cr.first->getRVA() - sec->header.VirtualAddress;
279 writeHeader(os, sec: sec->sectionIndex, addr: address);
280 os << " " << format_hex_no_prefix(N: size, Width: 8) << "H";
281 os << " " << left_justify(Str: cr.first->getSectionName(), Width: 23);
282 os << " " << SectionClass;
283 os << '\n';
284 }
285 }
286
287 // Print out the symbols table (without static symbols)
288 os << "\n";
289 os << " Address Publics by Value Rva+Base"
290 " Lib:Object\n";
291 os << "\n";
292 for (Defined *sym : syms)
293 os << symStr[sym] << '\n';
294
295 // Print out the entry point.
296 os << "\n";
297
298 uint16_t entrySecIndex = 0;
299 uint64_t entryAddress = 0;
300
301 if (!ctx.config.noEntry) {
302 Defined *entry = dyn_cast_or_null<Defined>(Val: ctx.symtab.entry);
303 if (entry) {
304 Chunk *chunk = entry->getChunk();
305 entrySecIndex = chunk->getOutputSectionIdx();
306 entryAddress =
307 entry->getRVA() - ctx.getOutputSection(c: chunk)->header.VirtualAddress;
308 }
309 }
310 os << " entry point at ";
311 os << format(Fmt: "%04x:%08llx", Vals: entrySecIndex, Vals: entryAddress);
312 os << "\n";
313
314 // Print out the static symbols
315 os << "\n";
316 os << " Static symbols\n";
317 os << "\n";
318 for (Defined *sym : staticSyms)
319 os << staticSymStr[sym] << '\n';
320
321 // Print out the exported functions
322 if (ctx.config.mapInfo) {
323 os << "\n";
324 os << " Exports\n";
325 os << "\n";
326 os << " ordinal name\n\n";
327 for (Export &e : ctx.symtab.exports) {
328 os << format(Fmt: " %7d", Vals: e.ordinal) << " " << e.name << "\n";
329 if (!e.extName.empty() && e.extName != e.name)
330 os << " exported name: " << e.extName << "\n";
331 }
332 }
333
334 t4.stop();
335 t1.stop();
336}
337

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lld/COFF/MapFile.cpp