xref: /llvm-project-15.0.7/lld/COFF/MapFile.cpp (revision 7effcbda)
16f24fdb6SPeter Collingbourne //===- MapFile.cpp --------------------------------------------------------===//
26f24fdb6SPeter Collingbourne //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66f24fdb6SPeter Collingbourne //
76f24fdb6SPeter Collingbourne //===----------------------------------------------------------------------===//
86f24fdb6SPeter Collingbourne //
9b91905a2SSylvain Audi // This file implements the /map option in the same format as link.exe
10b91905a2SSylvain Audi // (based on observations)
116f24fdb6SPeter Collingbourne //
12b91905a2SSylvain Audi // Header (program name, timestamp info, preferred load address)
136f24fdb6SPeter Collingbourne //
14b91905a2SSylvain Audi // Section list (Start = Section index:Base address):
15b91905a2SSylvain Audi // Start         Length     Name                   Class
16b91905a2SSylvain Audi // 0001:00001000 00000015H .text                   CODE
17b91905a2SSylvain Audi //
18b91905a2SSylvain Audi // Symbols list:
19b91905a2SSylvain Audi // Address        Publics by Value    Rva + Base          Lib:Object
20b91905a2SSylvain Audi // 0001:00001000  main                 0000000140001000    main.obj
21b91905a2SSylvain Audi // 0001:00001300  ?__scrt_common_main@@YAHXZ  0000000140001300 libcmt:exe_main.obj
22b91905a2SSylvain Audi //
23b91905a2SSylvain Audi // entry point at        0001:00000360
24b91905a2SSylvain Audi //
25b91905a2SSylvain Audi // Static symbols
26b91905a2SSylvain Audi //
27b91905a2SSylvain Audi // 0000:00000000  __guard_fids__       0000000140000000     libcmt : exe_main.obj
286f24fdb6SPeter Collingbourne //===----------------------------------------------------------------------===//
296f24fdb6SPeter Collingbourne 
306f24fdb6SPeter Collingbourne #include "MapFile.h"
316f7483b1SAmy Huang #include "COFFLinkerContext.h"
323969acf8SRui Ueyama #include "SymbolTable.h"
336f24fdb6SPeter Collingbourne #include "Symbols.h"
346f24fdb6SPeter Collingbourne #include "Writer.h"
35b8a59c8aSBob Haarman #include "lld/Common/ErrorHandler.h"
36b91905a2SSylvain Audi #include "lld/Common/Timer.h"
37932f0276SReid Kleckner #include "llvm/Support/Parallel.h"
38b91905a2SSylvain Audi #include "llvm/Support/Path.h"
39543731f9SRui Ueyama #include "llvm/Support/raw_ostream.h"
406f24fdb6SPeter Collingbourne 
416f24fdb6SPeter Collingbourne using namespace llvm;
426f24fdb6SPeter Collingbourne using namespace llvm::object;
438a310f40SReid Kleckner using namespace lld;
448a310f40SReid Kleckner using namespace lld::coff;
456f24fdb6SPeter Collingbourne 
46b91905a2SSylvain Audi // Print out the first two columns of a line.
writeHeader(raw_ostream & os,uint32_t sec,uint64_t addr)47b91905a2SSylvain Audi static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
48b91905a2SSylvain Audi   os << format(" %04x:%08llx", sec, addr);
496f24fdb6SPeter Collingbourne }
506f24fdb6SPeter Collingbourne 
51b91905a2SSylvain Audi // Write the time stamp with the format used by link.exe
52b91905a2SSylvain Audi // It seems identical to strftime with "%c" on msvc build, but we need a
53b91905a2SSylvain Audi // locale-agnostic version.
writeFormattedTimestamp(raw_ostream & os,time_t tds)54b91905a2SSylvain Audi static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
55b91905a2SSylvain Audi   constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
56b91905a2SSylvain Audi                                          "Thu", "Fri", "Sat"};
57b91905a2SSylvain Audi   constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
58b91905a2SSylvain Audi                                             "May", "Jun", "Jul", "Aug",
59b91905a2SSylvain Audi                                             "Sep", "Oct", "Nov", "Dec"};
60b91905a2SSylvain Audi   tm *time = localtime(&tds);
61b91905a2SSylvain Audi   os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
62b91905a2SSylvain Audi                months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
63b91905a2SSylvain Audi                time->tm_sec, time->tm_year + 1900);
646f24fdb6SPeter Collingbourne }
656f24fdb6SPeter Collingbourne 
sortUniqueSymbols(std::vector<Defined * > & syms)66b91905a2SSylvain Audi static void sortUniqueSymbols(std::vector<Defined *> &syms) {
67b91905a2SSylvain Audi   // Build helper vector
68b91905a2SSylvain Audi   using SortEntry = std::pair<Defined *, size_t>;
69b91905a2SSylvain Audi   std::vector<SortEntry> v;
70b91905a2SSylvain Audi   v.resize(syms.size());
71b91905a2SSylvain Audi   for (size_t i = 0, e = syms.size(); i < e; ++i)
72b91905a2SSylvain Audi     v[i] = SortEntry(syms[i], i);
733969acf8SRui Ueyama 
74b91905a2SSylvain Audi   // Remove duplicate symbol pointers
75b91905a2SSylvain Audi   parallelSort(v, std::less<SortEntry>());
76b91905a2SSylvain Audi   auto end = std::unique(v.begin(), v.end(),
77b91905a2SSylvain Audi                          [](const SortEntry &a, const SortEntry &b) {
78b91905a2SSylvain Audi                            return a.first == b.first;
793969acf8SRui Ueyama                          });
80b91905a2SSylvain Audi   v.erase(end, v.end());
81b91905a2SSylvain Audi 
82b91905a2SSylvain Audi   // Sort by RVA then original order
83b91905a2SSylvain Audi   parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
84b91905a2SSylvain Audi     // Add config->imageBase to avoid comparing "negative" RVAs.
85b91905a2SSylvain Audi     // This can happen with symbols of Absolute kind
86b91905a2SSylvain Audi     uint64_t rvaa = config->imageBase + a.first->getRVA();
87b91905a2SSylvain Audi     uint64_t rvab = config->imageBase + b.first->getRVA();
88b91905a2SSylvain Audi     return rvaa < rvab || (rvaa == rvab && a.second < b.second);
89b91905a2SSylvain Audi   });
90b91905a2SSylvain Audi 
91b91905a2SSylvain Audi   syms.resize(v.size());
92b91905a2SSylvain Audi   for (size_t i = 0, e = v.size(); i < e; ++i)
93b91905a2SSylvain Audi     syms[i] = v[i].first;
943969acf8SRui Ueyama }
95b91905a2SSylvain Audi 
96b91905a2SSylvain Audi // Returns the lists of all symbols that we want to print out.
getSymbols(const COFFLinkerContext & ctx,std::vector<Defined * > & syms,std::vector<Defined * > & staticSyms)976f7483b1SAmy Huang static void getSymbols(const COFFLinkerContext &ctx,
986f7483b1SAmy Huang                        std::vector<Defined *> &syms,
99a2fd05adSAmy Huang                        std::vector<Defined *> &staticSyms) {
100b91905a2SSylvain Audi 
1016f7483b1SAmy Huang   for (ObjFile *file : ctx.objFileInstances)
102b91905a2SSylvain Audi     for (Symbol *b : file->getSymbols()) {
103b91905a2SSylvain Audi       if (!b || !b->isLive())
104b91905a2SSylvain Audi         continue;
105b91905a2SSylvain Audi       if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
106b91905a2SSylvain Audi         COFFSymbolRef symRef = sym->getCOFFSymbol();
107b91905a2SSylvain Audi         if (!symRef.isSectionDefinition() &&
108b91905a2SSylvain Audi             symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
109b91905a2SSylvain Audi           if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
110b91905a2SSylvain Audi             staticSyms.push_back(sym);
111b91905a2SSylvain Audi           else
112b91905a2SSylvain Audi             syms.push_back(sym);
113b91905a2SSylvain Audi         }
114b91905a2SSylvain Audi       } else if (auto *sym = dyn_cast<Defined>(b)) {
115b91905a2SSylvain Audi         syms.push_back(sym);
116b91905a2SSylvain Audi       }
117b91905a2SSylvain Audi     }
118b91905a2SSylvain Audi 
1196f7483b1SAmy Huang   for (ImportFile *file : ctx.importFileInstances) {
120b91905a2SSylvain Audi     if (!file->live)
121b91905a2SSylvain Audi       continue;
122b91905a2SSylvain Audi 
123b91905a2SSylvain Audi     if (!file->thunkSym)
124b91905a2SSylvain Audi       continue;
125b91905a2SSylvain Audi 
126b91905a2SSylvain Audi     if (!file->thunkLive)
127b91905a2SSylvain Audi       continue;
128b91905a2SSylvain Audi 
129b91905a2SSylvain Audi     if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
130b91905a2SSylvain Audi       syms.push_back(thunkSym);
131b91905a2SSylvain Audi 
132b91905a2SSylvain Audi     if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
133b91905a2SSylvain Audi       syms.push_back(impSym);
134b91905a2SSylvain Audi   }
135b91905a2SSylvain Audi 
136b91905a2SSylvain Audi   sortUniqueSymbols(syms);
137b91905a2SSylvain Audi   sortUniqueSymbols(staticSyms);
1386f24fdb6SPeter Collingbourne }
1396f24fdb6SPeter Collingbourne 
1403969acf8SRui Ueyama // Construct a map from symbols to their stringified representations.
141b91905a2SSylvain Audi static DenseMap<Defined *, std::string>
getSymbolStrings(const COFFLinkerContext & ctx,ArrayRef<Defined * > syms)1426f7483b1SAmy Huang getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
143136d27abSRui Ueyama   std::vector<std::string> str(syms.size());
144*7effcbdaSNico Weber   parallelFor((size_t)0, syms.size(), [&](size_t i) {
145136d27abSRui Ueyama     raw_string_ostream os(str[i]);
146b91905a2SSylvain Audi     Defined *sym = syms[i];
147b91905a2SSylvain Audi 
148b91905a2SSylvain Audi     uint16_t sectionIdx = 0;
149b91905a2SSylvain Audi     uint64_t address = 0;
150b91905a2SSylvain Audi     SmallString<128> fileDescr;
151b91905a2SSylvain Audi 
152b91905a2SSylvain Audi     if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
153b91905a2SSylvain Audi       address = absSym->getVA();
154b91905a2SSylvain Audi       fileDescr = "<absolute>";
155b91905a2SSylvain Audi     } else if (isa<DefinedSynthetic>(sym)) {
156b91905a2SSylvain Audi       fileDescr = "<linker-defined>";
157b91905a2SSylvain Audi     } else if (isa<DefinedCommon>(sym)) {
158b91905a2SSylvain Audi       fileDescr = "<common>";
159b91905a2SSylvain Audi     } else if (Chunk *chunk = sym->getChunk()) {
160b91905a2SSylvain Audi       address = sym->getRVA();
1616f7483b1SAmy Huang       if (OutputSection *sec = ctx.getOutputSection(chunk))
162b91905a2SSylvain Audi         address -= sec->header.VirtualAddress;
163b91905a2SSylvain Audi 
164b91905a2SSylvain Audi       sectionIdx = chunk->getOutputSectionIdx();
165b91905a2SSylvain Audi 
166b91905a2SSylvain Audi       InputFile *file;
167b91905a2SSylvain Audi       if (auto *impSym = dyn_cast<DefinedImportData>(sym))
168b91905a2SSylvain Audi         file = impSym->file;
169b91905a2SSylvain Audi       else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
170b91905a2SSylvain Audi         file = thunkSym->wrappedSym->file;
171b91905a2SSylvain Audi       else
172b91905a2SSylvain Audi         file = sym->getFile();
173b91905a2SSylvain Audi 
174b91905a2SSylvain Audi       if (file) {
175b91905a2SSylvain Audi         if (!file->parentName.empty()) {
176b91905a2SSylvain Audi           fileDescr = sys::path::filename(file->parentName);
177b91905a2SSylvain Audi           sys::path::replace_extension(fileDescr, "");
178b91905a2SSylvain Audi           fileDescr += ":";
179b91905a2SSylvain Audi         }
180b91905a2SSylvain Audi         fileDescr += sys::path::filename(file->getName());
181b91905a2SSylvain Audi       }
182b91905a2SSylvain Audi     }
183b91905a2SSylvain Audi     writeHeader(os, sectionIdx, address);
184b91905a2SSylvain Audi     os << "       ";
185b91905a2SSylvain Audi     os << left_justify(sym->getName(), 26);
186b91905a2SSylvain Audi     os << " ";
187b91905a2SSylvain Audi     os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
188b91905a2SSylvain Audi     if (!fileDescr.empty()) {
189b91905a2SSylvain Audi       os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
190b91905a2SSylvain Audi                      // by link.exe in those spaces
191b91905a2SSylvain Audi       os << fileDescr;
192b91905a2SSylvain Audi     }
1933969acf8SRui Ueyama   });
1946f24fdb6SPeter Collingbourne 
195b91905a2SSylvain Audi   DenseMap<Defined *, std::string> ret;
196136d27abSRui Ueyama   for (size_t i = 0, e = syms.size(); i < e; ++i)
197136d27abSRui Ueyama     ret[syms[i]] = std::move(str[i]);
198136d27abSRui Ueyama   return ret;
1996f24fdb6SPeter Collingbourne }
2006f24fdb6SPeter Collingbourne 
writeMapFile(COFFLinkerContext & ctx)2016f7483b1SAmy Huang void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
202136d27abSRui Ueyama   if (config->mapFile.empty())
2036f24fdb6SPeter Collingbourne     return;
2046f24fdb6SPeter Collingbourne 
205136d27abSRui Ueyama   std::error_code ec;
206d9b948b6SFangrui Song   raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
207136d27abSRui Ueyama   if (ec)
208136d27abSRui Ueyama     fatal("cannot open " + config->mapFile + ": " + ec.message());
2093969acf8SRui Ueyama 
2106f7483b1SAmy Huang   ScopedTimer t1(ctx.totalMapTimer);
211b91905a2SSylvain Audi 
2123969acf8SRui Ueyama   // Collect symbol info that we want to print out.
2136f7483b1SAmy Huang   ScopedTimer t2(ctx.symbolGatherTimer);
214b91905a2SSylvain Audi   std::vector<Defined *> syms;
215b91905a2SSylvain Audi   std::vector<Defined *> staticSyms;
2166f7483b1SAmy Huang   getSymbols(ctx, syms, staticSyms);
217b91905a2SSylvain Audi   t2.stop();
2183969acf8SRui Ueyama 
2196f7483b1SAmy Huang   ScopedTimer t3(ctx.symbolStringsTimer);
2206f7483b1SAmy Huang   DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
2216f7483b1SAmy Huang   DenseMap<Defined *, std::string> staticSymStr =
2226f7483b1SAmy Huang       getSymbolStrings(ctx, staticSyms);
223b91905a2SSylvain Audi   t3.stop();
2243969acf8SRui Ueyama 
2256f7483b1SAmy Huang   ScopedTimer t4(ctx.writeTimer);
226b91905a2SSylvain Audi   SmallString<128> AppName = sys::path::filename(config->outputFile);
227b91905a2SSylvain Audi   sys::path::replace_extension(AppName, "");
228b91905a2SSylvain Audi 
229b91905a2SSylvain Audi   // Print out the file header
230b91905a2SSylvain Audi   os << " " << AppName << "\n";
231b91905a2SSylvain Audi   os << "\n";
232b91905a2SSylvain Audi 
233b91905a2SSylvain Audi   os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
234b91905a2SSylvain Audi   if (config->repro) {
235b91905a2SSylvain Audi     os << "Repro mode";
236b91905a2SSylvain Audi   } else {
237b91905a2SSylvain Audi     writeFormattedTimestamp(os, config->timestamp);
238b91905a2SSylvain Audi   }
239b91905a2SSylvain Audi   os << ")\n";
240b91905a2SSylvain Audi 
241b91905a2SSylvain Audi   os << "\n";
242b91905a2SSylvain Audi   os << " Preferred load address is "
243b91905a2SSylvain Audi      << format_hex_no_prefix(config->imageBase, 16) << "\n";
244b91905a2SSylvain Audi   os << "\n";
245b91905a2SSylvain Audi 
246b91905a2SSylvain Audi   // Print out section table.
247b91905a2SSylvain Audi   os << " Start         Length     Name                   Class\n";
248b91905a2SSylvain Audi 
2496f7483b1SAmy Huang   for (OutputSection *sec : ctx.outputSections) {
250b91905a2SSylvain Audi     // Merge display of chunks with same sectionName
251b91905a2SSylvain Audi     std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
252136d27abSRui Ueyama     for (Chunk *c : sec->chunks) {
253136d27abSRui Ueyama       auto *sc = dyn_cast<SectionChunk>(c);
254136d27abSRui Ueyama       if (!sc)
2553969acf8SRui Ueyama         continue;
2563969acf8SRui Ueyama 
257b91905a2SSylvain Audi       if (ChunkRanges.empty() ||
258b91905a2SSylvain Audi           c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
259b91905a2SSylvain Audi         ChunkRanges.emplace_back(sc, sc);
260b91905a2SSylvain Audi       } else {
261b91905a2SSylvain Audi         ChunkRanges.back().second = sc;
262b91905a2SSylvain Audi       }
263b91905a2SSylvain Audi     }
264b91905a2SSylvain Audi 
265b91905a2SSylvain Audi     const bool isCodeSection =
266b91905a2SSylvain Audi         (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
267b91905a2SSylvain Audi         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
268b91905a2SSylvain Audi         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
269b91905a2SSylvain Audi     StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
270b91905a2SSylvain Audi 
271b91905a2SSylvain Audi     for (auto &cr : ChunkRanges) {
272b91905a2SSylvain Audi       size_t size =
273b91905a2SSylvain Audi           cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
274b91905a2SSylvain Audi 
275b91905a2SSylvain Audi       auto address = cr.first->getRVA() - sec->header.VirtualAddress;
276b91905a2SSylvain Audi       writeHeader(os, sec->sectionIndex, address);
277b91905a2SSylvain Audi       os << " " << format_hex_no_prefix(size, 8) << "H";
278b91905a2SSylvain Audi       os << " " << left_justify(cr.first->getSectionName(), 23);
279b91905a2SSylvain Audi       os << " " << SectionClass;
280b91905a2SSylvain Audi       os << '\n';
281b91905a2SSylvain Audi     }
282b91905a2SSylvain Audi   }
283b91905a2SSylvain Audi 
284b91905a2SSylvain Audi   // Print out the symbols table (without static symbols)
285b91905a2SSylvain Audi   os << "\n";
286b91905a2SSylvain Audi   os << "  Address         Publics by Value              Rva+Base"
287b91905a2SSylvain Audi         "               Lib:Object\n";
288b91905a2SSylvain Audi   os << "\n";
289b91905a2SSylvain Audi   for (Defined *sym : syms)
290136d27abSRui Ueyama     os << symStr[sym] << '\n';
291b91905a2SSylvain Audi 
292b91905a2SSylvain Audi   // Print out the entry point.
293b91905a2SSylvain Audi   os << "\n";
294b91905a2SSylvain Audi 
295b91905a2SSylvain Audi   uint16_t entrySecIndex = 0;
296b91905a2SSylvain Audi   uint64_t entryAddress = 0;
297b91905a2SSylvain Audi 
298b91905a2SSylvain Audi   if (!config->noEntry) {
299b91905a2SSylvain Audi     Defined *entry = dyn_cast_or_null<Defined>(config->entry);
300b91905a2SSylvain Audi     if (entry) {
301b91905a2SSylvain Audi       Chunk *chunk = entry->getChunk();
302b91905a2SSylvain Audi       entrySecIndex = chunk->getOutputSectionIdx();
303b91905a2SSylvain Audi       entryAddress =
3046f7483b1SAmy Huang           entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
3053969acf8SRui Ueyama     }
3063969acf8SRui Ueyama   }
307b91905a2SSylvain Audi   os << " entry point at         ";
308b91905a2SSylvain Audi   os << format("%04x:%08llx", entrySecIndex, entryAddress);
309b91905a2SSylvain Audi   os << "\n";
310b91905a2SSylvain Audi 
311b91905a2SSylvain Audi   // Print out the static symbols
312b91905a2SSylvain Audi   os << "\n";
313b91905a2SSylvain Audi   os << " Static symbols\n";
314b91905a2SSylvain Audi   os << "\n";
315b91905a2SSylvain Audi   for (Defined *sym : staticSyms)
316b91905a2SSylvain Audi     os << staticSymStr[sym] << '\n';
317b91905a2SSylvain Audi 
318b91905a2SSylvain Audi   t4.stop();
319b91905a2SSylvain Audi   t1.stop();
3206f24fdb6SPeter Collingbourne }
321