6
6
//
7
7
// ===----------------------------------------------------------------------===//
8
8
//
9
- // This file implements the -map option, which maps address ranges to their
10
- // respective contents, plus the input file these contents were originally from.
11
- // The contents (typically symbols) are listed in address order. Dead-stripped
12
- // contents are included as well.
9
+ // This file implements the -map option. It shows lists in order and
10
+ // hierarchically the outputFile, arch, input files, output sections and
11
+ // symbols:
13
12
//
14
13
// # Path: test
15
14
// # Arch: x86_84
29
28
// ===----------------------------------------------------------------------===//
30
29
31
30
#include " MapFile.h"
32
- #include " ConcatOutputSection.h"
33
31
#include " Config.h"
34
32
#include " InputFiles.h"
35
33
#include " InputSection.h"
34
+ #include " OutputSection.h"
36
35
#include " OutputSegment.h"
37
36
#include " Symbols.h"
38
37
#include " SyntheticSections.h"
39
38
#include " Target.h"
40
39
#include " lld/Common/ErrorHandler.h"
41
- #include " llvm/ADT/DenseMap.h"
42
40
#include " llvm/Support/Parallel.h"
43
41
#include " llvm/Support/TimeProfiler.h"
44
42
@@ -47,77 +45,71 @@ using namespace llvm::sys;
47
45
using namespace lld ;
48
46
using namespace lld ::macho;
49
47
50
- struct CStringInfo {
51
- uint32_t fileIndex;
52
- StringRef str;
53
- };
54
-
55
48
struct MapInfo {
56
49
SmallVector<InputFile *> files;
50
+ SmallVector<Defined *> liveSymbols;
57
51
SmallVector<Defined *> deadSymbols;
58
- DenseMap<const OutputSection *,
59
- SmallVector<std::pair<uint64_t /* addr*/ , CStringInfo>>>
60
- liveCStringsForSection;
61
- SmallVector<CStringInfo> deadCStrings;
62
52
};
63
53
64
54
static MapInfo gatherMapInfo () {
65
55
MapInfo info;
66
56
for (InputFile *file : inputFiles)
67
57
if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) {
68
- uint32_t fileIndex = info.files .size () + 1 ;
69
- bool isReferencedFile = false ;
70
-
71
- // Gather the dead symbols. We don't have to bother with the live ones
72
- // because we will pick them up as we iterate over the OutputSections
73
- // later.
58
+ bool hasEmittedSymbol = false ;
74
59
for (Symbol *sym : file->symbols ) {
75
60
if (auto *d = dyn_cast_or_null<Defined>(sym))
76
- // Only emit the prevailing definition of a symbol. Also, don't emit
77
- // the symbol if it is part of a cstring section (we use the literal
78
- // value instead, similar to ld64)
79
- if (d->isec && d->getFile () == file &&
80
- !isa<CStringInputSection>(d->isec )) {
81
- isReferencedFile = true ;
82
- if (!d->isLive ())
61
+ if (d->isec && d->getFile () == file) {
62
+ if (d->isLive ()) {
63
+ assert (!shouldOmitFromOutput (d->isec ));
64
+ info.liveSymbols .push_back (d);
65
+ } else {
83
66
info.deadSymbols .push_back (d);
84
- }
85
- }
86
-
87
- // Gather all the cstrings (both live and dead). A CString(Output)Section
88
- // doesn't provide us a way of figuring out which InputSections its
89
- // cstring contents came from, so we need to build up that mapping here.
90
- for (const Section *sec : file->sections ) {
91
- for (const Subsection &subsec : sec->subsections ) {
92
- if (auto isec = dyn_cast<CStringInputSection>(subsec.isec )) {
93
- auto &liveCStrings = info.liveCStringsForSection [isec->parent ];
94
- for (const auto &[i, piece] : llvm::enumerate (isec->pieces )) {
95
- if (piece.live )
96
- liveCStrings.push_back ({isec->parent ->addr + piece.outSecOff ,
97
- {fileIndex, isec->getStringRef (i)}});
98
- else
99
- info.deadCStrings .push_back ({fileIndex, isec->getStringRef (i)});
100
- isReferencedFile = true ;
101
67
}
102
- } else {
103
- break ;
68
+ hasEmittedSymbol = true ;
104
69
}
105
- }
106
70
}
107
-
108
- if (isReferencedFile)
71
+ if (hasEmittedSymbol)
109
72
info.files .push_back (file);
110
73
}
111
-
112
- // cstrings are not stored in sorted order in their OutputSections, so we sort
113
- // them here.
114
- for (auto &liveCStrings : info.liveCStringsForSection )
115
- parallelSort (liveCStrings.second , [](const auto &p1, const auto &p2) {
116
- return p1.first < p2.first ;
117
- });
74
+ parallelSort (info.liveSymbols .begin (), info.liveSymbols .end (),
75
+ [](Defined *a, Defined *b) { return a->getVA () < b->getVA (); });
118
76
return info;
119
77
}
120
78
79
+ // Construct a map from symbols to their stringified representations.
80
+ // Demangling symbols (which is what toString() does) is slow, so
81
+ // we do that in batch using parallel-for.
82
+ static DenseMap<Symbol *, std::string>
83
+ getSymbolStrings (ArrayRef<Defined *> syms) {
84
+ std::vector<std::string> str (syms.size ());
85
+ parallelFor (0 , syms.size (), [&](size_t i) {
86
+ raw_string_ostream os (str[i]);
87
+ Defined *sym = syms[i];
88
+
89
+ switch (sym->isec ->kind ()) {
90
+ case InputSection::CStringLiteralKind: {
91
+ // Output "literal string: <string literal>"
92
+ const auto *isec = cast<CStringInputSection>(sym->isec );
93
+ const StringPiece &piece = isec->getStringPiece (sym->value );
94
+ assert (
95
+ sym->value == piece.inSecOff &&
96
+ " We expect symbols to always point to the start of a StringPiece." );
97
+ StringRef str = isec->getStringRef (&piece - &(*isec->pieces .begin ()));
98
+ (os << " literal string: " ).write_escaped (str);
99
+ break ;
100
+ }
101
+ case InputSection::ConcatKind:
102
+ case InputSection::WordLiteralKind:
103
+ os << toString (*sym);
104
+ }
105
+ });
106
+
107
+ DenseMap<Symbol *, std::string> ret;
108
+ for (size_t i = 0 , e = syms.size (); i < e; ++i)
109
+ ret[syms[i]] = std::move (str[i]);
110
+ return ret;
111
+ }
112
+
121
113
void macho::writeMapFile () {
122
114
if (config->mapFile .empty ())
123
115
return ;
@@ -132,12 +124,16 @@ void macho::writeMapFile() {
132
124
return ;
133
125
}
134
126
127
+ // Dump output path.
135
128
os << format (" # Path: %s\n " , config->outputFile .str ().c_str ());
129
+
130
+ // Dump output architecture.
136
131
os << format (" # Arch: %s\n " ,
137
132
getArchitectureName (config->arch ()).str ().c_str ());
138
133
139
134
MapInfo info = gatherMapInfo ();
140
135
136
+ // Dump table of object files.
141
137
os << " # Object files:\n " ;
142
138
os << format (" [%3u] %s\n " , 0 , (const char *)" linker synthesized" );
143
139
uint32_t fileIndex = 1 ;
@@ -147,6 +143,7 @@ void macho::writeMapFile() {
147
143
readerToFileOrdinal[file] = fileIndex++;
148
144
}
149
145
146
+ // Dump table of sections
150
147
os << " # Sections:\n " ;
151
148
os << " # Address\t Size \t Segment\t Section\n " ;
152
149
for (OutputSegment *seg : outputSegments)
@@ -158,48 +155,28 @@ void macho::writeMapFile() {
158
155
seg->name .str ().c_str (), osec->name .str ().c_str ());
159
156
}
160
157
158
+ // Dump table of symbols
159
+ DenseMap<Symbol *, std::string> liveSymbolStrings =
160
+ getSymbolStrings (info.liveSymbols );
161
161
os << " # Symbols:\n " ;
162
162
os << " # Address\t Size \t File Name\n " ;
163
- for (const OutputSegment *seg : outputSegments) {
164
- for (const OutputSection *osec : seg->getSections ()) {
165
- if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {
166
- for (const InputSection *isec : concatOsec->inputs ) {
167
- for (Defined *sym : isec->symbols )
168
- os << format (" 0x%08llX\t 0x%08llX\t [%3u] %s\n " , sym->getVA (),
169
- sym->size , readerToFileOrdinal[sym->getFile ()],
170
- sym->getName ().str ().data ());
171
- }
172
- } else if (osec == in.cStringSection || osec == in.objcMethnameSection ) {
173
- const auto &liveCStrings = info.liveCStringsForSection .lookup (osec);
174
- uint64_t lastAddr = 0 ; // strings will never start at address 0, so this
175
- // is a sentinel value
176
- for (const auto &[addr, info] : liveCStrings) {
177
- uint64_t size = 0 ;
178
- if (addr != lastAddr)
179
- size = info.str .size () + 1 ; // include null terminator
180
- lastAddr = addr;
181
- os << format (" 0x%08llX\t 0x%08llX\t [%3u] literal string: " , addr, size,
182
- info.fileIndex );
183
- os.write_escaped (info.str ) << " \n " ;
184
- }
185
- }
186
- // TODO print other synthetic sections
187
- }
163
+ for (Defined *sym : info.liveSymbols ) {
164
+ assert (sym->isLive ());
165
+ os << format (" 0x%08llX\t 0x%08llX\t [%3u] %s\n " , sym->getVA (), sym->size ,
166
+ readerToFileOrdinal[sym->getFile ()],
167
+ liveSymbolStrings[sym].c_str ());
188
168
}
189
169
190
170
if (config->deadStrip ) {
171
+ DenseMap<Symbol *, std::string> deadSymbolStrings =
172
+ getSymbolStrings (info.deadSymbols );
191
173
os << " # Dead Stripped Symbols:\n " ;
192
174
os << " # \t Size \t File Name\n " ;
193
175
for (Defined *sym : info.deadSymbols ) {
194
176
assert (!sym->isLive ());
195
177
os << format (" <<dead>>\t 0x%08llX\t [%3u] %s\n " , sym->size ,
196
178
readerToFileOrdinal[sym->getFile ()],
197
- sym->getName ().str ().data ());
198
- }
199
- for (CStringInfo &cstrInfo : info.deadCStrings ) {
200
- os << format (" <<dead>>\t 0x%08llX\t [%3u] literal string: " ,
201
- cstrInfo.str .size () + 1 , cstrInfo.fileIndex );
202
- os.write_escaped (cstrInfo.str ) << " \n " ;
179
+ deadSymbolStrings[sym].c_str ());
203
180
}
204
181
}
205
182
}
0 commit comments