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