xref: /freebsd/contrib/llvm-project/lld/COFF/MapFile.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
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 
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.
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.
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 
66 static 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(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(v, std::less<SortEntry>());
77   auto end = llvm::unique(v, [](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, [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(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.
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->impSym)
124       syms.push_back(file->impSym);
125     if (file->thunkSym && file->thunkSym->isLive())
126       syms.push_back(file->thunkSym);
127     if (file->auxThunkSym && file->auxThunkSym->isLive())
128       syms.push_back(file->auxThunkSym);
129     if (file->impchkThunk)
130       syms.push_back(file->impchkThunk->sym);
131     if (file->impECSym)
132       syms.push_back(file->impECSym);
133     if (file->auxImpCopySym)
134       syms.push_back(file->auxImpCopySym);
135   }
136 
137   sortUniqueSymbols(syms, ctx.config.imageBase);
138   sortUniqueSymbols(staticSyms, ctx.config.imageBase);
139 }
140 
141 // Construct a map from symbols to their stringified representations.
142 static DenseMap<Defined *, std::string>
143 getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
144   std::vector<std::string> str(syms.size());
145   parallelFor((size_t)0, syms.size(), [&](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>(sym)) {
154       address = absSym->getVA();
155       fileDescr = "<absolute>";
156     } else if (isa<DefinedSynthetic>(sym)) {
157       fileDescr = "<linker-defined>";
158     } else if (isa<DefinedCommon>(sym)) {
159       fileDescr = "<common>";
160     } else if (Chunk *chunk = sym->getChunk()) {
161       address = sym->getRVA();
162       if (OutputSection *sec = ctx.getOutputSection(chunk))
163         address -= sec->header.VirtualAddress;
164 
165       sectionIdx = chunk->getOutputSectionIdx();
166 
167       InputFile *file;
168       if (auto *impSym = dyn_cast<DefinedImportData>(sym))
169         file = impSym->file;
170       else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(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(file->parentName);
178           sys::path::replace_extension(fileDescr, "");
179           fileDescr += ":";
180         }
181         fileDescr += sys::path::filename(file->getName());
182       }
183     }
184     writeHeader(os, sectionIdx, address);
185     os << "       ";
186     os << left_justify(sym->getName(), 26);
187     os << " ";
188     os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 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 
202 void 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, staticSyms);
225   t3.stop();
226 
227   ScopedTimer t4(ctx.writeTimer);
228   SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
229   sys::path::replace_extension(AppName, "");
230 
231   // Print out the file header
232   os << " " << AppName << "\n";
233   os << "\n";
234 
235   os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
236      << " (";
237   if (ctx.config.repro) {
238     os << "Repro mode";
239   } else {
240     writeFormattedTimestamp(os, ctx.config.timestamp);
241   }
242   os << ")\n";
243 
244   os << "\n";
245   os << " Preferred load address is "
246      << format_hex_no_prefix(ctx.config.imageBase, 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>(c);
257       if (!sc)
258         continue;
259 
260       if (ChunkRanges.empty() ||
261           c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
262         ChunkRanges.emplace_back(sc, 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->sectionIndex, address);
280       os << " " << format_hex_no_prefix(size, 8) << "H";
281       os << " " << left_justify(cr.first->getSectionName(), 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>(ctx.symtab.entry);
303     if (entry) {
304       Chunk *chunk = entry->getChunk();
305       entrySecIndex = chunk->getOutputSectionIdx();
306       entryAddress =
307           entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
308     }
309   }
310   os << " entry point at         ";
311   os << format("%04x:%08llx", entrySecIndex, 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("  %7d", 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