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/ErrorHandler.h"
36#include "lld/Common/Timer.h"
37#include "llvm/Support/Parallel.h"
38#include "llvm/Support/Path.h"
39#include "llvm/Support/TimeProfiler.h"
40#include "llvm/Support/raw_ostream.h"
41
42using namespace llvm;
43using namespace llvm::object;
44using namespace lld;
45using namespace lld::coff;
46
47// Print out the first two columns of a line.
48static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
49  os << format(" %04x:%08llx", sec, addr);
50}
51
52// Write the time stamp with the format used by link.exe
53// It seems identical to strftime with "%c" on msvc build, but we need a
54// locale-agnostic version.
55static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
56  constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
57                                         "Thu", "Fri", "Sat"};
58  constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
59                                            "May", "Jun", "Jul", "Aug",
60                                            "Sep", "Oct", "Nov", "Dec"};
61  tm *time = localtime(&tds);
62  os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
63               months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
64               time->tm_sec, time->tm_year + 1900);
65}
66
67static void sortUniqueSymbols(std::vector<Defined *> &syms,
68                              uint64_t imageBase) {
69  // Build helper vector
70  using SortEntry = std::pair<Defined *, size_t>;
71  std::vector<SortEntry> v;
72  v.resize(syms.size());
73  for (size_t i = 0, e = syms.size(); i < e; ++i)
74    v[i] = SortEntry(syms[i], i);
75
76  // Remove duplicate symbol pointers
77  parallelSort(v, std::less<SortEntry>());
78  auto end = std::unique(v.begin(), v.end(),
79                         [](const SortEntry &a, const SortEntry &b) {
80                           return a.first == b.first;
81                         });
82  v.erase(end, v.end());
83
84  // Sort by RVA then original order
85  parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
86    // Add config.imageBase to avoid comparing "negative" RVAs.
87    // This can happen with symbols of Absolute kind
88    uint64_t rvaa = imageBase + a.first->getRVA();
89    uint64_t rvab = imageBase + b.first->getRVA();
90    return rvaa < rvab || (rvaa == rvab && a.second < b.second);
91  });
92
93  syms.resize(v.size());
94  for (size_t i = 0, e = v.size(); i < e; ++i)
95    syms[i] = v[i].first;
96}
97
98// Returns the lists of all symbols that we want to print out.
99static void getSymbols(const COFFLinkerContext &ctx,
100                       std::vector<Defined *> &syms,
101                       std::vector<Defined *> &staticSyms) {
102
103  for (ObjFile *file : ctx.objFileInstances)
104    for (Symbol *b : file->getSymbols()) {
105      if (!b || !b->isLive())
106        continue;
107      if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
108        COFFSymbolRef symRef = sym->getCOFFSymbol();
109        if (!symRef.isSectionDefinition() &&
110            symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
111          if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
112            staticSyms.push_back(sym);
113          else
114            syms.push_back(sym);
115        }
116      } else if (auto *sym = dyn_cast<Defined>(b)) {
117        syms.push_back(sym);
118      }
119    }
120
121  for (ImportFile *file : ctx.importFileInstances) {
122    if (!file->live)
123      continue;
124
125    if (!file->thunkSym)
126      continue;
127
128    if (!file->thunkLive)
129      continue;
130
131    if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
132      syms.push_back(thunkSym);
133
134    if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
135      syms.push_back(impSym);
136  }
137
138  sortUniqueSymbols(syms, ctx.config.imageBase);
139  sortUniqueSymbols(staticSyms, ctx.config.imageBase);
140}
141
142// Construct a map from symbols to their stringified representations.
143static DenseMap<Defined *, std::string>
144getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
145  std::vector<std::string> str(syms.size());
146  parallelFor((size_t)0, syms.size(), [&](size_t i) {
147    raw_string_ostream os(str[i]);
148    Defined *sym = syms[i];
149
150    uint16_t sectionIdx = 0;
151    uint64_t address = 0;
152    SmallString<128> fileDescr;
153
154    if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
155      address = absSym->getVA();
156      fileDescr = "<absolute>";
157    } else if (isa<DefinedSynthetic>(sym)) {
158      fileDescr = "<linker-defined>";
159    } else if (isa<DefinedCommon>(sym)) {
160      fileDescr = "<common>";
161    } else if (Chunk *chunk = sym->getChunk()) {
162      address = sym->getRVA();
163      if (OutputSection *sec = ctx.getOutputSection(chunk))
164        address -= sec->header.VirtualAddress;
165
166      sectionIdx = chunk->getOutputSectionIdx();
167
168      InputFile *file;
169      if (auto *impSym = dyn_cast<DefinedImportData>(sym))
170        file = impSym->file;
171      else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
172        file = thunkSym->wrappedSym->file;
173      else
174        file = sym->getFile();
175
176      if (file) {
177        if (!file->parentName.empty()) {
178          fileDescr = sys::path::filename(file->parentName);
179          sys::path::replace_extension(fileDescr, "");
180          fileDescr += ":";
181        }
182        fileDescr += sys::path::filename(file->getName());
183      }
184    }
185    writeHeader(os, sectionIdx, address);
186    os << "       ";
187    os << left_justify(sym->getName(), 26);
188    os << " ";
189    os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
190    if (!fileDescr.empty()) {
191      os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
192                     // by link.exe in those spaces
193      os << fileDescr;
194    }
195  });
196
197  DenseMap<Defined *, std::string> ret;
198  for (size_t i = 0, e = syms.size(); i < e; ++i)
199    ret[syms[i]] = std::move(str[i]);
200  return ret;
201}
202
203void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
204  if (ctx.config.mapFile.empty())
205    return;
206
207  llvm::TimeTraceScope timeScope("Map file");
208  std::error_code ec;
209  raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
210  if (ec)
211    fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
212
213  ScopedTimer t1(ctx.totalMapTimer);
214
215  // Collect symbol info that we want to print out.
216  ScopedTimer t2(ctx.symbolGatherTimer);
217  std::vector<Defined *> syms;
218  std::vector<Defined *> staticSyms;
219  getSymbols(ctx, syms, staticSyms);
220  t2.stop();
221
222  ScopedTimer t3(ctx.symbolStringsTimer);
223  DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
224  DenseMap<Defined *, std::string> staticSymStr =
225      getSymbolStrings(ctx, staticSyms);
226  t3.stop();
227
228  ScopedTimer t4(ctx.writeTimer);
229  SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
230  sys::path::replace_extension(AppName, "");
231
232  // Print out the file header
233  os << " " << AppName << "\n";
234  os << "\n";
235
236  os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
237     << " (";
238  if (ctx.config.repro) {
239    os << "Repro mode";
240  } else {
241    writeFormattedTimestamp(os, ctx.config.timestamp);
242  }
243  os << ")\n";
244
245  os << "\n";
246  os << " Preferred load address is "
247     << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
248  os << "\n";
249
250  // Print out section table.
251  os << " Start         Length     Name                   Class\n";
252
253  for (OutputSection *sec : ctx.outputSections) {
254    // Merge display of chunks with same sectionName
255    std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
256    for (Chunk *c : sec->chunks) {
257      auto *sc = dyn_cast<SectionChunk>(c);
258      if (!sc)
259        continue;
260
261      if (ChunkRanges.empty() ||
262          c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
263        ChunkRanges.emplace_back(sc, sc);
264      } else {
265        ChunkRanges.back().second = sc;
266      }
267    }
268
269    const bool isCodeSection =
270        (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
271        (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
272        (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
273    StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
274
275    for (auto &cr : ChunkRanges) {
276      size_t size =
277          cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
278
279      auto address = cr.first->getRVA() - sec->header.VirtualAddress;
280      writeHeader(os, sec->sectionIndex, address);
281      os << " " << format_hex_no_prefix(size, 8) << "H";
282      os << " " << left_justify(cr.first->getSectionName(), 23);
283      os << " " << SectionClass;
284      os << '\n';
285    }
286  }
287
288  // Print out the symbols table (without static symbols)
289  os << "\n";
290  os << "  Address         Publics by Value              Rva+Base"
291        "               Lib:Object\n";
292  os << "\n";
293  for (Defined *sym : syms)
294    os << symStr[sym] << '\n';
295
296  // Print out the entry point.
297  os << "\n";
298
299  uint16_t entrySecIndex = 0;
300  uint64_t entryAddress = 0;
301
302  if (!ctx.config.noEntry) {
303    Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
304    if (entry) {
305      Chunk *chunk = entry->getChunk();
306      entrySecIndex = chunk->getOutputSectionIdx();
307      entryAddress =
308          entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
309    }
310  }
311  os << " entry point at         ";
312  os << format("%04x:%08llx", entrySecIndex, entryAddress);
313  os << "\n";
314
315  // Print out the static symbols
316  os << "\n";
317  os << " Static symbols\n";
318  os << "\n";
319  for (Defined *sym : staticSyms)
320    os << staticSymStr[sym] << '\n';
321
322  // Print out the exported functions
323  if (ctx.config.mapInfo) {
324    os << "\n";
325    os << " Exports\n";
326    os << "\n";
327    os << "  ordinal    name\n\n";
328    for (Export &e : ctx.config.exports) {
329      os << format("  %7d", e.ordinal) << "    " << e.name << "\n";
330      if (!e.extName.empty() && e.extName != e.name)
331        os << "               exported name: " << e.extName << "\n";
332    }
333  }
334
335  t4.stop();
336  t1.stop();
337}
338