xref: /freebsd/contrib/llvm-project/lld/MachO/MapFile.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1fe6060f1SDimitry Andric //===- MapFile.cpp --------------------------------------------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric //
9*bdd1243dSDimitry Andric // This file implements the -map option, which maps address ranges to their
10*bdd1243dSDimitry Andric // respective contents, plus the input file these contents were originally from.
11*bdd1243dSDimitry Andric // The contents (typically symbols) are listed in address order. Dead-stripped
12*bdd1243dSDimitry Andric // contents are included as well.
13fe6060f1SDimitry Andric //
14fe6060f1SDimitry Andric // # Path: test
15fe6060f1SDimitry Andric // # Arch: x86_84
16fe6060f1SDimitry Andric // # Object files:
17fe6060f1SDimitry Andric // [  0] linker synthesized
18fe6060f1SDimitry Andric // [  1] a.o
19fe6060f1SDimitry Andric // # Sections:
20fe6060f1SDimitry Andric // # Address    Size       Segment  Section
21fe6060f1SDimitry Andric // 0x1000005C0  0x0000004C __TEXT   __text
22fe6060f1SDimitry Andric // # Symbols:
23*bdd1243dSDimitry Andric // # Address    Size       File  Name
24*bdd1243dSDimitry Andric // 0x1000005C0  0x00000001 [  1] _main
25*bdd1243dSDimitry Andric // # Dead Stripped Symbols:
26*bdd1243dSDimitry Andric // #            Size       File  Name
27*bdd1243dSDimitry Andric // <<dead>>     0x00000001 [  1] _foo
28fe6060f1SDimitry Andric //
29fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
30fe6060f1SDimitry Andric 
31fe6060f1SDimitry Andric #include "MapFile.h"
32*bdd1243dSDimitry Andric #include "ConcatOutputSection.h"
33fe6060f1SDimitry Andric #include "Config.h"
34fe6060f1SDimitry Andric #include "InputFiles.h"
35fe6060f1SDimitry Andric #include "InputSection.h"
36fe6060f1SDimitry Andric #include "OutputSegment.h"
37fe6060f1SDimitry Andric #include "Symbols.h"
3881ad6265SDimitry Andric #include "SyntheticSections.h"
39fe6060f1SDimitry Andric #include "Target.h"
40*bdd1243dSDimitry Andric #include "lld/Common/ErrorHandler.h"
41*bdd1243dSDimitry Andric #include "llvm/ADT/DenseMap.h"
42fe6060f1SDimitry Andric #include "llvm/Support/Parallel.h"
43fe6060f1SDimitry Andric #include "llvm/Support/TimeProfiler.h"
44fe6060f1SDimitry Andric 
45fe6060f1SDimitry Andric using namespace llvm;
46fe6060f1SDimitry Andric using namespace llvm::sys;
47fe6060f1SDimitry Andric using namespace lld;
48fe6060f1SDimitry Andric using namespace lld::macho;
49fe6060f1SDimitry Andric 
50*bdd1243dSDimitry Andric struct CStringInfo {
51*bdd1243dSDimitry Andric   uint32_t fileIndex;
52*bdd1243dSDimitry Andric   StringRef str;
53*bdd1243dSDimitry Andric };
54*bdd1243dSDimitry Andric 
55*bdd1243dSDimitry Andric struct MapInfo {
56*bdd1243dSDimitry Andric   SmallVector<InputFile *> files;
57*bdd1243dSDimitry Andric   SmallVector<Defined *> deadSymbols;
58*bdd1243dSDimitry Andric   DenseMap<const OutputSection *,
59*bdd1243dSDimitry Andric            SmallVector<std::pair<uint64_t /*addr*/, CStringInfo>>>
60*bdd1243dSDimitry Andric       liveCStringsForSection;
61*bdd1243dSDimitry Andric   SmallVector<CStringInfo> deadCStrings;
62*bdd1243dSDimitry Andric };
63*bdd1243dSDimitry Andric 
64*bdd1243dSDimitry Andric static MapInfo gatherMapInfo() {
65*bdd1243dSDimitry Andric   MapInfo info;
66*bdd1243dSDimitry Andric   for (InputFile *file : inputFiles) {
67*bdd1243dSDimitry Andric     bool isReferencedFile = false;
68*bdd1243dSDimitry Andric 
69*bdd1243dSDimitry Andric     if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) {
70*bdd1243dSDimitry Andric       uint32_t fileIndex = info.files.size() + 1;
71*bdd1243dSDimitry Andric 
72*bdd1243dSDimitry Andric       // Gather the dead symbols. We don't have to bother with the live ones
73*bdd1243dSDimitry Andric       // because we will pick them up as we iterate over the OutputSections
74*bdd1243dSDimitry Andric       // later.
75*bdd1243dSDimitry Andric       for (Symbol *sym : file->symbols) {
76fe6060f1SDimitry Andric         if (auto *d = dyn_cast_or_null<Defined>(sym))
77*bdd1243dSDimitry Andric           // Only emit the prevailing definition of a symbol. Also, don't emit
78*bdd1243dSDimitry Andric           // the symbol if it is part of a cstring section (we use the literal
79*bdd1243dSDimitry Andric           // value instead, similar to ld64)
80*bdd1243dSDimitry Andric           if (d->isec && d->getFile() == file &&
81*bdd1243dSDimitry Andric               !isa<CStringInputSection>(d->isec)) {
82*bdd1243dSDimitry Andric             isReferencedFile = true;
83*bdd1243dSDimitry Andric             if (!d->isLive())
84*bdd1243dSDimitry Andric               info.deadSymbols.push_back(d);
85*bdd1243dSDimitry Andric           }
86*bdd1243dSDimitry Andric       }
87*bdd1243dSDimitry Andric 
88*bdd1243dSDimitry Andric       // Gather all the cstrings (both live and dead). A CString(Output)Section
89*bdd1243dSDimitry Andric       // doesn't provide us a way of figuring out which InputSections its
90*bdd1243dSDimitry Andric       // cstring contents came from, so we need to build up that mapping here.
91*bdd1243dSDimitry Andric       for (const Section *sec : file->sections) {
92*bdd1243dSDimitry Andric         for (const Subsection &subsec : sec->subsections) {
93*bdd1243dSDimitry Andric           if (auto isec = dyn_cast<CStringInputSection>(subsec.isec)) {
94*bdd1243dSDimitry Andric             auto &liveCStrings = info.liveCStringsForSection[isec->parent];
95*bdd1243dSDimitry Andric             for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
96*bdd1243dSDimitry Andric               if (piece.live)
97*bdd1243dSDimitry Andric                 liveCStrings.push_back({isec->parent->addr + piece.outSecOff,
98*bdd1243dSDimitry Andric                                         {fileIndex, isec->getStringRef(i)}});
99*bdd1243dSDimitry Andric               else
100*bdd1243dSDimitry Andric                 info.deadCStrings.push_back({fileIndex, isec->getStringRef(i)});
101*bdd1243dSDimitry Andric               isReferencedFile = true;
102*bdd1243dSDimitry Andric             }
1031fd87a68SDimitry Andric           } else {
10481ad6265SDimitry Andric             break;
10581ad6265SDimitry Andric           }
10681ad6265SDimitry Andric         }
107*bdd1243dSDimitry Andric       }
108*bdd1243dSDimitry Andric     } else if (const auto *dylibFile = dyn_cast<DylibFile>(file)) {
109*bdd1243dSDimitry Andric       isReferencedFile = dylibFile->isReferenced();
110*bdd1243dSDimitry Andric     }
111fe6060f1SDimitry Andric 
112*bdd1243dSDimitry Andric     if (isReferencedFile)
113*bdd1243dSDimitry Andric       info.files.push_back(file);
114*bdd1243dSDimitry Andric   }
115*bdd1243dSDimitry Andric 
116*bdd1243dSDimitry Andric   // cstrings are not stored in sorted order in their OutputSections, so we sort
117*bdd1243dSDimitry Andric   // them here.
118*bdd1243dSDimitry Andric   for (auto &liveCStrings : info.liveCStringsForSection)
119*bdd1243dSDimitry Andric     parallelSort(liveCStrings.second, [](const auto &p1, const auto &p2) {
120*bdd1243dSDimitry Andric       return p1.first < p2.first;
121*bdd1243dSDimitry Andric     });
122*bdd1243dSDimitry Andric   return info;
123*bdd1243dSDimitry Andric }
124*bdd1243dSDimitry Andric 
125*bdd1243dSDimitry Andric // For printing the contents of the __stubs and __la_symbol_ptr sections.
126*bdd1243dSDimitry Andric void printStubsEntries(
127*bdd1243dSDimitry Andric     raw_fd_ostream &os,
128*bdd1243dSDimitry Andric     const DenseMap<lld::macho::InputFile *, uint32_t> &readerToFileOrdinal,
129*bdd1243dSDimitry Andric     const OutputSection *osec, size_t entrySize) {
130*bdd1243dSDimitry Andric   for (const Symbol *sym : in.stubs->getEntries())
131*bdd1243dSDimitry Andric     os << format("0x%08llX\t0x%08zX\t[%3u] %s\n",
132*bdd1243dSDimitry Andric                  osec->addr + sym->stubsIndex * entrySize, entrySize,
133*bdd1243dSDimitry Andric                  readerToFileOrdinal.lookup(sym->getFile()),
134*bdd1243dSDimitry Andric                  sym->getName().str().data());
135*bdd1243dSDimitry Andric }
136*bdd1243dSDimitry Andric 
137*bdd1243dSDimitry Andric void printNonLazyPointerSection(raw_fd_ostream &os,
138*bdd1243dSDimitry Andric                                 NonLazyPointerSectionBase *osec) {
139*bdd1243dSDimitry Andric   // ld64 considers stubs to belong to particular files, but considers GOT
140*bdd1243dSDimitry Andric   // entries to be linker-synthesized. Not sure why they made that decision, but
141*bdd1243dSDimitry Andric   // I think we can follow suit unless there's demand for better symbol-to-file
142*bdd1243dSDimitry Andric   // associations.
143*bdd1243dSDimitry Andric   for (const Symbol *sym : osec->getEntries())
144*bdd1243dSDimitry Andric     os << format("0x%08llX\t0x%08zX\t[  0] non-lazy-pointer-to-local: %s\n",
145*bdd1243dSDimitry Andric                  osec->addr + sym->gotIndex * target->wordSize,
146*bdd1243dSDimitry Andric                  target->wordSize, sym->getName().str().data());
147fe6060f1SDimitry Andric }
148fe6060f1SDimitry Andric 
149fe6060f1SDimitry Andric void macho::writeMapFile() {
150fe6060f1SDimitry Andric   if (config->mapFile.empty())
151fe6060f1SDimitry Andric     return;
152fe6060f1SDimitry Andric 
153fe6060f1SDimitry Andric   TimeTraceScope timeScope("Write map file");
154fe6060f1SDimitry Andric 
155fe6060f1SDimitry Andric   // Open a map file for writing.
156fe6060f1SDimitry Andric   std::error_code ec;
157fe6060f1SDimitry Andric   raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
158fe6060f1SDimitry Andric   if (ec) {
159fe6060f1SDimitry Andric     error("cannot open " + config->mapFile + ": " + ec.message());
160fe6060f1SDimitry Andric     return;
161fe6060f1SDimitry Andric   }
162fe6060f1SDimitry Andric 
163fe6060f1SDimitry Andric   os << format("# Path: %s\n", config->outputFile.str().c_str());
164fe6060f1SDimitry Andric   os << format("# Arch: %s\n",
165fe6060f1SDimitry Andric                getArchitectureName(config->arch()).str().c_str());
166fe6060f1SDimitry Andric 
167*bdd1243dSDimitry Andric   MapInfo info = gatherMapInfo();
168*bdd1243dSDimitry Andric 
169fe6060f1SDimitry Andric   os << "# Object files:\n";
170fe6060f1SDimitry Andric   os << format("[%3u] %s\n", 0, (const char *)"linker synthesized");
171fe6060f1SDimitry Andric   uint32_t fileIndex = 1;
172fe6060f1SDimitry Andric   DenseMap<lld::macho::InputFile *, uint32_t> readerToFileOrdinal;
173*bdd1243dSDimitry Andric   for (InputFile *file : info.files) {
174fe6060f1SDimitry Andric     os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str());
175fe6060f1SDimitry Andric     readerToFileOrdinal[file] = fileIndex++;
176fe6060f1SDimitry Andric   }
177fe6060f1SDimitry Andric 
178fe6060f1SDimitry Andric   os << "# Sections:\n";
179fe6060f1SDimitry Andric   os << "# Address\tSize    \tSegment\tSection\n";
180fe6060f1SDimitry Andric   for (OutputSegment *seg : outputSegments)
181fe6060f1SDimitry Andric     for (OutputSection *osec : seg->getSections()) {
182fe6060f1SDimitry Andric       if (osec->isHidden())
183fe6060f1SDimitry Andric         continue;
184fe6060f1SDimitry Andric 
185fe6060f1SDimitry Andric       os << format("0x%08llX\t0x%08llX\t%s\t%s\n", osec->addr, osec->getSize(),
186fe6060f1SDimitry Andric                    seg->name.str().c_str(), osec->name.str().c_str());
187fe6060f1SDimitry Andric     }
188fe6060f1SDimitry Andric 
189fe6060f1SDimitry Andric   os << "# Symbols:\n";
190*bdd1243dSDimitry Andric   os << "# Address\tSize    \tFile  Name\n";
191*bdd1243dSDimitry Andric   for (const OutputSegment *seg : outputSegments) {
192*bdd1243dSDimitry Andric     for (const OutputSection *osec : seg->getSections()) {
193*bdd1243dSDimitry Andric       if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {
194*bdd1243dSDimitry Andric         for (const InputSection *isec : concatOsec->inputs) {
195*bdd1243dSDimitry Andric           for (Defined *sym : isec->symbols)
196*bdd1243dSDimitry Andric             os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(),
197*bdd1243dSDimitry Andric                          sym->size, readerToFileOrdinal[sym->getFile()],
198*bdd1243dSDimitry Andric                          sym->getName().str().data());
199*bdd1243dSDimitry Andric         }
200*bdd1243dSDimitry Andric       } else if (osec == in.cStringSection || osec == in.objcMethnameSection) {
201*bdd1243dSDimitry Andric         const auto &liveCStrings = info.liveCStringsForSection.lookup(osec);
202*bdd1243dSDimitry Andric         uint64_t lastAddr = 0; // strings will never start at address 0, so this
203*bdd1243dSDimitry Andric                                // is a sentinel value
204*bdd1243dSDimitry Andric         for (const auto &[addr, info] : liveCStrings) {
205*bdd1243dSDimitry Andric           uint64_t size = 0;
206*bdd1243dSDimitry Andric           if (addr != lastAddr)
207*bdd1243dSDimitry Andric             size = info.str.size() + 1; // include null terminator
208*bdd1243dSDimitry Andric           lastAddr = addr;
209*bdd1243dSDimitry Andric           os << format("0x%08llX\t0x%08llX\t[%3u] literal string: ", addr, size,
210*bdd1243dSDimitry Andric                        info.fileIndex);
211*bdd1243dSDimitry Andric           os.write_escaped(info.str) << "\n";
212*bdd1243dSDimitry Andric         }
213*bdd1243dSDimitry Andric       } else if (osec == (void *)in.unwindInfo) {
214*bdd1243dSDimitry Andric         os << format("0x%08llX\t0x%08llX\t[  0] compact unwind info\n",
215*bdd1243dSDimitry Andric                      osec->addr, osec->getSize());
216*bdd1243dSDimitry Andric       } else if (osec == in.stubs) {
217*bdd1243dSDimitry Andric         printStubsEntries(os, readerToFileOrdinal, osec, target->stubSize);
218*bdd1243dSDimitry Andric       } else if (osec == in.lazyPointers) {
219*bdd1243dSDimitry Andric         printStubsEntries(os, readerToFileOrdinal, osec, target->wordSize);
220*bdd1243dSDimitry Andric       } else if (osec == in.stubHelper) {
221*bdd1243dSDimitry Andric         // yes, ld64 calls it "helper helper"...
222*bdd1243dSDimitry Andric         os << format("0x%08llX\t0x%08llX\t[  0] helper helper\n", osec->addr,
223*bdd1243dSDimitry Andric                      osec->getSize());
224*bdd1243dSDimitry Andric       } else if (osec == in.got) {
225*bdd1243dSDimitry Andric         printNonLazyPointerSection(os, in.got);
226*bdd1243dSDimitry Andric       } else if (osec == in.tlvPointers) {
227*bdd1243dSDimitry Andric         printNonLazyPointerSection(os, in.tlvPointers);
228*bdd1243dSDimitry Andric       }
229*bdd1243dSDimitry Andric       // TODO print other synthetic sections
230*bdd1243dSDimitry Andric     }
231fe6060f1SDimitry Andric   }
232fe6060f1SDimitry Andric 
2331fd87a68SDimitry Andric   if (config->deadStrip) {
2341fd87a68SDimitry Andric     os << "# Dead Stripped Symbols:\n";
235*bdd1243dSDimitry Andric     os << "#        \tSize    \tFile  Name\n";
236*bdd1243dSDimitry Andric     for (Defined *sym : info.deadSymbols) {
2371fd87a68SDimitry Andric       assert(!sym->isLive());
238*bdd1243dSDimitry Andric       os << format("<<dead>>\t0x%08llX\t[%3u] %s\n", sym->size,
239*bdd1243dSDimitry Andric                    readerToFileOrdinal[sym->getFile()],
240*bdd1243dSDimitry Andric                    sym->getName().str().data());
241*bdd1243dSDimitry Andric     }
242*bdd1243dSDimitry Andric     for (CStringInfo &cstrInfo : info.deadCStrings) {
243*bdd1243dSDimitry Andric       os << format("<<dead>>\t0x%08zX\t[%3u] literal string: ",
244*bdd1243dSDimitry Andric                    cstrInfo.str.size() + 1, cstrInfo.fileIndex);
245*bdd1243dSDimitry Andric       os.write_escaped(cstrInfo.str) << "\n";
2461fd87a68SDimitry Andric     }
2471fd87a68SDimitry Andric   }
248fe6060f1SDimitry Andric }
249