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