xref: /openbsd-src/gnu/llvm/lld/MachO/MapFile.cpp (revision dfe94b169149f14cc1aee2cf6dad58a8d9a1860c)
11cf9926bSpatrick //===- MapFile.cpp --------------------------------------------------------===//
21cf9926bSpatrick //
31cf9926bSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41cf9926bSpatrick // See https://llvm.org/LICENSE.txt for license information.
51cf9926bSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61cf9926bSpatrick //
71cf9926bSpatrick //===----------------------------------------------------------------------===//
81cf9926bSpatrick //
9*dfe94b16Srobert // This file implements the -map option, which maps address ranges to their
10*dfe94b16Srobert // respective contents, plus the input file these contents were originally from.
11*dfe94b16Srobert // The contents (typically symbols) are listed in address order. Dead-stripped
12*dfe94b16Srobert // contents are included as well.
131cf9926bSpatrick //
141cf9926bSpatrick // # Path: test
151cf9926bSpatrick // # Arch: x86_84
161cf9926bSpatrick // # Object files:
171cf9926bSpatrick // [  0] linker synthesized
181cf9926bSpatrick // [  1] a.o
191cf9926bSpatrick // # Sections:
201cf9926bSpatrick // # Address    Size       Segment  Section
211cf9926bSpatrick // 0x1000005C0  0x0000004C __TEXT   __text
221cf9926bSpatrick // # Symbols:
23*dfe94b16Srobert // # Address    Size       File  Name
24*dfe94b16Srobert // 0x1000005C0  0x00000001 [  1] _main
25*dfe94b16Srobert // # Dead Stripped Symbols:
26*dfe94b16Srobert // #            Size       File  Name
27*dfe94b16Srobert // <<dead>>     0x00000001 [  1] _foo
281cf9926bSpatrick //
291cf9926bSpatrick //===----------------------------------------------------------------------===//
301cf9926bSpatrick 
311cf9926bSpatrick #include "MapFile.h"
32*dfe94b16Srobert #include "ConcatOutputSection.h"
331cf9926bSpatrick #include "Config.h"
341cf9926bSpatrick #include "InputFiles.h"
351cf9926bSpatrick #include "InputSection.h"
361cf9926bSpatrick #include "OutputSegment.h"
371cf9926bSpatrick #include "Symbols.h"
38*dfe94b16Srobert #include "SyntheticSections.h"
391cf9926bSpatrick #include "Target.h"
40*dfe94b16Srobert #include "lld/Common/ErrorHandler.h"
41*dfe94b16Srobert #include "llvm/ADT/DenseMap.h"
421cf9926bSpatrick #include "llvm/Support/Parallel.h"
431cf9926bSpatrick #include "llvm/Support/TimeProfiler.h"
441cf9926bSpatrick 
451cf9926bSpatrick using namespace llvm;
461cf9926bSpatrick using namespace llvm::sys;
471cf9926bSpatrick using namespace lld;
481cf9926bSpatrick using namespace lld::macho;
491cf9926bSpatrick 
50*dfe94b16Srobert struct CStringInfo {
51*dfe94b16Srobert   uint32_t fileIndex;
52*dfe94b16Srobert   StringRef str;
53*dfe94b16Srobert };
541cf9926bSpatrick 
55*dfe94b16Srobert struct MapInfo {
56*dfe94b16Srobert   SmallVector<InputFile *> files;
57*dfe94b16Srobert   SmallVector<Defined *> deadSymbols;
58*dfe94b16Srobert   DenseMap<const OutputSection *,
59*dfe94b16Srobert            SmallVector<std::pair<uint64_t /*addr*/, CStringInfo>>>
60*dfe94b16Srobert       liveCStringsForSection;
61*dfe94b16Srobert   SmallVector<CStringInfo> deadCStrings;
62*dfe94b16Srobert };
631cf9926bSpatrick 
gatherMapInfo()64*dfe94b16Srobert static MapInfo gatherMapInfo() {
65*dfe94b16Srobert   MapInfo info;
66*dfe94b16Srobert   for (InputFile *file : inputFiles) {
67*dfe94b16Srobert     bool isReferencedFile = false;
681cf9926bSpatrick 
69*dfe94b16Srobert     if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) {
70*dfe94b16Srobert       uint32_t fileIndex = info.files.size() + 1;
71*dfe94b16Srobert 
72*dfe94b16Srobert       // Gather the dead symbols. We don't have to bother with the live ones
73*dfe94b16Srobert       // because we will pick them up as we iterate over the OutputSections
74*dfe94b16Srobert       // later.
75*dfe94b16Srobert       for (Symbol *sym : file->symbols) {
761cf9926bSpatrick         if (auto *d = dyn_cast_or_null<Defined>(sym))
77*dfe94b16Srobert           // Only emit the prevailing definition of a symbol. Also, don't emit
78*dfe94b16Srobert           // the symbol if it is part of a cstring section (we use the literal
79*dfe94b16Srobert           // value instead, similar to ld64)
80*dfe94b16Srobert           if (d->isec && d->getFile() == file &&
81*dfe94b16Srobert               !isa<CStringInputSection>(d->isec)) {
82*dfe94b16Srobert             isReferencedFile = true;
83*dfe94b16Srobert             if (!d->isLive())
84*dfe94b16Srobert               info.deadSymbols.push_back(d);
851cf9926bSpatrick           }
861cf9926bSpatrick       }
871cf9926bSpatrick 
88*dfe94b16Srobert       // Gather all the cstrings (both live and dead). A CString(Output)Section
89*dfe94b16Srobert       // doesn't provide us a way of figuring out which InputSections its
90*dfe94b16Srobert       // cstring contents came from, so we need to build up that mapping here.
91*dfe94b16Srobert       for (const Section *sec : file->sections) {
92*dfe94b16Srobert         for (const Subsection &subsec : sec->subsections) {
93*dfe94b16Srobert           if (auto isec = dyn_cast<CStringInputSection>(subsec.isec)) {
94*dfe94b16Srobert             auto &liveCStrings = info.liveCStringsForSection[isec->parent];
95*dfe94b16Srobert             for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
96*dfe94b16Srobert               if (piece.live)
97*dfe94b16Srobert                 liveCStrings.push_back({isec->parent->addr + piece.outSecOff,
98*dfe94b16Srobert                                         {fileIndex, isec->getStringRef(i)}});
99*dfe94b16Srobert               else
100*dfe94b16Srobert                 info.deadCStrings.push_back({fileIndex, isec->getStringRef(i)});
101*dfe94b16Srobert               isReferencedFile = true;
102*dfe94b16Srobert             }
103*dfe94b16Srobert           } else {
104*dfe94b16Srobert             break;
105*dfe94b16Srobert           }
106*dfe94b16Srobert         }
107*dfe94b16Srobert       }
108*dfe94b16Srobert     } else if (const auto *dylibFile = dyn_cast<DylibFile>(file)) {
109*dfe94b16Srobert       isReferencedFile = dylibFile->isReferenced();
110*dfe94b16Srobert     }
111*dfe94b16Srobert 
112*dfe94b16Srobert     if (isReferencedFile)
113*dfe94b16Srobert       info.files.push_back(file);
114*dfe94b16Srobert   }
115*dfe94b16Srobert 
116*dfe94b16Srobert   // cstrings are not stored in sorted order in their OutputSections, so we sort
117*dfe94b16Srobert   // them here.
118*dfe94b16Srobert   for (auto &liveCStrings : info.liveCStringsForSection)
119*dfe94b16Srobert     parallelSort(liveCStrings.second, [](const auto &p1, const auto &p2) {
120*dfe94b16Srobert       return p1.first < p2.first;
1211cf9926bSpatrick     });
122*dfe94b16Srobert   return info;
123*dfe94b16Srobert }
1241cf9926bSpatrick 
125*dfe94b16Srobert // For printing the contents of the __stubs and __la_symbol_ptr sections.
printStubsEntries(raw_fd_ostream & os,const DenseMap<lld::macho::InputFile *,uint32_t> & readerToFileOrdinal,const OutputSection * osec,size_t entrySize)126*dfe94b16Srobert void printStubsEntries(
127*dfe94b16Srobert     raw_fd_ostream &os,
128*dfe94b16Srobert     const DenseMap<lld::macho::InputFile *, uint32_t> &readerToFileOrdinal,
129*dfe94b16Srobert     const OutputSection *osec, size_t entrySize) {
130*dfe94b16Srobert   for (const Symbol *sym : in.stubs->getEntries())
131*dfe94b16Srobert     os << format("0x%08llX\t0x%08zX\t[%3u] %s\n",
132*dfe94b16Srobert                  osec->addr + sym->stubsIndex * entrySize, entrySize,
133*dfe94b16Srobert                  readerToFileOrdinal.lookup(sym->getFile()),
134*dfe94b16Srobert                  sym->getName().str().data());
135*dfe94b16Srobert }
136*dfe94b16Srobert 
printNonLazyPointerSection(raw_fd_ostream & os,NonLazyPointerSectionBase * osec)137*dfe94b16Srobert void printNonLazyPointerSection(raw_fd_ostream &os,
138*dfe94b16Srobert                                 NonLazyPointerSectionBase *osec) {
139*dfe94b16Srobert   // ld64 considers stubs to belong to particular files, but considers GOT
140*dfe94b16Srobert   // entries to be linker-synthesized. Not sure why they made that decision, but
141*dfe94b16Srobert   // I think we can follow suit unless there's demand for better symbol-to-file
142*dfe94b16Srobert   // associations.
143*dfe94b16Srobert   for (const Symbol *sym : osec->getEntries())
144*dfe94b16Srobert     os << format("0x%08llX\t0x%08zX\t[  0] non-lazy-pointer-to-local: %s\n",
145*dfe94b16Srobert                  osec->addr + sym->gotIndex * target->wordSize,
146*dfe94b16Srobert                  target->wordSize, sym->getName().str().data());
1471cf9926bSpatrick }
1481cf9926bSpatrick 
writeMapFile()1491cf9926bSpatrick void macho::writeMapFile() {
1501cf9926bSpatrick   if (config->mapFile.empty())
1511cf9926bSpatrick     return;
1521cf9926bSpatrick 
1531cf9926bSpatrick   TimeTraceScope timeScope("Write map file");
1541cf9926bSpatrick 
1551cf9926bSpatrick   // Open a map file for writing.
1561cf9926bSpatrick   std::error_code ec;
1571cf9926bSpatrick   raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
1581cf9926bSpatrick   if (ec) {
1591cf9926bSpatrick     error("cannot open " + config->mapFile + ": " + ec.message());
1601cf9926bSpatrick     return;
1611cf9926bSpatrick   }
1621cf9926bSpatrick 
1631cf9926bSpatrick   os << format("# Path: %s\n", config->outputFile.str().c_str());
1641cf9926bSpatrick   os << format("# Arch: %s\n",
1651cf9926bSpatrick                getArchitectureName(config->arch()).str().c_str());
1661cf9926bSpatrick 
167*dfe94b16Srobert   MapInfo info = gatherMapInfo();
168*dfe94b16Srobert 
1691cf9926bSpatrick   os << "# Object files:\n";
1701cf9926bSpatrick   os << format("[%3u] %s\n", 0, (const char *)"linker synthesized");
1711cf9926bSpatrick   uint32_t fileIndex = 1;
1721cf9926bSpatrick   DenseMap<lld::macho::InputFile *, uint32_t> readerToFileOrdinal;
173*dfe94b16Srobert   for (InputFile *file : info.files) {
1741cf9926bSpatrick     os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str());
1751cf9926bSpatrick     readerToFileOrdinal[file] = fileIndex++;
1761cf9926bSpatrick   }
1771cf9926bSpatrick 
1781cf9926bSpatrick   os << "# Sections:\n";
1791cf9926bSpatrick   os << "# Address\tSize    \tSegment\tSection\n";
1801cf9926bSpatrick   for (OutputSegment *seg : outputSegments)
1811cf9926bSpatrick     for (OutputSection *osec : seg->getSections()) {
1821cf9926bSpatrick       if (osec->isHidden())
1831cf9926bSpatrick         continue;
1841cf9926bSpatrick 
1851cf9926bSpatrick       os << format("0x%08llX\t0x%08llX\t%s\t%s\n", osec->addr, osec->getSize(),
1861cf9926bSpatrick                    seg->name.str().c_str(), osec->name.str().c_str());
1871cf9926bSpatrick     }
1881cf9926bSpatrick 
1891cf9926bSpatrick   os << "# Symbols:\n";
190*dfe94b16Srobert   os << "# Address\tSize    \tFile  Name\n";
191*dfe94b16Srobert   for (const OutputSegment *seg : outputSegments) {
192*dfe94b16Srobert     for (const OutputSection *osec : seg->getSections()) {
193*dfe94b16Srobert       if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {
194*dfe94b16Srobert         for (const InputSection *isec : concatOsec->inputs) {
195*dfe94b16Srobert           for (Defined *sym : isec->symbols)
196*dfe94b16Srobert             os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(),
197*dfe94b16Srobert                          sym->size, readerToFileOrdinal[sym->getFile()],
198*dfe94b16Srobert                          sym->getName().str().data());
199*dfe94b16Srobert         }
200*dfe94b16Srobert       } else if (osec == in.cStringSection || osec == in.objcMethnameSection) {
201*dfe94b16Srobert         const auto &liveCStrings = info.liveCStringsForSection.lookup(osec);
202*dfe94b16Srobert         uint64_t lastAddr = 0; // strings will never start at address 0, so this
203*dfe94b16Srobert                                // is a sentinel value
204*dfe94b16Srobert         for (const auto &[addr, info] : liveCStrings) {
205*dfe94b16Srobert           uint64_t size = 0;
206*dfe94b16Srobert           if (addr != lastAddr)
207*dfe94b16Srobert             size = info.str.size() + 1; // include null terminator
208*dfe94b16Srobert           lastAddr = addr;
209*dfe94b16Srobert           os << format("0x%08llX\t0x%08llX\t[%3u] literal string: ", addr, size,
210*dfe94b16Srobert                        info.fileIndex);
211*dfe94b16Srobert           os.write_escaped(info.str) << "\n";
212*dfe94b16Srobert         }
213*dfe94b16Srobert       } else if (osec == (void *)in.unwindInfo) {
214*dfe94b16Srobert         os << format("0x%08llX\t0x%08llX\t[  0] compact unwind info\n",
215*dfe94b16Srobert                      osec->addr, osec->getSize());
216*dfe94b16Srobert       } else if (osec == in.stubs) {
217*dfe94b16Srobert         printStubsEntries(os, readerToFileOrdinal, osec, target->stubSize);
218*dfe94b16Srobert       } else if (osec == in.lazyPointers) {
219*dfe94b16Srobert         printStubsEntries(os, readerToFileOrdinal, osec, target->wordSize);
220*dfe94b16Srobert       } else if (osec == in.stubHelper) {
221*dfe94b16Srobert         // yes, ld64 calls it "helper helper"...
222*dfe94b16Srobert         os << format("0x%08llX\t0x%08llX\t[  0] helper helper\n", osec->addr,
223*dfe94b16Srobert                      osec->getSize());
224*dfe94b16Srobert       } else if (osec == in.got) {
225*dfe94b16Srobert         printNonLazyPointerSection(os, in.got);
226*dfe94b16Srobert       } else if (osec == in.tlvPointers) {
227*dfe94b16Srobert         printNonLazyPointerSection(os, in.tlvPointers);
228*dfe94b16Srobert       }
229*dfe94b16Srobert       // TODO print other synthetic sections
2301cf9926bSpatrick     }
2311cf9926bSpatrick   }
2321cf9926bSpatrick 
233*dfe94b16Srobert   if (config->deadStrip) {
234*dfe94b16Srobert     os << "# Dead Stripped Symbols:\n";
235*dfe94b16Srobert     os << "#        \tSize    \tFile  Name\n";
236*dfe94b16Srobert     for (Defined *sym : info.deadSymbols) {
237*dfe94b16Srobert       assert(!sym->isLive());
238*dfe94b16Srobert       os << format("<<dead>>\t0x%08llX\t[%3u] %s\n", sym->size,
239*dfe94b16Srobert                    readerToFileOrdinal[sym->getFile()],
240*dfe94b16Srobert                    sym->getName().str().data());
241*dfe94b16Srobert     }
242*dfe94b16Srobert     for (CStringInfo &cstrInfo : info.deadCStrings) {
243*dfe94b16Srobert       os << format("<<dead>>\t0x%08zX\t[%3u] literal string: ",
244*dfe94b16Srobert                    cstrInfo.str.size() + 1, cstrInfo.fileIndex);
245*dfe94b16Srobert       os.write_escaped(cstrInfo.str) << "\n";
246*dfe94b16Srobert     }
247*dfe94b16Srobert   }
2481cf9926bSpatrick }
249