1275970Scy//===- MapFile.cpp --------------------------------------------------------===//
2275970Scy//
3275970Scy// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4275970Scy// See https://llvm.org/LICENSE.txt for license information.
5275970Scy// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6275970Scy//
7275970Scy//===----------------------------------------------------------------------===//
8275970Scy//
9275970Scy// This file implements the /map option in the same format as link.exe
10275970Scy// (based on observations)
11275970Scy//
12275970Scy// Header (program name, timestamp info, preferred load address)
13275970Scy//
14275970Scy// Section list (Start = Section index:Base address):
15275970Scy// Start         Length     Name                   Class
16275970Scy// 0001:00001000 00000015H .text                   CODE
17275970Scy//
18275970Scy// Symbols list:
19275970Scy// Address        Publics by Value    Rva + Base          Lib:Object
20275970Scy// 0001:00001000  main                 0000000140001000    main.obj
21275970Scy// 0001:00001300  ?__scrt_common_main@@YAHXZ  0000000140001300 libcmt:exe_main.obj
22275970Scy//
23275970Scy// entry point at        0001:00000360
24275970Scy//
25275970Scy// Static symbols
26275970Scy//
27275970Scy// 0000:00000000  __guard_fids__       0000000140000000     libcmt : exe_main.obj
28275970Scy//===----------------------------------------------------------------------===//
29275970Scy
30275970Scy#include "MapFile.h"
31275970Scy#include "COFFLinkerContext.h"
32275970Scy#include "SymbolTable.h"
33275970Scy#include "Symbols.h"
34275970Scy#include "Writer.h"
35275970Scy#include "lld/Common/ErrorHandler.h"
36275970Scy#include "lld/Common/Timer.h"
37275970Scy#include "llvm/Support/Parallel.h"
38275970Scy#include "llvm/Support/Path.h"
39275970Scy#include "llvm/Support/raw_ostream.h"
40275970Scy
41275970Scyusing namespace llvm;
42275970Scyusing namespace llvm::object;
43275970Scyusing namespace lld;
44275970Scyusing namespace lld::coff;
45275970Scy
46275970Scy// Print out the first two columns of a line.
47275970Scystatic void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
48275970Scy  os << format(" %04x:%08llx", sec, addr);
49275970Scy}
50275970Scy
51275970Scy// Write the time stamp with the format used by link.exe
52275970Scy// It seems identical to strftime with "%c" on msvc build, but we need a
53275970Scy// locale-agnostic version.
54275970Scystatic void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
55275970Scy  constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
56275970Scy                                         "Thu", "Fri", "Sat"};
57275970Scy  constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
58275970Scy                                            "May", "Jun", "Jul", "Aug",
59275970Scy                                            "Sep", "Oct", "Nov", "Dec"};
60275970Scy  tm *time = localtime(&tds);
61275970Scy  os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
62282408Scy               months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
63282408Scy               time->tm_sec, time->tm_year + 1900);
64282408Scy}
65282408Scy
66275970Scystatic void sortUniqueSymbols(std::vector<Defined *> &syms,
67275970Scy                              uint64_t imageBase) {
68275970Scy  // Build helper vector
69275970Scy  using SortEntry = std::pair<Defined *, size_t>;
70275970Scy  std::vector<SortEntry> v;
71275970Scy  v.resize(syms.size());
72275970Scy  for (size_t i = 0, e = syms.size(); i < e; ++i)
73275970Scy    v[i] = SortEntry(syms[i], i);
74275970Scy
75275970Scy  // Remove duplicate symbol pointers
76275970Scy  parallelSort(v, std::less<SortEntry>());
77275970Scy  auto end = std::unique(v.begin(), v.end(),
78275970Scy                         [](const SortEntry &a, const SortEntry &b) {
79275970Scy                           return a.first == b.first;
80275970Scy                         });
81275970Scy  v.erase(end, v.end());
82275970Scy
83275970Scy  // Sort by RVA then original order
84275970Scy  parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
85275970Scy    // Add config.imageBase to avoid comparing "negative" RVAs.
86275970Scy    // This can happen with symbols of Absolute kind
87275970Scy    uint64_t rvaa = imageBase + a.first->getRVA();
88275970Scy    uint64_t rvab = imageBase + b.first->getRVA();
89275970Scy    return rvaa < rvab || (rvaa == rvab && a.second < b.second);
90275970Scy  });
91275970Scy
92275970Scy  syms.resize(v.size());
93275970Scy  for (size_t i = 0, e = v.size(); i < e; ++i)
94275970Scy    syms[i] = v[i].first;
95275970Scy}
96275970Scy
97275970Scy// Returns the lists of all symbols that we want to print out.
98275970Scystatic void getSymbols(const COFFLinkerContext &ctx,
99275970Scy                       std::vector<Defined *> &syms,
100275970Scy                       std::vector<Defined *> &staticSyms) {
101275970Scy
102275970Scy  for (ObjFile *file : ctx.objFileInstances)
103275970Scy    for (Symbol *b : file->getSymbols()) {
104275970Scy      if (!b || !b->isLive())
105275970Scy        continue;
106275970Scy      if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
107275970Scy        COFFSymbolRef symRef = sym->getCOFFSymbol();
108275970Scy        if (!symRef.isSectionDefinition() &&
109275970Scy            symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
110275970Scy          if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
111275970Scy            staticSyms.push_back(sym);
112275970Scy          else
113275970Scy            syms.push_back(sym);
114275970Scy        }
115275970Scy      } else if (auto *sym = dyn_cast<Defined>(b)) {
116275970Scy        syms.push_back(sym);
117275970Scy      }
118275970Scy    }
119275970Scy
120275970Scy  for (ImportFile *file : ctx.importFileInstances) {
121275970Scy    if (!file->live)
122275970Scy      continue;
123275970Scy
124275970Scy    if (!file->thunkSym)
125275970Scy      continue;
126275970Scy
127275970Scy    if (!file->thunkLive)
128275970Scy      continue;
129275970Scy
130275970Scy    if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
131275970Scy      syms.push_back(thunkSym);
132275970Scy
133275970Scy    if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
134275970Scy      syms.push_back(impSym);
135275970Scy  }
136280849Scy
137275970Scy  sortUniqueSymbols(syms, ctx.config.imageBase);
138275970Scy  sortUniqueSymbols(staticSyms, ctx.config.imageBase);
139275970Scy}
140275970Scy
141275970Scy// Construct a map from symbols to their stringified representations.
142275970Scystatic DenseMap<Defined *, std::string>
143275970ScygetSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
144275970Scy  std::vector<std::string> str(syms.size());
145275970Scy  parallelFor((size_t)0, syms.size(), [&](size_t i) {
146275970Scy    raw_string_ostream os(str[i]);
147275970Scy    Defined *sym = syms[i];
148275970Scy
149275970Scy    uint16_t sectionIdx = 0;
150275970Scy    uint64_t address = 0;
151275970Scy    SmallString<128> fileDescr;
152275970Scy
153275970Scy    if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
154275970Scy      address = absSym->getVA();
155275970Scy      fileDescr = "<absolute>";
156275970Scy    } else if (isa<DefinedSynthetic>(sym)) {
157275970Scy      fileDescr = "<linker-defined>";
158275970Scy    } else if (isa<DefinedCommon>(sym)) {
159275970Scy      fileDescr = "<common>";
160275970Scy    } else if (Chunk *chunk = sym->getChunk()) {
161275970Scy      address = sym->getRVA();
162275970Scy      if (OutputSection *sec = ctx.getOutputSection(chunk))
163275970Scy        address -= sec->header.VirtualAddress;
164275970Scy
165275970Scy      sectionIdx = chunk->getOutputSectionIdx();
166275970Scy
167275970Scy      InputFile *file;
168275970Scy      if (auto *impSym = dyn_cast<DefinedImportData>(sym))
169275970Scy        file = impSym->file;
170275970Scy      else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
171275970Scy        file = thunkSym->wrappedSym->file;
172275970Scy      else
173275970Scy        file = sym->getFile();
174275970Scy
175275970Scy      if (file) {
176275970Scy        if (!file->parentName.empty()) {
177275970Scy          fileDescr = sys::path::filename(file->parentName);
178275970Scy          sys::path::replace_extension(fileDescr, "");
179275970Scy          fileDescr += ":";
180275970Scy        }
181275970Scy        fileDescr += sys::path::filename(file->getName());
182275970Scy      }
183275970Scy    }
184275970Scy    writeHeader(os, sectionIdx, address);
185275970Scy    os << "       ";
186275970Scy    os << left_justify(sym->getName(), 26);
187275970Scy    os << " ";
188275970Scy    os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
189275970Scy    if (!fileDescr.empty()) {
190275970Scy      os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
191275970Scy                     // by link.exe in those spaces
192275970Scy      os << fileDescr;
193275970Scy    }
194275970Scy  });
195275970Scy
196275970Scy  DenseMap<Defined *, std::string> ret;
197275970Scy  for (size_t i = 0, e = syms.size(); i < e; ++i)
198275970Scy    ret[syms[i]] = std::move(str[i]);
199275970Scy  return ret;
200275970Scy}
201275970Scy
202275970Scyvoid lld::coff::writeMapFile(COFFLinkerContext &ctx) {
203275970Scy  if (ctx.config.mapFile.empty())
204275970Scy    return;
205275970Scy
206275970Scy  std::error_code ec;
207275970Scy  raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
208275970Scy  if (ec)
209275970Scy    fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
210275970Scy
211275970Scy  ScopedTimer t1(ctx.totalMapTimer);
212275970Scy
213275970Scy  // Collect symbol info that we want to print out.
214275970Scy  ScopedTimer t2(ctx.symbolGatherTimer);
215275970Scy  std::vector<Defined *> syms;
216275970Scy  std::vector<Defined *> staticSyms;
217275970Scy  getSymbols(ctx, syms, staticSyms);
218275970Scy  t2.stop();
219275970Scy
220275970Scy  ScopedTimer t3(ctx.symbolStringsTimer);
221275970Scy  DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
222275970Scy  DenseMap<Defined *, std::string> staticSymStr =
223275970Scy      getSymbolStrings(ctx, staticSyms);
224275970Scy  t3.stop();
225275970Scy
226275970Scy  ScopedTimer t4(ctx.writeTimer);
227275970Scy  SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
228275970Scy  sys::path::replace_extension(AppName, "");
229275970Scy
230275970Scy  // Print out the file header
231275970Scy  os << " " << AppName << "\n";
232275970Scy  os << "\n";
233275970Scy
234275970Scy  os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
235275970Scy     << " (";
236275970Scy  if (ctx.config.repro) {
237275970Scy    os << "Repro mode";
238275970Scy  } else {
239275970Scy    writeFormattedTimestamp(os, ctx.config.timestamp);
240275970Scy  }
241275970Scy  os << ")\n";
242275970Scy
243275970Scy  os << "\n";
244275970Scy  os << " Preferred load address is "
245275970Scy     << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
246275970Scy  os << "\n";
247275970Scy
248275970Scy  // Print out section table.
249275970Scy  os << " Start         Length     Name                   Class\n";
250275970Scy
251275970Scy  for (OutputSection *sec : ctx.outputSections) {
252282408Scy    // Merge display of chunks with same sectionName
253282408Scy    std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
254282408Scy    for (Chunk *c : sec->chunks) {
255282408Scy      auto *sc = dyn_cast<SectionChunk>(c);
256282408Scy      if (!sc)
257275970Scy        continue;
258275970Scy
259275970Scy      if (ChunkRanges.empty() ||
260275970Scy          c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
261275970Scy        ChunkRanges.emplace_back(sc, sc);
262275970Scy      } else {
263275970Scy        ChunkRanges.back().second = sc;
264275970Scy      }
265275970Scy    }
266275970Scy
267275970Scy    const bool isCodeSection =
268275970Scy        (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
269275970Scy        (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
270275970Scy        (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
271275970Scy    StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
272275970Scy
273275970Scy    for (auto &cr : ChunkRanges) {
274275970Scy      size_t size =
275275970Scy          cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
276275970Scy
277275970Scy      auto address = cr.first->getRVA() - sec->header.VirtualAddress;
278275970Scy      writeHeader(os, sec->sectionIndex, address);
279275970Scy      os << " " << format_hex_no_prefix(size, 8) << "H";
280275970Scy      os << " " << left_justify(cr.first->getSectionName(), 23);
281275970Scy      os << " " << SectionClass;
282275970Scy      os << '\n';
283275970Scy    }
284275970Scy  }
285275970Scy
286275970Scy  // Print out the symbols table (without static symbols)
287275970Scy  os << "\n";
288275970Scy  os << "  Address         Publics by Value              Rva+Base"
289275970Scy        "               Lib:Object\n";
290275970Scy  os << "\n";
291275970Scy  for (Defined *sym : syms)
292275970Scy    os << symStr[sym] << '\n';
293275970Scy
294275970Scy  // Print out the entry point.
295275970Scy  os << "\n";
296275970Scy
297275970Scy  uint16_t entrySecIndex = 0;
298275970Scy  uint64_t entryAddress = 0;
299275970Scy
300275970Scy  if (!ctx.config.noEntry) {
301275970Scy    Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
302275970Scy    if (entry) {
303275970Scy      Chunk *chunk = entry->getChunk();
304275970Scy      entrySecIndex = chunk->getOutputSectionIdx();
305275970Scy      entryAddress =
306275970Scy          entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
307275970Scy    }
308275970Scy  }
309275970Scy  os << " entry point at         ";
310275970Scy  os << format("%04x:%08llx", entrySecIndex, entryAddress);
311275970Scy  os << "\n";
312275970Scy
313275970Scy  // Print out the static symbols
314275970Scy  os << "\n";
315275970Scy  os << " Static symbols\n";
316275970Scy  os << "\n";
317275970Scy  for (Defined *sym : staticSyms)
318275970Scy    os << staticSymStr[sym] << '\n';
319275970Scy
320275970Scy  // Print out the exported functions
321275970Scy  if (ctx.config.mapInfo) {
322275970Scy    os << "\n";
323275970Scy    os << " Exports\n";
324275970Scy    os << "\n";
325275970Scy    os << "  ordinal    name\n\n";
326275970Scy    for (Export &e : ctx.config.exports) {
327275970Scy      os << format("  %7d", e.ordinal) << "    " << e.name << "\n";
328275970Scy      if (!e.extName.empty() && e.extName != e.name)
329275970Scy        os << "               exported name: " << e.extName << "\n";
330275970Scy    }
331275970Scy  }
332275970Scy
333275970Scy  t4.stop();
334275970Scy  t1.stop();
335275970Scy}
336275970Scy