xref: /freebsd/contrib/llvm-project/lld/COFF/MapFile.cpp (revision 7877fdebeeb35fad1cbbafce22598b1bdf97c786)
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 "SymbolTable.h"
32 #include "Symbols.h"
33 #include "Writer.h"
34 #include "lld/Common/ErrorHandler.h"
35 #include "lld/Common/Timer.h"
36 #include "llvm/Support/Parallel.h"
37 #include "llvm/Support/Path.h"
38 #include "llvm/Support/raw_ostream.h"
39 
40 using namespace llvm;
41 using namespace llvm::object;
42 using namespace lld;
43 using namespace lld::coff;
44 
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);
49 
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 }
54 
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);
68 }
69 
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;
98 }
99 
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) {
103 
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);
137   }
138 
139   sortUniqueSymbols(syms);
140   sortUniqueSymbols(staticSyms);
141 }
142 
143 // Construct a map from symbols to their stringified representations.
144 static DenseMap<Defined *, std::string>
145 getSymbolStrings(ArrayRef<Defined *> syms) {
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]);
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     }
196   });
197 
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;
202 }
203 
204 void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
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 
213   ScopedTimer t1(totalMapTimer);
214 
215   // Collect symbol info that we want to print out.
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, "");
230 
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";
242 
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 
251   for (OutputSection *sec : outputSections) {
252     // Merge display of chunks with same sectionName
253     std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
254     for (Chunk *c : sec->chunks) {
255       auto *sc = dyn_cast<SectionChunk>(c);
256       if (!sc)
257         continue;
258 
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';
283     }
284   }
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;
299 
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 }
323