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