66//
77// ===----------------------------------------------------------------------===//
88//
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:
1312//
1413// # Path: test
1514// # Arch: x86_84
2928// ===----------------------------------------------------------------------===//
3029
3130#include " MapFile.h"
32- #include " ConcatOutputSection.h"
3331#include " Config.h"
3432#include " InputFiles.h"
3533#include " InputSection.h"
34+ #include " OutputSection.h"
3635#include " OutputSegment.h"
3736#include " Symbols.h"
3837#include " SyntheticSections.h"
3938#include " Target.h"
4039#include " lld/Common/ErrorHandler.h"
41- #include " llvm/ADT/DenseMap.h"
4240#include " llvm/Support/Parallel.h"
4341#include " llvm/Support/TimeProfiler.h"
4442
@@ -47,77 +45,71 @@ using namespace llvm::sys;
4745using namespace lld ;
4846using namespace lld ::macho;
4947
50- struct CStringInfo {
51- uint32_t fileIndex;
52- StringRef str;
53- };
54-
5548struct MapInfo {
5649 SmallVector<InputFile *> files;
50+ SmallVector<Defined *> liveSymbols;
5751 SmallVector<Defined *> deadSymbols;
58- DenseMap<const OutputSection *,
59- SmallVector<std::pair<uint64_t /* addr*/ , CStringInfo>>>
60- liveCStringsForSection;
61- SmallVector<CStringInfo> deadCStrings;
6252};
6353
6454static MapInfo gatherMapInfo () {
6555 MapInfo info;
6656 for (InputFile *file : inputFiles)
6757 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 ;
7459 for (Symbol *sym : file->symbols ) {
7560 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 {
8366 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 ;
10167 }
102- } else {
103- break ;
68+ hasEmittedSymbol = true ;
10469 }
105- }
10670 }
107-
108- if (isReferencedFile)
71+ if (hasEmittedSymbol)
10972 info.files .push_back (file);
11073 }
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 (); });
11876 return info;
11977}
12078
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+
121113void macho::writeMapFile () {
122114 if (config->mapFile .empty ())
123115 return ;
@@ -132,12 +124,16 @@ void macho::writeMapFile() {
132124 return ;
133125 }
134126
127+ // Dump output path.
135128 os << format (" # Path: %s\n " , config->outputFile .str ().c_str ());
129+
130+ // Dump output architecture.
136131 os << format (" # Arch: %s\n " ,
137132 getArchitectureName (config->arch ()).str ().c_str ());
138133
139134 MapInfo info = gatherMapInfo ();
140135
136+ // Dump table of object files.
141137 os << " # Object files:\n " ;
142138 os << format (" [%3u] %s\n " , 0 , (const char *)" linker synthesized" );
143139 uint32_t fileIndex = 1 ;
@@ -147,6 +143,7 @@ void macho::writeMapFile() {
147143 readerToFileOrdinal[file] = fileIndex++;
148144 }
149145
146+ // Dump table of sections
150147 os << " # Sections:\n " ;
151148 os << " # Address\t Size \t Segment\t Section\n " ;
152149 for (OutputSegment *seg : outputSegments)
@@ -158,48 +155,28 @@ void macho::writeMapFile() {
158155 seg->name .str ().c_str (), osec->name .str ().c_str ());
159156 }
160157
158+ // Dump table of symbols
159+ DenseMap<Symbol *, std::string> liveSymbolStrings =
160+ getSymbolStrings (info.liveSymbols );
161161 os << " # Symbols:\n " ;
162162 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 ());
188168 }
189169
190170 if (config->deadStrip ) {
171+ DenseMap<Symbol *, std::string> deadSymbolStrings =
172+ getSymbolStrings (info.deadSymbols );
191173 os << " # Dead Stripped Symbols:\n " ;
192174 os << " # \t Size \t File Name\n " ;
193175 for (Defined *sym : info.deadSymbols ) {
194176 assert (!sym->isLive ());
195177 os << format (" <<dead>>\t 0x%08llX\t [%3u] %s\n " , sym->size ,
196178 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 ());
203180 }
204181 }
205182}
0 commit comments