1 //===- MapFile.cpp --------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the /map option in the same format as link.exe
10 // (based on observations)
12 // Header (program name, timestamp info, preferred load address)
14 // Section list (Start = Section index:Base address):
15 // Start Length Name Class
16 // 0001:00001000 00000015H .text CODE
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
23 // entry point at 0001:00000360
27 // 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
28 //===----------------------------------------------------------------------===//
31 #include "COFFLinkerContext.h"
32 #include "SymbolTable.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"
43 using namespace llvm::object
;
45 using namespace lld::coff
;
47 // Print out the first two columns of a line.
48 static void writeHeader(raw_ostream
&os
, uint32_t sec
, uint64_t addr
) {
49 os
<< format(" %04x:%08llx", sec
, addr
);
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.
55 static void writeFormattedTimestamp(raw_ostream
&os
, time_t tds
) {
56 constexpr const char *const days
[7] = {"Sun", "Mon", "Tue", "Wed",
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);
67 static void sortUniqueSymbols(std::vector
<Defined
*> &syms
,
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
);
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
;
82 v
.erase(end
, v
.end());
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
);
93 syms
.resize(v
.size());
94 for (size_t i
= 0, e
= v
.size(); i
< e
; ++i
)
98 // Returns the lists of all symbols that we want to print out.
99 static void getSymbols(const COFFLinkerContext
&ctx
,
100 std::vector
<Defined
*> &syms
,
101 std::vector
<Defined
*> &staticSyms
) {
103 for (ObjFile
*file
: ctx
.objFileInstances
)
104 for (Symbol
*b
: file
->getSymbols()) {
105 if (!b
|| !b
->isLive())
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
);
116 } else if (auto *sym
= dyn_cast
<Defined
>(b
)) {
121 for (ImportFile
*file
: ctx
.importFileInstances
) {
126 syms
.push_back(file
->impSym
);
127 if (file
->thunkSym
&& file
->thunkSym
->isLive())
128 syms
.push_back(file
->thunkSym
);
129 if (file
->auxThunkSym
&& file
->auxThunkSym
->isLive())
130 syms
.push_back(file
->auxThunkSym
);
131 if (file
->impchkThunk
)
132 syms
.push_back(file
->impchkThunk
->sym
);
134 syms
.push_back(file
->impECSym
);
135 if (file
->auxImpCopySym
)
136 syms
.push_back(file
->auxImpCopySym
);
139 sortUniqueSymbols(syms
, ctx
.config
.imageBase
);
140 sortUniqueSymbols(staticSyms
, ctx
.config
.imageBase
);
143 // Construct a map from symbols to their stringified representations.
144 static DenseMap
<Defined
*, std::string
>
145 getSymbolStrings(const COFFLinkerContext
&ctx
, ArrayRef
<Defined
*> syms
) {
146 std::vector
<std::string
> str(syms
.size());
147 parallelFor((size_t)0, syms
.size(), [&](size_t i
) {
148 raw_string_ostream
os(str
[i
]);
149 Defined
*sym
= syms
[i
];
151 uint16_t sectionIdx
= 0;
152 uint64_t address
= 0;
153 SmallString
<128> fileDescr
;
155 if (auto *absSym
= dyn_cast
<DefinedAbsolute
>(sym
)) {
156 address
= absSym
->getVA();
157 fileDescr
= "<absolute>";
158 } else if (isa
<DefinedSynthetic
>(sym
)) {
159 fileDescr
= "<linker-defined>";
160 } else if (isa
<DefinedCommon
>(sym
)) {
161 fileDescr
= "<common>";
162 } else if (Chunk
*chunk
= sym
->getChunk()) {
163 address
= sym
->getRVA();
164 if (OutputSection
*sec
= ctx
.getOutputSection(chunk
))
165 address
-= sec
->header
.VirtualAddress
;
167 sectionIdx
= chunk
->getOutputSectionIdx();
170 if (auto *impSym
= dyn_cast
<DefinedImportData
>(sym
))
172 else if (auto *thunkSym
= dyn_cast
<DefinedImportThunk
>(sym
))
173 file
= thunkSym
->wrappedSym
->file
;
175 file
= sym
->getFile();
178 if (!file
->parentName
.empty()) {
179 fileDescr
= sys::path::filename(file
->parentName
);
180 sys::path::replace_extension(fileDescr
, "");
183 fileDescr
+= sys::path::filename(file
->getName());
186 writeHeader(os
, sectionIdx
, address
);
188 os
<< left_justify(sym
->getName(), 26);
190 os
<< format_hex_no_prefix((ctx
.config
.imageBase
+ sym
->getRVA()), 16);
191 if (!fileDescr
.empty()) {
192 os
<< " "; // FIXME : Handle "f" and "i" flags sometimes generated
193 // by link.exe in those spaces
198 DenseMap
<Defined
*, std::string
> ret
;
199 for (size_t i
= 0, e
= syms
.size(); i
< e
; ++i
)
200 ret
[syms
[i
]] = std::move(str
[i
]);
204 void lld::coff::writeMapFile(COFFLinkerContext
&ctx
) {
205 if (ctx
.config
.mapFile
.empty())
208 llvm::TimeTraceScope
timeScope("Map file");
210 raw_fd_ostream
os(ctx
.config
.mapFile
, ec
, sys::fs::OF_None
);
212 fatal("cannot open " + ctx
.config
.mapFile
+ ": " + ec
.message());
214 ScopedTimer
t1(ctx
.totalMapTimer
);
216 // Collect symbol info that we want to print out.
217 ScopedTimer
t2(ctx
.symbolGatherTimer
);
218 std::vector
<Defined
*> syms
;
219 std::vector
<Defined
*> staticSyms
;
220 getSymbols(ctx
, syms
, staticSyms
);
223 ScopedTimer
t3(ctx
.symbolStringsTimer
);
224 DenseMap
<Defined
*, std::string
> symStr
= getSymbolStrings(ctx
, syms
);
225 DenseMap
<Defined
*, std::string
> staticSymStr
=
226 getSymbolStrings(ctx
, staticSyms
);
229 ScopedTimer
t4(ctx
.writeTimer
);
230 SmallString
<128> AppName
= sys::path::filename(ctx
.config
.outputFile
);
231 sys::path::replace_extension(AppName
, "");
233 // Print out the file header
234 os
<< " " << AppName
<< "\n";
237 os
<< " Timestamp is " << format_hex_no_prefix(ctx
.config
.timestamp
, 8)
239 if (ctx
.config
.repro
) {
242 writeFormattedTimestamp(os
, ctx
.config
.timestamp
);
247 os
<< " Preferred load address is "
248 << format_hex_no_prefix(ctx
.config
.imageBase
, 16) << "\n";
251 // Print out section table.
252 os
<< " Start Length Name Class\n";
254 for (OutputSection
*sec
: ctx
.outputSections
) {
255 // Merge display of chunks with same sectionName
256 std::vector
<std::pair
<SectionChunk
*, SectionChunk
*>> ChunkRanges
;
257 for (Chunk
*c
: sec
->chunks
) {
258 auto *sc
= dyn_cast
<SectionChunk
>(c
);
262 if (ChunkRanges
.empty() ||
263 c
->getSectionName() != ChunkRanges
.back().first
->getSectionName()) {
264 ChunkRanges
.emplace_back(sc
, sc
);
266 ChunkRanges
.back().second
= sc
;
270 const bool isCodeSection
=
271 (sec
->header
.Characteristics
& COFF::IMAGE_SCN_CNT_CODE
) &&
272 (sec
->header
.Characteristics
& COFF::IMAGE_SCN_MEM_READ
) &&
273 (sec
->header
.Characteristics
& COFF::IMAGE_SCN_MEM_EXECUTE
);
274 StringRef SectionClass
= (isCodeSection
? "CODE" : "DATA");
276 for (auto &cr
: ChunkRanges
) {
278 cr
.second
->getRVA() + cr
.second
->getSize() - cr
.first
->getRVA();
280 auto address
= cr
.first
->getRVA() - sec
->header
.VirtualAddress
;
281 writeHeader(os
, sec
->sectionIndex
, address
);
282 os
<< " " << format_hex_no_prefix(size
, 8) << "H";
283 os
<< " " << left_justify(cr
.first
->getSectionName(), 23);
284 os
<< " " << SectionClass
;
289 // Print out the symbols table (without static symbols)
291 os
<< " Address Publics by Value Rva+Base"
294 for (Defined
*sym
: syms
)
295 os
<< symStr
[sym
] << '\n';
297 // Print out the entry point.
300 uint16_t entrySecIndex
= 0;
301 uint64_t entryAddress
= 0;
303 if (!ctx
.config
.noEntry
) {
304 Defined
*entry
= dyn_cast_or_null
<Defined
>(ctx
.config
.entry
);
306 Chunk
*chunk
= entry
->getChunk();
307 entrySecIndex
= chunk
->getOutputSectionIdx();
309 entry
->getRVA() - ctx
.getOutputSection(chunk
)->header
.VirtualAddress
;
312 os
<< " entry point at ";
313 os
<< format("%04x:%08llx", entrySecIndex
, entryAddress
);
316 // Print out the static symbols
318 os
<< " Static symbols\n";
320 for (Defined
*sym
: staticSyms
)
321 os
<< staticSymStr
[sym
] << '\n';
323 // Print out the exported functions
324 if (ctx
.config
.mapInfo
) {
328 os
<< " ordinal name\n\n";
329 for (Export
&e
: ctx
.config
.exports
) {
330 os
<< format(" %7d", e
.ordinal
) << " " << e
.name
<< "\n";
331 if (!e
.extName
.empty() && e
.extName
!= e
.name
)
332 os
<< " exported name: " << e
.extName
<< "\n";