xref: /llvm-project/lld/COFF/MapFile.cpp (revision 455b3d6df20c9bb50cdba66fd2f3202bc43eb4ac)
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"
39356139bdSAlexandre Ganea #include "llvm/Support/TimeProfiler.h"
40543731f9SRui Ueyama #include "llvm/Support/raw_ostream.h"
416f24fdb6SPeter Collingbourne 
426f24fdb6SPeter Collingbourne using namespace llvm;
436f24fdb6SPeter Collingbourne using namespace llvm::object;
448a310f40SReid Kleckner using namespace lld;
458a310f40SReid Kleckner using namespace lld::coff;
466f24fdb6SPeter Collingbourne 
47b91905a2SSylvain Audi // Print out the first two columns of a line.
48b91905a2SSylvain Audi static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
49b91905a2SSylvain Audi   os << format(" %04x:%08llx", sec, addr);
506f24fdb6SPeter Collingbourne }
516f24fdb6SPeter Collingbourne 
52b91905a2SSylvain Audi // Write the time stamp with the format used by link.exe
53b91905a2SSylvain Audi // It seems identical to strftime with "%c" on msvc build, but we need a
54b91905a2SSylvain Audi // locale-agnostic version.
55b91905a2SSylvain Audi static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
56b91905a2SSylvain Audi   constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
57b91905a2SSylvain Audi                                          "Thu", "Fri", "Sat"};
58b91905a2SSylvain Audi   constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
59b91905a2SSylvain Audi                                             "May", "Jun", "Jul", "Aug",
60b91905a2SSylvain Audi                                             "Sep", "Oct", "Nov", "Dec"};
61b91905a2SSylvain Audi   tm *time = localtime(&tds);
62b91905a2SSylvain Audi   os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
63b91905a2SSylvain Audi                months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
64b91905a2SSylvain Audi                time->tm_sec, time->tm_year + 1900);
656f24fdb6SPeter Collingbourne }
666f24fdb6SPeter Collingbourne 
675a58b19fSAmy Huang static void sortUniqueSymbols(std::vector<Defined *> &syms,
685a58b19fSAmy Huang                               uint64_t imageBase) {
69b91905a2SSylvain Audi   // Build helper vector
70b91905a2SSylvain Audi   using SortEntry = std::pair<Defined *, size_t>;
71b91905a2SSylvain Audi   std::vector<SortEntry> v;
72b91905a2SSylvain Audi   v.resize(syms.size());
73b91905a2SSylvain Audi   for (size_t i = 0, e = syms.size(); i < e; ++i)
74b91905a2SSylvain Audi     v[i] = SortEntry(syms[i], i);
753969acf8SRui Ueyama 
76b91905a2SSylvain Audi   // Remove duplicate symbol pointers
77b91905a2SSylvain Audi   parallelSort(v, std::less<SortEntry>());
78b91905a2SSylvain Audi   auto end = std::unique(v.begin(), v.end(),
79b91905a2SSylvain Audi                          [](const SortEntry &a, const SortEntry &b) {
80b91905a2SSylvain Audi                            return a.first == b.first;
813969acf8SRui Ueyama                          });
82b91905a2SSylvain Audi   v.erase(end, v.end());
83b91905a2SSylvain Audi 
84b91905a2SSylvain Audi   // Sort by RVA then original order
855a58b19fSAmy Huang   parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
865a58b19fSAmy Huang     // Add config.imageBase to avoid comparing "negative" RVAs.
87b91905a2SSylvain Audi     // This can happen with symbols of Absolute kind
885a58b19fSAmy Huang     uint64_t rvaa = imageBase + a.first->getRVA();
895a58b19fSAmy Huang     uint64_t rvab = imageBase + b.first->getRVA();
90b91905a2SSylvain Audi     return rvaa < rvab || (rvaa == rvab && a.second < b.second);
91b91905a2SSylvain Audi   });
92b91905a2SSylvain Audi 
93b91905a2SSylvain Audi   syms.resize(v.size());
94b91905a2SSylvain Audi   for (size_t i = 0, e = v.size(); i < e; ++i)
95b91905a2SSylvain Audi     syms[i] = v[i].first;
963969acf8SRui Ueyama }
97b91905a2SSylvain Audi 
98b91905a2SSylvain Audi // Returns the lists of all symbols that we want to print out.
996f7483b1SAmy Huang static void getSymbols(const COFFLinkerContext &ctx,
1006f7483b1SAmy Huang                        std::vector<Defined *> &syms,
101a2fd05adSAmy Huang                        std::vector<Defined *> &staticSyms) {
102b91905a2SSylvain Audi 
1036f7483b1SAmy Huang   for (ObjFile *file : ctx.objFileInstances)
104b91905a2SSylvain Audi     for (Symbol *b : file->getSymbols()) {
105b91905a2SSylvain Audi       if (!b || !b->isLive())
106b91905a2SSylvain Audi         continue;
107b91905a2SSylvain Audi       if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
108b91905a2SSylvain Audi         COFFSymbolRef symRef = sym->getCOFFSymbol();
109b91905a2SSylvain Audi         if (!symRef.isSectionDefinition() &&
110b91905a2SSylvain Audi             symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
111b91905a2SSylvain Audi           if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
112b91905a2SSylvain Audi             staticSyms.push_back(sym);
113b91905a2SSylvain Audi           else
114b91905a2SSylvain Audi             syms.push_back(sym);
115b91905a2SSylvain Audi         }
116b91905a2SSylvain Audi       } else if (auto *sym = dyn_cast<Defined>(b)) {
117b91905a2SSylvain Audi         syms.push_back(sym);
118b91905a2SSylvain Audi       }
119b91905a2SSylvain Audi     }
120b91905a2SSylvain Audi 
1216f7483b1SAmy Huang   for (ImportFile *file : ctx.importFileInstances) {
122b91905a2SSylvain Audi     if (!file->live)
123b91905a2SSylvain Audi       continue;
124b91905a2SSylvain Audi 
125912e821aSJacek Caban     if (file->impSym)
126912e821aSJacek Caban       syms.push_back(file->impSym);
127912e821aSJacek Caban     if (file->thunkSym && file->thunkSym->isLive())
12886d2abefSJacek Caban       syms.push_back(file->thunkSym);
129486f790dSJacek Caban     if (file->auxThunkSym && file->auxThunkSym->isLive())
130486f790dSJacek Caban       syms.push_back(file->auxThunkSym);
131486f790dSJacek Caban     if (file->impchkThunk)
132486f790dSJacek Caban       syms.push_back(file->impchkThunk->sym);
133486f790dSJacek Caban     if (file->impECSym)
134486f790dSJacek Caban       syms.push_back(file->impECSym);
135486f790dSJacek Caban     if (file->auxImpCopySym)
136486f790dSJacek Caban       syms.push_back(file->auxImpCopySym);
137b91905a2SSylvain Audi   }
138b91905a2SSylvain Audi 
1395a58b19fSAmy Huang   sortUniqueSymbols(syms, ctx.config.imageBase);
1405a58b19fSAmy Huang   sortUniqueSymbols(staticSyms, ctx.config.imageBase);
1416f24fdb6SPeter Collingbourne }
1426f24fdb6SPeter Collingbourne 
1433969acf8SRui Ueyama // Construct a map from symbols to their stringified representations.
144b91905a2SSylvain Audi static DenseMap<Defined *, std::string>
1456f7483b1SAmy Huang getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
146136d27abSRui Ueyama   std::vector<std::string> str(syms.size());
1477effcbdaSNico Weber   parallelFor((size_t)0, syms.size(), [&](size_t i) {
148136d27abSRui Ueyama     raw_string_ostream os(str[i]);
149b91905a2SSylvain Audi     Defined *sym = syms[i];
150b91905a2SSylvain Audi 
151b91905a2SSylvain Audi     uint16_t sectionIdx = 0;
152b91905a2SSylvain Audi     uint64_t address = 0;
153b91905a2SSylvain Audi     SmallString<128> fileDescr;
154b91905a2SSylvain Audi 
155b91905a2SSylvain Audi     if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
156b91905a2SSylvain Audi       address = absSym->getVA();
157b91905a2SSylvain Audi       fileDescr = "<absolute>";
158b91905a2SSylvain Audi     } else if (isa<DefinedSynthetic>(sym)) {
159b91905a2SSylvain Audi       fileDescr = "<linker-defined>";
160b91905a2SSylvain Audi     } else if (isa<DefinedCommon>(sym)) {
161b91905a2SSylvain Audi       fileDescr = "<common>";
162b91905a2SSylvain Audi     } else if (Chunk *chunk = sym->getChunk()) {
163b91905a2SSylvain Audi       address = sym->getRVA();
1646f7483b1SAmy Huang       if (OutputSection *sec = ctx.getOutputSection(chunk))
165b91905a2SSylvain Audi         address -= sec->header.VirtualAddress;
166b91905a2SSylvain Audi 
167b91905a2SSylvain Audi       sectionIdx = chunk->getOutputSectionIdx();
168b91905a2SSylvain Audi 
169b91905a2SSylvain Audi       InputFile *file;
170b91905a2SSylvain Audi       if (auto *impSym = dyn_cast<DefinedImportData>(sym))
171b91905a2SSylvain Audi         file = impSym->file;
172b91905a2SSylvain Audi       else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
173b91905a2SSylvain Audi         file = thunkSym->wrappedSym->file;
174b91905a2SSylvain Audi       else
175b91905a2SSylvain Audi         file = sym->getFile();
176b91905a2SSylvain Audi 
177b91905a2SSylvain Audi       if (file) {
178b91905a2SSylvain Audi         if (!file->parentName.empty()) {
179b91905a2SSylvain Audi           fileDescr = sys::path::filename(file->parentName);
180b91905a2SSylvain Audi           sys::path::replace_extension(fileDescr, "");
181b91905a2SSylvain Audi           fileDescr += ":";
182b91905a2SSylvain Audi         }
183b91905a2SSylvain Audi         fileDescr += sys::path::filename(file->getName());
184b91905a2SSylvain Audi       }
185b91905a2SSylvain Audi     }
186b91905a2SSylvain Audi     writeHeader(os, sectionIdx, address);
187b91905a2SSylvain Audi     os << "       ";
188b91905a2SSylvain Audi     os << left_justify(sym->getName(), 26);
189b91905a2SSylvain Audi     os << " ";
1905a58b19fSAmy Huang     os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
191b91905a2SSylvain Audi     if (!fileDescr.empty()) {
192b91905a2SSylvain Audi       os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
193b91905a2SSylvain Audi                      // by link.exe in those spaces
194b91905a2SSylvain Audi       os << fileDescr;
195b91905a2SSylvain Audi     }
1963969acf8SRui Ueyama   });
1976f24fdb6SPeter Collingbourne 
198b91905a2SSylvain Audi   DenseMap<Defined *, std::string> ret;
199136d27abSRui Ueyama   for (size_t i = 0, e = syms.size(); i < e; ++i)
200136d27abSRui Ueyama     ret[syms[i]] = std::move(str[i]);
201136d27abSRui Ueyama   return ret;
2026f24fdb6SPeter Collingbourne }
2036f24fdb6SPeter Collingbourne 
2046f7483b1SAmy Huang void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
2055a58b19fSAmy Huang   if (ctx.config.mapFile.empty())
2066f24fdb6SPeter Collingbourne     return;
2076f24fdb6SPeter Collingbourne 
208356139bdSAlexandre Ganea   llvm::TimeTraceScope timeScope("Map file");
209136d27abSRui Ueyama   std::error_code ec;
2105a58b19fSAmy Huang   raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
211136d27abSRui Ueyama   if (ec)
2128b844de3SFangrui Song     Fatal(ctx) << "cannot open " << ctx.config.mapFile << ": " << ec.message();
2133969acf8SRui Ueyama 
2146f7483b1SAmy Huang   ScopedTimer t1(ctx.totalMapTimer);
215b91905a2SSylvain Audi 
2163969acf8SRui Ueyama   // Collect symbol info that we want to print out.
2176f7483b1SAmy Huang   ScopedTimer t2(ctx.symbolGatherTimer);
218b91905a2SSylvain Audi   std::vector<Defined *> syms;
219b91905a2SSylvain Audi   std::vector<Defined *> staticSyms;
2206f7483b1SAmy Huang   getSymbols(ctx, syms, staticSyms);
221b91905a2SSylvain Audi   t2.stop();
2223969acf8SRui Ueyama 
2236f7483b1SAmy Huang   ScopedTimer t3(ctx.symbolStringsTimer);
2246f7483b1SAmy Huang   DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
2256f7483b1SAmy Huang   DenseMap<Defined *, std::string> staticSymStr =
2266f7483b1SAmy Huang       getSymbolStrings(ctx, staticSyms);
227b91905a2SSylvain Audi   t3.stop();
2283969acf8SRui Ueyama 
2296f7483b1SAmy Huang   ScopedTimer t4(ctx.writeTimer);
2305a58b19fSAmy Huang   SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
231b91905a2SSylvain Audi   sys::path::replace_extension(AppName, "");
232b91905a2SSylvain Audi 
233b91905a2SSylvain Audi   // Print out the file header
234b91905a2SSylvain Audi   os << " " << AppName << "\n";
235b91905a2SSylvain Audi   os << "\n";
236b91905a2SSylvain Audi 
2375a58b19fSAmy Huang   os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
2385a58b19fSAmy Huang      << " (";
2395a58b19fSAmy Huang   if (ctx.config.repro) {
240b91905a2SSylvain Audi     os << "Repro mode";
241b91905a2SSylvain Audi   } else {
2425a58b19fSAmy Huang     writeFormattedTimestamp(os, ctx.config.timestamp);
243b91905a2SSylvain Audi   }
244b91905a2SSylvain Audi   os << ")\n";
245b91905a2SSylvain Audi 
246b91905a2SSylvain Audi   os << "\n";
247b91905a2SSylvain Audi   os << " Preferred load address is "
2485a58b19fSAmy Huang      << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
249b91905a2SSylvain Audi   os << "\n";
250b91905a2SSylvain Audi 
251b91905a2SSylvain Audi   // Print out section table.
252b91905a2SSylvain Audi   os << " Start         Length     Name                   Class\n";
253b91905a2SSylvain Audi 
2546f7483b1SAmy Huang   for (OutputSection *sec : ctx.outputSections) {
255b91905a2SSylvain Audi     // Merge display of chunks with same sectionName
256b91905a2SSylvain Audi     std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
257136d27abSRui Ueyama     for (Chunk *c : sec->chunks) {
258136d27abSRui Ueyama       auto *sc = dyn_cast<SectionChunk>(c);
259136d27abSRui Ueyama       if (!sc)
2603969acf8SRui Ueyama         continue;
2613969acf8SRui Ueyama 
262b91905a2SSylvain Audi       if (ChunkRanges.empty() ||
263b91905a2SSylvain Audi           c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
264b91905a2SSylvain Audi         ChunkRanges.emplace_back(sc, sc);
265b91905a2SSylvain Audi       } else {
266b91905a2SSylvain Audi         ChunkRanges.back().second = sc;
267b91905a2SSylvain Audi       }
268b91905a2SSylvain Audi     }
269b91905a2SSylvain Audi 
270b91905a2SSylvain Audi     const bool isCodeSection =
271b91905a2SSylvain Audi         (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
272b91905a2SSylvain Audi         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
273b91905a2SSylvain Audi         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
274b91905a2SSylvain Audi     StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
275b91905a2SSylvain Audi 
276b91905a2SSylvain Audi     for (auto &cr : ChunkRanges) {
277b91905a2SSylvain Audi       size_t size =
278b91905a2SSylvain Audi           cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
279b91905a2SSylvain Audi 
280b91905a2SSylvain Audi       auto address = cr.first->getRVA() - sec->header.VirtualAddress;
281b91905a2SSylvain Audi       writeHeader(os, sec->sectionIndex, address);
282b91905a2SSylvain Audi       os << " " << format_hex_no_prefix(size, 8) << "H";
283b91905a2SSylvain Audi       os << " " << left_justify(cr.first->getSectionName(), 23);
284b91905a2SSylvain Audi       os << " " << SectionClass;
285b91905a2SSylvain Audi       os << '\n';
286b91905a2SSylvain Audi     }
287b91905a2SSylvain Audi   }
288b91905a2SSylvain Audi 
289b91905a2SSylvain Audi   // Print out the symbols table (without static symbols)
290b91905a2SSylvain Audi   os << "\n";
291b91905a2SSylvain Audi   os << "  Address         Publics by Value              Rva+Base"
292b91905a2SSylvain Audi         "               Lib:Object\n";
293b91905a2SSylvain Audi   os << "\n";
294b91905a2SSylvain Audi   for (Defined *sym : syms)
295136d27abSRui Ueyama     os << symStr[sym] << '\n';
296b91905a2SSylvain Audi 
297b91905a2SSylvain Audi   // Print out the entry point.
298b91905a2SSylvain Audi   os << "\n";
299b91905a2SSylvain Audi 
300b91905a2SSylvain Audi   uint16_t entrySecIndex = 0;
301b91905a2SSylvain Audi   uint64_t entryAddress = 0;
302b91905a2SSylvain Audi 
3035a58b19fSAmy Huang   if (!ctx.config.noEntry) {
304d004947aSJacek Caban     Defined *entry = dyn_cast_or_null<Defined>(ctx.symtab.entry);
305b91905a2SSylvain Audi     if (entry) {
306b91905a2SSylvain Audi       Chunk *chunk = entry->getChunk();
307b91905a2SSylvain Audi       entrySecIndex = chunk->getOutputSectionIdx();
308b91905a2SSylvain Audi       entryAddress =
3096f7483b1SAmy Huang           entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
3103969acf8SRui Ueyama     }
3113969acf8SRui Ueyama   }
312b91905a2SSylvain Audi   os << " entry point at         ";
313b91905a2SSylvain Audi   os << format("%04x:%08llx", entrySecIndex, entryAddress);
314b91905a2SSylvain Audi   os << "\n";
315b91905a2SSylvain Audi 
316b91905a2SSylvain Audi   // Print out the static symbols
317b91905a2SSylvain Audi   os << "\n";
318b91905a2SSylvain Audi   os << " Static symbols\n";
319b91905a2SSylvain Audi   os << "\n";
320b91905a2SSylvain Audi   for (Defined *sym : staticSyms)
321b91905a2SSylvain Audi     os << staticSymStr[sym] << '\n';
322b91905a2SSylvain Audi 
3237b317563SPengxuan Zheng   // Print out the exported functions
3245a58b19fSAmy Huang   if (ctx.config.mapInfo) {
3257b317563SPengxuan Zheng     os << "\n";
3267b317563SPengxuan Zheng     os << " Exports\n";
3277b317563SPengxuan Zheng     os << "\n";
3287b317563SPengxuan Zheng     os << "  ordinal    name\n\n";
329*455b3d6dSJacek Caban     for (Export &e : ctx.symtab.exports) {
3307b317563SPengxuan Zheng       os << format("  %7d", e.ordinal) << "    " << e.name << "\n";
3317b317563SPengxuan Zheng       if (!e.extName.empty() && e.extName != e.name)
3327b317563SPengxuan Zheng         os << "               exported name: " << e.extName << "\n";
3337b317563SPengxuan Zheng     }
3347b317563SPengxuan Zheng   }
3357b317563SPengxuan Zheng 
336b91905a2SSylvain Audi   t4.stop();
337b91905a2SSylvain Audi   t1.stop();
3386f24fdb6SPeter Collingbourne }
339