Revert "[libc] Use best-fit binary trie to make malloc logarithmic" (#117065)
[llvm-project.git] / lld / COFF / MapFile.cpp
blob55a1cd942ab8587dd859c8d8541aa63f3d144b55
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)
12 // Header (program name, timestamp info, preferred load address)
14 // Section list (Start = Section index:Base address):
15 // Start Length Name Class
16 // 0001:00001000 00000015H .text CODE
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
23 // entry point at 0001:00000360
25 // Static symbols
27 // 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
28 //===----------------------------------------------------------------------===//
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"
42 using namespace llvm;
43 using namespace llvm::object;
44 using namespace lld;
45 using namespace lld::coff;
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);
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);
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);
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());
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 });
93 syms.resize(v.size());
94 for (size_t i = 0, e = v.size(); i < e; ++i)
95 syms[i] = v[i].first;
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) {
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);
116 } else if (auto *sym = dyn_cast<Defined>(b)) {
117 syms.push_back(sym);
121 for (ImportFile *file : ctx.importFileInstances) {
122 if (!file->live)
123 continue;
125 if (file->impSym)
126 syms.push_back(file->impSym);
127 if (file->thunkSym && file->thunkSym->isLive())
128 syms.push_back(file->thunkSym);
129 if (file->auxThunkSym && file->auxThunkSym->isLive())
130 syms.push_back(file->auxThunkSym);
131 if (file->impchkThunk)
132 syms.push_back(file->impchkThunk->sym);
133 if (file->impECSym)
134 syms.push_back(file->impECSym);
135 if (file->auxImpCopySym)
136 syms.push_back(file->auxImpCopySym);
139 sortUniqueSymbols(syms, ctx.config.imageBase);
140 sortUniqueSymbols(staticSyms, ctx.config.imageBase);
143 // Construct a map from symbols to their stringified representations.
144 static DenseMap<Defined *, std::string>
145 getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
146 std::vector<std::string> str(syms.size());
147 parallelFor((size_t)0, syms.size(), [&](size_t i) {
148 raw_string_ostream os(str[i]);
149 Defined *sym = syms[i];
151 uint16_t sectionIdx = 0;
152 uint64_t address = 0;
153 SmallString<128> fileDescr;
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 = ctx.getOutputSection(chunk))
165 address -= sec->header.VirtualAddress;
167 sectionIdx = chunk->getOutputSectionIdx();
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();
177 if (file) {
178 if (!file->parentName.empty()) {
179 fileDescr = sys::path::filename(file->parentName);
180 sys::path::replace_extension(fileDescr, "");
181 fileDescr += ":";
183 fileDescr += sys::path::filename(file->getName());
186 writeHeader(os, sectionIdx, address);
187 os << " ";
188 os << left_justify(sym->getName(), 26);
189 os << " ";
190 os << format_hex_no_prefix((ctx.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;
198 DenseMap<Defined *, std::string> ret;
199 for (size_t i = 0, e = syms.size(); i < e; ++i)
200 ret[syms[i]] = std::move(str[i]);
201 return ret;
204 void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
205 if (ctx.config.mapFile.empty())
206 return;
208 llvm::TimeTraceScope timeScope("Map file");
209 std::error_code ec;
210 raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
211 if (ec)
212 fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
214 ScopedTimer t1(ctx.totalMapTimer);
216 // Collect symbol info that we want to print out.
217 ScopedTimer t2(ctx.symbolGatherTimer);
218 std::vector<Defined *> syms;
219 std::vector<Defined *> staticSyms;
220 getSymbols(ctx, syms, staticSyms);
221 t2.stop();
223 ScopedTimer t3(ctx.symbolStringsTimer);
224 DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
225 DenseMap<Defined *, std::string> staticSymStr =
226 getSymbolStrings(ctx, staticSyms);
227 t3.stop();
229 ScopedTimer t4(ctx.writeTimer);
230 SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
231 sys::path::replace_extension(AppName, "");
233 // Print out the file header
234 os << " " << AppName << "\n";
235 os << "\n";
237 os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
238 << " (";
239 if (ctx.config.repro) {
240 os << "Repro mode";
241 } else {
242 writeFormattedTimestamp(os, ctx.config.timestamp);
244 os << ")\n";
246 os << "\n";
247 os << " Preferred load address is "
248 << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
249 os << "\n";
251 // Print out section table.
252 os << " Start Length Name Class\n";
254 for (OutputSection *sec : ctx.outputSections) {
255 // Merge display of chunks with same sectionName
256 std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
257 for (Chunk *c : sec->chunks) {
258 auto *sc = dyn_cast<SectionChunk>(c);
259 if (!sc)
260 continue;
262 if (ChunkRanges.empty() ||
263 c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
264 ChunkRanges.emplace_back(sc, sc);
265 } else {
266 ChunkRanges.back().second = sc;
270 const bool isCodeSection =
271 (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
272 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
273 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
274 StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
276 for (auto &cr : ChunkRanges) {
277 size_t size =
278 cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
280 auto address = cr.first->getRVA() - sec->header.VirtualAddress;
281 writeHeader(os, sec->sectionIndex, address);
282 os << " " << format_hex_no_prefix(size, 8) << "H";
283 os << " " << left_justify(cr.first->getSectionName(), 23);
284 os << " " << SectionClass;
285 os << '\n';
289 // Print out the symbols table (without static symbols)
290 os << "\n";
291 os << " Address Publics by Value Rva+Base"
292 " Lib:Object\n";
293 os << "\n";
294 for (Defined *sym : syms)
295 os << symStr[sym] << '\n';
297 // Print out the entry point.
298 os << "\n";
300 uint16_t entrySecIndex = 0;
301 uint64_t entryAddress = 0;
303 if (!ctx.config.noEntry) {
304 Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
305 if (entry) {
306 Chunk *chunk = entry->getChunk();
307 entrySecIndex = chunk->getOutputSectionIdx();
308 entryAddress =
309 entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
312 os << " entry point at ";
313 os << format("%04x:%08llx", entrySecIndex, entryAddress);
314 os << "\n";
316 // Print out the static symbols
317 os << "\n";
318 os << " Static symbols\n";
319 os << "\n";
320 for (Defined *sym : staticSyms)
321 os << staticSymStr[sym] << '\n';
323 // Print out the exported functions
324 if (ctx.config.mapInfo) {
325 os << "\n";
326 os << " Exports\n";
327 os << "\n";
328 os << " ordinal name\n\n";
329 for (Export &e : ctx.config.exports) {
330 os << format(" %7d", e.ordinal) << " " << e.name << "\n";
331 if (!e.extName.empty() && e.extName != e.name)
332 os << " exported name: " << e.extName << "\n";
336 t4.stop();
337 t1.stop();