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/raw_ostream.h"
40
41 using namespace llvm;
42 using namespace llvm::object;
43 using namespace lld;
44 using namespace lld::coff;
45
46 // Print out the first two columns of a line.
writeHeader(raw_ostream & os,uint32_t sec,uint64_t addr)47 static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
48 os << format(" %04x:%08llx", sec, 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.
writeFormattedTimestamp(raw_ostream & os,time_t tds)54 static 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(&tds);
61 os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
62 months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
63 time->tm_sec, time->tm_year + 1900);
64 }
65
sortUniqueSymbols(std::vector<Defined * > & syms)66 static void sortUniqueSymbols(std::vector<Defined *> &syms) {
67 // Build helper vector
68 using SortEntry = std::pair<Defined *, size_t>;
69 std::vector<SortEntry> v;
70 v.resize(syms.size());
71 for (size_t i = 0, e = syms.size(); i < e; ++i)
72 v[i] = SortEntry(syms[i], i);
73
74 // Remove duplicate symbol pointers
75 parallelSort(v, std::less<SortEntry>());
76 auto end = std::unique(v.begin(), v.end(),
77 [](const SortEntry &a, const SortEntry &b) {
78 return a.first == b.first;
79 });
80 v.erase(end, v.end());
81
82 // Sort by RVA then original order
83 parallelSort(v, [](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 = config->imageBase + a.first->getRVA();
87 uint64_t rvab = config->imageBase + b.first->getRVA();
88 return rvaa < rvab || (rvaa == rvab && a.second < b.second);
89 });
90
91 syms.resize(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.
getSymbols(const COFFLinkerContext & ctx,std::vector<Defined * > & syms,std::vector<Defined * > & staticSyms)97 static 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>(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(sym);
111 else
112 syms.push_back(sym);
113 }
114 } else if (auto *sym = dyn_cast<Defined>(b)) {
115 syms.push_back(sym);
116 }
117 }
118
119 for (ImportFile *file : ctx.importFileInstances) {
120 if (!file->live)
121 continue;
122
123 if (!file->thunkSym)
124 continue;
125
126 if (!file->thunkLive)
127 continue;
128
129 if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
130 syms.push_back(thunkSym);
131
132 if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
133 syms.push_back(impSym);
134 }
135
136 sortUniqueSymbols(syms);
137 sortUniqueSymbols(staticSyms);
138 }
139
140 // Construct a map from symbols to their stringified representations.
141 static DenseMap<Defined *, std::string>
getSymbolStrings(const COFFLinkerContext & ctx,ArrayRef<Defined * > syms)142 getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
143 std::vector<std::string> str(syms.size());
144 parallelFor((size_t)0, syms.size(), [&](size_t i) {
145 raw_string_ostream os(str[i]);
146 Defined *sym = syms[i];
147
148 uint16_t sectionIdx = 0;
149 uint64_t address = 0;
150 SmallString<128> fileDescr;
151
152 if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
153 address = absSym->getVA();
154 fileDescr = "<absolute>";
155 } else if (isa<DefinedSynthetic>(sym)) {
156 fileDescr = "<linker-defined>";
157 } else if (isa<DefinedCommon>(sym)) {
158 fileDescr = "<common>";
159 } else if (Chunk *chunk = sym->getChunk()) {
160 address = sym->getRVA();
161 if (OutputSection *sec = ctx.getOutputSection(chunk))
162 address -= sec->header.VirtualAddress;
163
164 sectionIdx = chunk->getOutputSectionIdx();
165
166 InputFile *file;
167 if (auto *impSym = dyn_cast<DefinedImportData>(sym))
168 file = impSym->file;
169 else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
170 file = thunkSym->wrappedSym->file;
171 else
172 file = sym->getFile();
173
174 if (file) {
175 if (!file->parentName.empty()) {
176 fileDescr = sys::path::filename(file->parentName);
177 sys::path::replace_extension(fileDescr, "");
178 fileDescr += ":";
179 }
180 fileDescr += sys::path::filename(file->getName());
181 }
182 }
183 writeHeader(os, sectionIdx, address);
184 os << " ";
185 os << left_justify(sym->getName(), 26);
186 os << " ";
187 os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
188 if (!fileDescr.empty()) {
189 os << " "; // FIXME : Handle "f" and "i" flags sometimes generated
190 // by link.exe in those spaces
191 os << fileDescr;
192 }
193 });
194
195 DenseMap<Defined *, std::string> ret;
196 for (size_t i = 0, e = syms.size(); i < e; ++i)
197 ret[syms[i]] = std::move(str[i]);
198 return ret;
199 }
200
writeMapFile(COFFLinkerContext & ctx)201 void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
202 if (config->mapFile.empty())
203 return;
204
205 std::error_code ec;
206 raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
207 if (ec)
208 fatal("cannot open " + config->mapFile + ": " + ec.message());
209
210 ScopedTimer t1(ctx.totalMapTimer);
211
212 // Collect symbol info that we want to print out.
213 ScopedTimer t2(ctx.symbolGatherTimer);
214 std::vector<Defined *> syms;
215 std::vector<Defined *> staticSyms;
216 getSymbols(ctx, syms, staticSyms);
217 t2.stop();
218
219 ScopedTimer t3(ctx.symbolStringsTimer);
220 DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
221 DenseMap<Defined *, std::string> staticSymStr =
222 getSymbolStrings(ctx, staticSyms);
223 t3.stop();
224
225 ScopedTimer t4(ctx.writeTimer);
226 SmallString<128> AppName = sys::path::filename(config->outputFile);
227 sys::path::replace_extension(AppName, "");
228
229 // Print out the file header
230 os << " " << AppName << "\n";
231 os << "\n";
232
233 os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
234 if (config->repro) {
235 os << "Repro mode";
236 } else {
237 writeFormattedTimestamp(os, config->timestamp);
238 }
239 os << ")\n";
240
241 os << "\n";
242 os << " Preferred load address is "
243 << format_hex_no_prefix(config->imageBase, 16) << "\n";
244 os << "\n";
245
246 // Print out section table.
247 os << " Start Length Name Class\n";
248
249 for (OutputSection *sec : ctx.outputSections) {
250 // Merge display of chunks with same sectionName
251 std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
252 for (Chunk *c : sec->chunks) {
253 auto *sc = dyn_cast<SectionChunk>(c);
254 if (!sc)
255 continue;
256
257 if (ChunkRanges.empty() ||
258 c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
259 ChunkRanges.emplace_back(sc, sc);
260 } else {
261 ChunkRanges.back().second = sc;
262 }
263 }
264
265 const bool isCodeSection =
266 (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
267 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
268 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
269 StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
270
271 for (auto &cr : ChunkRanges) {
272 size_t size =
273 cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
274
275 auto address = cr.first->getRVA() - sec->header.VirtualAddress;
276 writeHeader(os, sec->sectionIndex, address);
277 os << " " << format_hex_no_prefix(size, 8) << "H";
278 os << " " << left_justify(cr.first->getSectionName(), 23);
279 os << " " << SectionClass;
280 os << '\n';
281 }
282 }
283
284 // Print out the symbols table (without static symbols)
285 os << "\n";
286 os << " Address Publics by Value Rva+Base"
287 " Lib:Object\n";
288 os << "\n";
289 for (Defined *sym : syms)
290 os << symStr[sym] << '\n';
291
292 // Print out the entry point.
293 os << "\n";
294
295 uint16_t entrySecIndex = 0;
296 uint64_t entryAddress = 0;
297
298 if (!config->noEntry) {
299 Defined *entry = dyn_cast_or_null<Defined>(config->entry);
300 if (entry) {
301 Chunk *chunk = entry->getChunk();
302 entrySecIndex = chunk->getOutputSectionIdx();
303 entryAddress =
304 entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
305 }
306 }
307 os << " entry point at ";
308 os << format("%04x:%08llx", entrySecIndex, entryAddress);
309 os << "\n";
310
311 // Print out the static symbols
312 os << "\n";
313 os << " Static symbols\n";
314 os << "\n";
315 for (Defined *sym : staticSyms)
316 os << staticSymStr[sym] << '\n';
317
318 t4.stop();
319 t1.stop();
320 }
321