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