6
6
//
7
7
// ===----------------------------------------------------------------------===//
8
8
//
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:
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.
12
13
//
13
14
// # Path: test
14
15
// # Arch: x86_84
28
29
// ===----------------------------------------------------------------------===//
29
30
30
31
#include " MapFile.h"
32
+ #include " ConcatOutputSection.h"
31
33
#include " Config.h"
32
34
#include " InputFiles.h"
33
35
#include " InputSection.h"
34
- #include " OutputSection.h"
35
36
#include " OutputSegment.h"
36
37
#include " Symbols.h"
37
38
#include " SyntheticSections.h"
38
39
#include " Target.h"
39
40
#include " lld/Common/ErrorHandler.h"
41
+ #include " llvm/ADT/DenseMap.h"
40
42
#include " llvm/Support/Parallel.h"
41
43
#include " llvm/Support/TimeProfiler.h"
42
44
@@ -45,69 +47,75 @@ using namespace llvm::sys;
45
47
using namespace lld ;
46
48
using namespace lld ::macho;
47
49
50
+ struct CStringInfo {
51
+ uint32_t fileIndex;
52
+ StringRef str;
53
+ };
54
+
48
55
struct MapInfo {
49
56
SmallVector<InputFile *> files;
50
- SmallVector<Defined *> liveSymbols;
51
57
SmallVector<Defined *> deadSymbols;
58
+ DenseMap<const OutputSection *,
59
+ SmallVector<std::pair<uint64_t /* addr*/ , CStringInfo>>>
60
+ liveCStringsForSection;
61
+ SmallVector<CStringInfo> deadCStrings;
52
62
};
53
63
54
64
static MapInfo gatherMapInfo () {
55
65
MapInfo info;
56
66
for (InputFile *file : inputFiles)
57
67
if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) {
58
- bool hasEmittedSymbol = false ;
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.
59
74
for (Symbol *sym : file->symbols ) {
60
75
if (auto *d = dyn_cast_or_null<Defined>(sym))
61
- if (d->isec && d->getFile () == file) {
62
- if (d->isLive ()) {
63
- assert (!shouldOmitFromOutput (d->isec ));
64
- info.liveSymbols .push_back (d);
65
- } else {
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 ())
66
83
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 ;
67
101
}
68
- hasEmittedSymbol = true ;
102
+ } else {
103
+ break ;
69
104
}
105
+ }
70
106
}
71
- if (hasEmittedSymbol)
72
- info.files .push_back (file);
73
- }
74
- parallelSort (info.liveSymbols .begin (), info.liveSymbols .end (),
75
- [](Defined *a, Defined *b) { return a->getVA () < b->getVA (); });
76
- return info;
77
- }
78
107
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);
108
+ if (isReferencedFile)
109
+ info.files .push_back (file);
104
110
}
105
- });
106
111
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;
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
+ });
118
+ return info;
111
119
}
112
120
113
121
void macho::writeMapFile () {
@@ -124,16 +132,12 @@ void macho::writeMapFile() {
124
132
return ;
125
133
}
126
134
127
- // Dump output path.
128
135
os << format (" # Path: %s\n " , config->outputFile .str ().c_str ());
129
-
130
- // Dump output architecture.
131
136
os << format (" # Arch: %s\n " ,
132
137
getArchitectureName (config->arch ()).str ().c_str ());
133
138
134
139
MapInfo info = gatherMapInfo ();
135
140
136
- // Dump table of object files.
137
141
os << " # Object files:\n " ;
138
142
os << format (" [%3u] %s\n " , 0 , (const char *)" linker synthesized" );
139
143
uint32_t fileIndex = 1 ;
@@ -143,7 +147,6 @@ void macho::writeMapFile() {
143
147
readerToFileOrdinal[file] = fileIndex++;
144
148
}
145
149
146
- // Dump table of sections
147
150
os << " # Sections:\n " ;
148
151
os << " # Address\t Size \t Segment\t Section\n " ;
149
152
for (OutputSegment *seg : outputSegments)
@@ -155,28 +158,48 @@ void macho::writeMapFile() {
155
158
seg->name .str ().c_str (), osec->name .str ().c_str ());
156
159
}
157
160
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 (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 ());
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
+ }
168
188
}
169
189
170
190
if (config->deadStrip ) {
171
- DenseMap<Symbol *, std::string> deadSymbolStrings =
172
- getSymbolStrings (info.deadSymbols );
173
191
os << " # Dead Stripped Symbols:\n " ;
174
192
os << " # \t Size \t File Name\n " ;
175
193
for (Defined *sym : info.deadSymbols ) {
176
194
assert (!sym->isLive ());
177
195
os << format (" <<dead>>\t 0x%08llX\t [%3u] %s\n " , sym->size ,
178
196
readerToFileOrdinal[sym->getFile ()],
179
- deadSymbolStrings[sym].c_str ());
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 " ;
180
203
}
181
204
}
182
205
}
0 commit comments