xref: /freebsd/contrib/llvm-project/lld/COFF/MapFile.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
10b57cec5SDimitry Andric //===- MapFile.cpp --------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
9*5ffd83dbSDimitry Andric // This file implements the /map option in the same format as link.exe
10*5ffd83dbSDimitry Andric // (based on observations)
110b57cec5SDimitry Andric //
12*5ffd83dbSDimitry Andric // Header (program name, timestamp info, preferred load address)
130b57cec5SDimitry Andric //
14*5ffd83dbSDimitry Andric // Section list (Start = Section index:Base address):
15*5ffd83dbSDimitry Andric // Start         Length     Name                   Class
16*5ffd83dbSDimitry Andric // 0001:00001000 00000015H .text                   CODE
17*5ffd83dbSDimitry Andric //
18*5ffd83dbSDimitry Andric // Symbols list:
19*5ffd83dbSDimitry Andric // Address        Publics by Value    Rva + Base          Lib:Object
20*5ffd83dbSDimitry Andric // 0001:00001000  main                 0000000140001000    main.obj
21*5ffd83dbSDimitry Andric // 0001:00001300  ?__scrt_common_main@@YAHXZ  0000000140001300 libcmt:exe_main.obj
22*5ffd83dbSDimitry Andric //
23*5ffd83dbSDimitry Andric // entry point at        0001:00000360
24*5ffd83dbSDimitry Andric //
25*5ffd83dbSDimitry Andric // Static symbols
26*5ffd83dbSDimitry Andric //
27*5ffd83dbSDimitry Andric // 0000:00000000  __guard_fids__       0000000140000000     libcmt : exe_main.obj
280b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric #include "MapFile.h"
310b57cec5SDimitry Andric #include "SymbolTable.h"
320b57cec5SDimitry Andric #include "Symbols.h"
330b57cec5SDimitry Andric #include "Writer.h"
340b57cec5SDimitry Andric #include "lld/Common/ErrorHandler.h"
35*5ffd83dbSDimitry Andric #include "lld/Common/Timer.h"
36*5ffd83dbSDimitry Andric #include "llvm/Support/Parallel.h"
37*5ffd83dbSDimitry Andric #include "llvm/Support/Path.h"
380b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric using namespace llvm;
410b57cec5SDimitry Andric using namespace llvm::object;
42*5ffd83dbSDimitry Andric using namespace lld;
43*5ffd83dbSDimitry Andric using namespace lld::coff;
440b57cec5SDimitry Andric 
45*5ffd83dbSDimitry Andric static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root());
46*5ffd83dbSDimitry Andric static Timer symbolGatherTimer("Gather symbols", totalMapTimer);
47*5ffd83dbSDimitry Andric static Timer symbolStringsTimer("Build symbol strings", totalMapTimer);
48*5ffd83dbSDimitry Andric static Timer writeTimer("Write to file", totalMapTimer);
490b57cec5SDimitry Andric 
50*5ffd83dbSDimitry Andric // Print out the first two columns of a line.
51*5ffd83dbSDimitry Andric static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
52*5ffd83dbSDimitry Andric   os << format(" %04x:%08llx", sec, addr);
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric 
55*5ffd83dbSDimitry Andric // Write the time stamp with the format used by link.exe
56*5ffd83dbSDimitry Andric // It seems identical to strftime with "%c" on msvc build, but we need a
57*5ffd83dbSDimitry Andric // locale-agnostic version.
58*5ffd83dbSDimitry Andric static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
59*5ffd83dbSDimitry Andric   constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
60*5ffd83dbSDimitry Andric                                          "Thu", "Fri", "Sat"};
61*5ffd83dbSDimitry Andric   constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
62*5ffd83dbSDimitry Andric                                             "May", "Jun", "Jul", "Aug",
63*5ffd83dbSDimitry Andric                                             "Sep", "Oct", "Nov", "Dec"};
64*5ffd83dbSDimitry Andric   tm *time = localtime(&tds);
65*5ffd83dbSDimitry Andric   os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
66*5ffd83dbSDimitry Andric                months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
67*5ffd83dbSDimitry Andric                time->tm_sec, time->tm_year + 1900);
680b57cec5SDimitry Andric }
690b57cec5SDimitry Andric 
70*5ffd83dbSDimitry Andric static void sortUniqueSymbols(std::vector<Defined *> &syms) {
71*5ffd83dbSDimitry Andric   // Build helper vector
72*5ffd83dbSDimitry Andric   using SortEntry = std::pair<Defined *, size_t>;
73*5ffd83dbSDimitry Andric   std::vector<SortEntry> v;
74*5ffd83dbSDimitry Andric   v.resize(syms.size());
75*5ffd83dbSDimitry Andric   for (size_t i = 0, e = syms.size(); i < e; ++i)
76*5ffd83dbSDimitry Andric     v[i] = SortEntry(syms[i], i);
770b57cec5SDimitry Andric 
78*5ffd83dbSDimitry Andric   // Remove duplicate symbol pointers
79*5ffd83dbSDimitry Andric   parallelSort(v, std::less<SortEntry>());
80*5ffd83dbSDimitry Andric   auto end = std::unique(v.begin(), v.end(),
81*5ffd83dbSDimitry Andric                          [](const SortEntry &a, const SortEntry &b) {
82*5ffd83dbSDimitry Andric                            return a.first == b.first;
830b57cec5SDimitry Andric                          });
84*5ffd83dbSDimitry Andric   v.erase(end, v.end());
85*5ffd83dbSDimitry Andric 
86*5ffd83dbSDimitry Andric   // Sort by RVA then original order
87*5ffd83dbSDimitry Andric   parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
88*5ffd83dbSDimitry Andric     // Add config->imageBase to avoid comparing "negative" RVAs.
89*5ffd83dbSDimitry Andric     // This can happen with symbols of Absolute kind
90*5ffd83dbSDimitry Andric     uint64_t rvaa = config->imageBase + a.first->getRVA();
91*5ffd83dbSDimitry Andric     uint64_t rvab = config->imageBase + b.first->getRVA();
92*5ffd83dbSDimitry Andric     return rvaa < rvab || (rvaa == rvab && a.second < b.second);
93*5ffd83dbSDimitry Andric   });
94*5ffd83dbSDimitry Andric 
95*5ffd83dbSDimitry Andric   syms.resize(v.size());
96*5ffd83dbSDimitry Andric   for (size_t i = 0, e = v.size(); i < e; ++i)
97*5ffd83dbSDimitry Andric     syms[i] = v[i].first;
980b57cec5SDimitry Andric }
99*5ffd83dbSDimitry Andric 
100*5ffd83dbSDimitry Andric // Returns the lists of all symbols that we want to print out.
101*5ffd83dbSDimitry Andric static void getSymbols(std::vector<Defined *> &syms,
102*5ffd83dbSDimitry Andric                        std::vector<Defined *> &staticSyms) {
103*5ffd83dbSDimitry Andric 
104*5ffd83dbSDimitry Andric   for (ObjFile *file : ObjFile::instances)
105*5ffd83dbSDimitry Andric     for (Symbol *b : file->getSymbols()) {
106*5ffd83dbSDimitry Andric       if (!b || !b->isLive())
107*5ffd83dbSDimitry Andric         continue;
108*5ffd83dbSDimitry Andric       if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
109*5ffd83dbSDimitry Andric         COFFSymbolRef symRef = sym->getCOFFSymbol();
110*5ffd83dbSDimitry Andric         if (!symRef.isSectionDefinition() &&
111*5ffd83dbSDimitry Andric             symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
112*5ffd83dbSDimitry Andric           if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
113*5ffd83dbSDimitry Andric             staticSyms.push_back(sym);
114*5ffd83dbSDimitry Andric           else
115*5ffd83dbSDimitry Andric             syms.push_back(sym);
116*5ffd83dbSDimitry Andric         }
117*5ffd83dbSDimitry Andric       } else if (auto *sym = dyn_cast<Defined>(b)) {
118*5ffd83dbSDimitry Andric         syms.push_back(sym);
119*5ffd83dbSDimitry Andric       }
120*5ffd83dbSDimitry Andric     }
121*5ffd83dbSDimitry Andric 
122*5ffd83dbSDimitry Andric   for (ImportFile *file : ImportFile::instances) {
123*5ffd83dbSDimitry Andric     if (!file->live)
124*5ffd83dbSDimitry Andric       continue;
125*5ffd83dbSDimitry Andric 
126*5ffd83dbSDimitry Andric     if (!file->thunkSym)
127*5ffd83dbSDimitry Andric       continue;
128*5ffd83dbSDimitry Andric 
129*5ffd83dbSDimitry Andric     if (!file->thunkLive)
130*5ffd83dbSDimitry Andric       continue;
131*5ffd83dbSDimitry Andric 
132*5ffd83dbSDimitry Andric     if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
133*5ffd83dbSDimitry Andric       syms.push_back(thunkSym);
134*5ffd83dbSDimitry Andric 
135*5ffd83dbSDimitry Andric     if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
136*5ffd83dbSDimitry Andric       syms.push_back(impSym);
137*5ffd83dbSDimitry Andric   }
138*5ffd83dbSDimitry Andric 
139*5ffd83dbSDimitry Andric   sortUniqueSymbols(syms);
140*5ffd83dbSDimitry Andric   sortUniqueSymbols(staticSyms);
1410b57cec5SDimitry Andric }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric // Construct a map from symbols to their stringified representations.
144*5ffd83dbSDimitry Andric static DenseMap<Defined *, std::string>
145*5ffd83dbSDimitry Andric getSymbolStrings(ArrayRef<Defined *> syms) {
1460b57cec5SDimitry Andric   std::vector<std::string> str(syms.size());
1470b57cec5SDimitry Andric   parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
1480b57cec5SDimitry Andric     raw_string_ostream os(str[i]);
149*5ffd83dbSDimitry Andric     Defined *sym = syms[i];
150*5ffd83dbSDimitry Andric 
151*5ffd83dbSDimitry Andric     uint16_t sectionIdx = 0;
152*5ffd83dbSDimitry Andric     uint64_t address = 0;
153*5ffd83dbSDimitry Andric     SmallString<128> fileDescr;
154*5ffd83dbSDimitry Andric 
155*5ffd83dbSDimitry Andric     if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
156*5ffd83dbSDimitry Andric       address = absSym->getVA();
157*5ffd83dbSDimitry Andric       fileDescr = "<absolute>";
158*5ffd83dbSDimitry Andric     } else if (isa<DefinedSynthetic>(sym)) {
159*5ffd83dbSDimitry Andric       fileDescr = "<linker-defined>";
160*5ffd83dbSDimitry Andric     } else if (isa<DefinedCommon>(sym)) {
161*5ffd83dbSDimitry Andric       fileDescr = "<common>";
162*5ffd83dbSDimitry Andric     } else if (Chunk *chunk = sym->getChunk()) {
163*5ffd83dbSDimitry Andric       address = sym->getRVA();
164*5ffd83dbSDimitry Andric       if (OutputSection *sec = chunk->getOutputSection())
165*5ffd83dbSDimitry Andric         address -= sec->header.VirtualAddress;
166*5ffd83dbSDimitry Andric 
167*5ffd83dbSDimitry Andric       sectionIdx = chunk->getOutputSectionIdx();
168*5ffd83dbSDimitry Andric 
169*5ffd83dbSDimitry Andric       InputFile *file;
170*5ffd83dbSDimitry Andric       if (auto *impSym = dyn_cast<DefinedImportData>(sym))
171*5ffd83dbSDimitry Andric         file = impSym->file;
172*5ffd83dbSDimitry Andric       else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
173*5ffd83dbSDimitry Andric         file = thunkSym->wrappedSym->file;
174*5ffd83dbSDimitry Andric       else
175*5ffd83dbSDimitry Andric         file = sym->getFile();
176*5ffd83dbSDimitry Andric 
177*5ffd83dbSDimitry Andric       if (file) {
178*5ffd83dbSDimitry Andric         if (!file->parentName.empty()) {
179*5ffd83dbSDimitry Andric           fileDescr = sys::path::filename(file->parentName);
180*5ffd83dbSDimitry Andric           sys::path::replace_extension(fileDescr, "");
181*5ffd83dbSDimitry Andric           fileDescr += ":";
182*5ffd83dbSDimitry Andric         }
183*5ffd83dbSDimitry Andric         fileDescr += sys::path::filename(file->getName());
184*5ffd83dbSDimitry Andric       }
185*5ffd83dbSDimitry Andric     }
186*5ffd83dbSDimitry Andric     writeHeader(os, sectionIdx, address);
187*5ffd83dbSDimitry Andric     os << "       ";
188*5ffd83dbSDimitry Andric     os << left_justify(sym->getName(), 26);
189*5ffd83dbSDimitry Andric     os << " ";
190*5ffd83dbSDimitry Andric     os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
191*5ffd83dbSDimitry Andric     if (!fileDescr.empty()) {
192*5ffd83dbSDimitry Andric       os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
193*5ffd83dbSDimitry Andric                      // by link.exe in those spaces
194*5ffd83dbSDimitry Andric       os << fileDescr;
195*5ffd83dbSDimitry Andric     }
1960b57cec5SDimitry Andric   });
1970b57cec5SDimitry Andric 
198*5ffd83dbSDimitry Andric   DenseMap<Defined *, std::string> ret;
1990b57cec5SDimitry Andric   for (size_t i = 0, e = syms.size(); i < e; ++i)
2000b57cec5SDimitry Andric     ret[syms[i]] = std::move(str[i]);
2010b57cec5SDimitry Andric   return ret;
2020b57cec5SDimitry Andric }
2030b57cec5SDimitry Andric 
204*5ffd83dbSDimitry Andric void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
2050b57cec5SDimitry Andric   if (config->mapFile.empty())
2060b57cec5SDimitry Andric     return;
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric   std::error_code ec;
20985868e8aSDimitry Andric   raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
2100b57cec5SDimitry Andric   if (ec)
2110b57cec5SDimitry Andric     fatal("cannot open " + config->mapFile + ": " + ec.message());
2120b57cec5SDimitry Andric 
213*5ffd83dbSDimitry Andric   ScopedTimer t1(totalMapTimer);
214*5ffd83dbSDimitry Andric 
2150b57cec5SDimitry Andric   // Collect symbol info that we want to print out.
216*5ffd83dbSDimitry Andric   ScopedTimer t2(symbolGatherTimer);
217*5ffd83dbSDimitry Andric   std::vector<Defined *> syms;
218*5ffd83dbSDimitry Andric   std::vector<Defined *> staticSyms;
219*5ffd83dbSDimitry Andric   getSymbols(syms, staticSyms);
220*5ffd83dbSDimitry Andric   t2.stop();
2210b57cec5SDimitry Andric 
222*5ffd83dbSDimitry Andric   ScopedTimer t3(symbolStringsTimer);
223*5ffd83dbSDimitry Andric   DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
224*5ffd83dbSDimitry Andric   DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
225*5ffd83dbSDimitry Andric   t3.stop();
2260b57cec5SDimitry Andric 
227*5ffd83dbSDimitry Andric   ScopedTimer t4(writeTimer);
228*5ffd83dbSDimitry Andric   SmallString<128> AppName = sys::path::filename(config->outputFile);
229*5ffd83dbSDimitry Andric   sys::path::replace_extension(AppName, "");
230*5ffd83dbSDimitry Andric 
231*5ffd83dbSDimitry Andric   // Print out the file header
232*5ffd83dbSDimitry Andric   os << " " << AppName << "\n";
233*5ffd83dbSDimitry Andric   os << "\n";
234*5ffd83dbSDimitry Andric 
235*5ffd83dbSDimitry Andric   os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
236*5ffd83dbSDimitry Andric   if (config->repro) {
237*5ffd83dbSDimitry Andric     os << "Repro mode";
238*5ffd83dbSDimitry Andric   } else {
239*5ffd83dbSDimitry Andric     writeFormattedTimestamp(os, config->timestamp);
240*5ffd83dbSDimitry Andric   }
241*5ffd83dbSDimitry Andric   os << ")\n";
242*5ffd83dbSDimitry Andric 
243*5ffd83dbSDimitry Andric   os << "\n";
244*5ffd83dbSDimitry Andric   os << " Preferred load address is "
245*5ffd83dbSDimitry Andric      << format_hex_no_prefix(config->imageBase, 16) << "\n";
246*5ffd83dbSDimitry Andric   os << "\n";
247*5ffd83dbSDimitry Andric 
248*5ffd83dbSDimitry Andric   // Print out section table.
249*5ffd83dbSDimitry Andric   os << " Start         Length     Name                   Class\n";
250*5ffd83dbSDimitry Andric 
2510b57cec5SDimitry Andric   for (OutputSection *sec : outputSections) {
252*5ffd83dbSDimitry Andric     // Merge display of chunks with same sectionName
253*5ffd83dbSDimitry Andric     std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
2540b57cec5SDimitry Andric     for (Chunk *c : sec->chunks) {
2550b57cec5SDimitry Andric       auto *sc = dyn_cast<SectionChunk>(c);
2560b57cec5SDimitry Andric       if (!sc)
2570b57cec5SDimitry Andric         continue;
2580b57cec5SDimitry Andric 
259*5ffd83dbSDimitry Andric       if (ChunkRanges.empty() ||
260*5ffd83dbSDimitry Andric           c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
261*5ffd83dbSDimitry Andric         ChunkRanges.emplace_back(sc, sc);
262*5ffd83dbSDimitry Andric       } else {
263*5ffd83dbSDimitry Andric         ChunkRanges.back().second = sc;
2640b57cec5SDimitry Andric       }
2650b57cec5SDimitry Andric     }
26685868e8aSDimitry Andric 
267*5ffd83dbSDimitry Andric     const bool isCodeSection =
268*5ffd83dbSDimitry Andric         (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
269*5ffd83dbSDimitry Andric         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
270*5ffd83dbSDimitry Andric         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
271*5ffd83dbSDimitry Andric     StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
272*5ffd83dbSDimitry Andric 
273*5ffd83dbSDimitry Andric     for (auto &cr : ChunkRanges) {
274*5ffd83dbSDimitry Andric       size_t size =
275*5ffd83dbSDimitry Andric           cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
276*5ffd83dbSDimitry Andric 
277*5ffd83dbSDimitry Andric       auto address = cr.first->getRVA() - sec->header.VirtualAddress;
278*5ffd83dbSDimitry Andric       writeHeader(os, sec->sectionIndex, address);
279*5ffd83dbSDimitry Andric       os << " " << format_hex_no_prefix(size, 8) << "H";
280*5ffd83dbSDimitry Andric       os << " " << left_justify(cr.first->getSectionName(), 23);
281*5ffd83dbSDimitry Andric       os << " " << SectionClass;
282*5ffd83dbSDimitry Andric       os << '\n';
283*5ffd83dbSDimitry Andric     }
284*5ffd83dbSDimitry Andric   }
285*5ffd83dbSDimitry Andric 
286*5ffd83dbSDimitry Andric   // Print out the symbols table (without static symbols)
287*5ffd83dbSDimitry Andric   os << "\n";
288*5ffd83dbSDimitry Andric   os << "  Address         Publics by Value              Rva+Base"
289*5ffd83dbSDimitry Andric         "               Lib:Object\n";
290*5ffd83dbSDimitry Andric   os << "\n";
291*5ffd83dbSDimitry Andric   for (Defined *sym : syms)
292*5ffd83dbSDimitry Andric     os << symStr[sym] << '\n';
293*5ffd83dbSDimitry Andric 
294*5ffd83dbSDimitry Andric   // Print out the entry point.
295*5ffd83dbSDimitry Andric   os << "\n";
296*5ffd83dbSDimitry Andric 
297*5ffd83dbSDimitry Andric   uint16_t entrySecIndex = 0;
298*5ffd83dbSDimitry Andric   uint64_t entryAddress = 0;
299*5ffd83dbSDimitry Andric 
300*5ffd83dbSDimitry Andric   if (!config->noEntry) {
301*5ffd83dbSDimitry Andric     Defined *entry = dyn_cast_or_null<Defined>(config->entry);
302*5ffd83dbSDimitry Andric     if (entry) {
303*5ffd83dbSDimitry Andric       Chunk *chunk = entry->getChunk();
304*5ffd83dbSDimitry Andric       entrySecIndex = chunk->getOutputSectionIdx();
305*5ffd83dbSDimitry Andric       entryAddress =
306*5ffd83dbSDimitry Andric           entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
307*5ffd83dbSDimitry Andric     }
308*5ffd83dbSDimitry Andric   }
309*5ffd83dbSDimitry Andric   os << " entry point at         ";
310*5ffd83dbSDimitry Andric   os << format("%04x:%08llx", entrySecIndex, entryAddress);
311*5ffd83dbSDimitry Andric   os << "\n";
312*5ffd83dbSDimitry Andric 
313*5ffd83dbSDimitry Andric   // Print out the static symbols
314*5ffd83dbSDimitry Andric   os << "\n";
315*5ffd83dbSDimitry Andric   os << " Static symbols\n";
316*5ffd83dbSDimitry Andric   os << "\n";
317*5ffd83dbSDimitry Andric   for (Defined *sym : staticSyms)
318*5ffd83dbSDimitry Andric     os << staticSymStr[sym] << '\n';
319*5ffd83dbSDimitry Andric 
320*5ffd83dbSDimitry Andric   t4.stop();
321*5ffd83dbSDimitry Andric   t1.stop();
322*5ffd83dbSDimitry Andric }
323