1ed8bff13Scaoming.roy //===- MapFile.cpp --------------------------------------------------------===// 2ed8bff13Scaoming.roy // 3ed8bff13Scaoming.roy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4ed8bff13Scaoming.roy // See https://llvm.org/LICENSE.txt for license information. 5ed8bff13Scaoming.roy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6ed8bff13Scaoming.roy // 7ed8bff13Scaoming.roy //===----------------------------------------------------------------------===// 8ed8bff13Scaoming.roy // 97ca32bd4SJez Ng // This file implements the -map option, which maps address ranges to their 107ca32bd4SJez Ng // respective contents, plus the input file these contents were originally from. 117ca32bd4SJez Ng // The contents (typically symbols) are listed in address order. Dead-stripped 127ca32bd4SJez Ng // contents are included as well. 13ed8bff13Scaoming.roy // 14ed8bff13Scaoming.roy // # Path: test 15ed8bff13Scaoming.roy // # Arch: x86_84 16ed8bff13Scaoming.roy // # Object files: 17ed8bff13Scaoming.roy // [ 0] linker synthesized 18ed8bff13Scaoming.roy // [ 1] a.o 19ed8bff13Scaoming.roy // # Sections: 20ed8bff13Scaoming.roy // # Address Size Segment Section 21ed8bff13Scaoming.roy // 0x1000005C0 0x0000004C __TEXT __text 22ed8bff13Scaoming.roy // # Symbols: 23da374d18SJez Ng // # Address Size File Name 24da374d18SJez Ng // 0x1000005C0 0x00000001 [ 1] _main 25da374d18SJez Ng // # Dead Stripped Symbols: 26da374d18SJez Ng // # Size File Name 27da374d18SJez Ng // <<dead>> 0x00000001 [ 1] _foo 28ed8bff13Scaoming.roy // 29ed8bff13Scaoming.roy //===----------------------------------------------------------------------===// 30ed8bff13Scaoming.roy 31ed8bff13Scaoming.roy #include "MapFile.h" 327ca32bd4SJez Ng #include "ConcatOutputSection.h" 33ed8bff13Scaoming.roy #include "Config.h" 34ed8bff13Scaoming.roy #include "InputFiles.h" 35ed8bff13Scaoming.roy #include "InputSection.h" 36ed8bff13Scaoming.roy #include "OutputSegment.h" 37ed8bff13Scaoming.roy #include "Symbols.h" 384f2c46c3SRoger Kim #include "SyntheticSections.h" 39ed8bff13Scaoming.roy #include "Target.h" 40fc7a7189SVy Nguyen #include "lld/Common/ErrorHandler.h" 417ca32bd4SJez Ng #include "llvm/ADT/DenseMap.h" 42ed8bff13Scaoming.roy #include "llvm/Support/Parallel.h" 434bcaafebSJez Ng #include "llvm/Support/TimeProfiler.h" 44ed8bff13Scaoming.roy 45ed8bff13Scaoming.roy using namespace llvm; 46ed8bff13Scaoming.roy using namespace llvm::sys; 47ed8bff13Scaoming.roy using namespace lld; 48ed8bff13Scaoming.roy using namespace lld::macho; 49ed8bff13Scaoming.roy 507ca32bd4SJez Ng struct CStringInfo { 517ca32bd4SJez Ng uint32_t fileIndex; 527ca32bd4SJez Ng StringRef str; 537ca32bd4SJez Ng }; 547ca32bd4SJez Ng 55b9457330SJez Ng struct MapInfo { 56b9457330SJez Ng SmallVector<InputFile *> files; 57b9457330SJez Ng SmallVector<Defined *> deadSymbols; 587ca32bd4SJez Ng DenseMap<const OutputSection *, 597ca32bd4SJez Ng SmallVector<std::pair<uint64_t /*addr*/, CStringInfo>>> 607ca32bd4SJez Ng liveCStringsForSection; 617ca32bd4SJez Ng SmallVector<CStringInfo> deadCStrings; 62b9457330SJez Ng }; 63b9457330SJez Ng 64b9457330SJez Ng static MapInfo gatherMapInfo() { 65b9457330SJez Ng MapInfo info; 66aa288fd9SJez Ng for (InputFile *file : inputFiles) { 67aa288fd9SJez Ng bool isReferencedFile = false; 68aa288fd9SJez Ng 69b9457330SJez Ng if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) { 707ca32bd4SJez Ng uint32_t fileIndex = info.files.size() + 1; 717ca32bd4SJez Ng 727ca32bd4SJez Ng // Gather the dead symbols. We don't have to bother with the live ones 737ca32bd4SJez Ng // because we will pick them up as we iterate over the OutputSections 747ca32bd4SJez Ng // later. 75b9457330SJez Ng for (Symbol *sym : file->symbols) { 76a5645513SNico Weber if (auto *d = dyn_cast_or_null<Defined>(sym)) 777ca32bd4SJez Ng // Only emit the prevailing definition of a symbol. Also, don't emit 787ca32bd4SJez Ng // the symbol if it is part of a cstring section (we use the literal 797ca32bd4SJez Ng // value instead, similar to ld64) 802a3a79ceSalx32 if (d->isec() && d->getFile() == file && 812a3a79ceSalx32 !isa<CStringInputSection>(d->isec())) { 827ca32bd4SJez Ng isReferencedFile = true; 837ca32bd4SJez Ng if (!d->isLive()) 84b9457330SJez Ng info.deadSymbols.push_back(d); 85b9457330SJez Ng } 86ed8bff13Scaoming.roy } 87ed8bff13Scaoming.roy 887ca32bd4SJez Ng // Gather all the cstrings (both live and dead). A CString(Output)Section 897ca32bd4SJez Ng // doesn't provide us a way of figuring out which InputSections its 907ca32bd4SJez Ng // cstring contents came from, so we need to build up that mapping here. 917ca32bd4SJez Ng for (const Section *sec : file->sections) { 927ca32bd4SJez Ng for (const Subsection &subsec : sec->subsections) { 937ca32bd4SJez Ng if (auto isec = dyn_cast<CStringInputSection>(subsec.isec)) { 947ca32bd4SJez Ng auto &liveCStrings = info.liveCStringsForSection[isec->parent]; 957ca32bd4SJez Ng for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) { 967ca32bd4SJez Ng if (piece.live) 977ca32bd4SJez Ng liveCStrings.push_back({isec->parent->addr + piece.outSecOff, 987ca32bd4SJez Ng {fileIndex, isec->getStringRef(i)}}); 997ca32bd4SJez Ng else 1007ca32bd4SJez Ng info.deadCStrings.push_back({fileIndex, isec->getStringRef(i)}); 1017ca32bd4SJez Ng isReferencedFile = true; 1027ca32bd4SJez Ng } 1037ca32bd4SJez Ng } else { 10438d6202aSMuhammad Omair Javaid break; 10538d6202aSMuhammad Omair Javaid } 10638d6202aSMuhammad Omair Javaid } 1077ca32bd4SJez Ng } 108aa288fd9SJez Ng } else if (const auto *dylibFile = dyn_cast<DylibFile>(file)) { 109aa288fd9SJez Ng isReferencedFile = dylibFile->isReferenced(); 110aa288fd9SJez Ng } 11138d6202aSMuhammad Omair Javaid 1127ca32bd4SJez Ng if (isReferencedFile) 1137ca32bd4SJez Ng info.files.push_back(file); 1147ca32bd4SJez Ng } 1157ca32bd4SJez Ng 1167ca32bd4SJez Ng // cstrings are not stored in sorted order in their OutputSections, so we sort 1177ca32bd4SJez Ng // them here. 1187ca32bd4SJez Ng for (auto &liveCStrings : info.liveCStringsForSection) 1197ca32bd4SJez Ng parallelSort(liveCStrings.second, [](const auto &p1, const auto &p2) { 1207ca32bd4SJez Ng return p1.first < p2.first; 1217ca32bd4SJez Ng }); 1227ca32bd4SJez Ng return info; 12338d6202aSMuhammad Omair Javaid } 12438d6202aSMuhammad Omair Javaid 1255b21395cSJez Ng // We use this instead of `toString(const InputFile *)` as we don't want to 1265b21395cSJez Ng // include the dylib install name in our output. 1275b21395cSJez Ng static void printFileName(raw_fd_ostream &os, const InputFile *f) { 1285b21395cSJez Ng if (f->archiveName.empty()) 1295b21395cSJez Ng os << f->getName(); 1305b21395cSJez Ng else 1315b21395cSJez Ng os << f->archiveName << "(" << path::filename(f->getName()) + ")"; 1325b21395cSJez Ng } 1335b21395cSJez Ng 134aa288fd9SJez Ng // For printing the contents of the __stubs and __la_symbol_ptr sections. 1355b21395cSJez Ng static void printStubsEntries( 136aa288fd9SJez Ng raw_fd_ostream &os, 137aa288fd9SJez Ng const DenseMap<lld::macho::InputFile *, uint32_t> &readerToFileOrdinal, 138aa288fd9SJez Ng const OutputSection *osec, size_t entrySize) { 139aa288fd9SJez Ng for (const Symbol *sym : in.stubs->getEntries()) 140aa288fd9SJez Ng os << format("0x%08llX\t0x%08zX\t[%3u] %s\n", 141aa288fd9SJez Ng osec->addr + sym->stubsIndex * entrySize, entrySize, 142aa288fd9SJez Ng readerToFileOrdinal.lookup(sym->getFile()), 143aa288fd9SJez Ng sym->getName().str().data()); 144aa288fd9SJez Ng } 145aa288fd9SJez Ng 1465b21395cSJez Ng static void printNonLazyPointerSection(raw_fd_ostream &os, 147aa288fd9SJez Ng NonLazyPointerSectionBase *osec) { 148aa288fd9SJez Ng // ld64 considers stubs to belong to particular files, but considers GOT 149aa288fd9SJez Ng // entries to be linker-synthesized. Not sure why they made that decision, but 150aa288fd9SJez Ng // I think we can follow suit unless there's demand for better symbol-to-file 151aa288fd9SJez Ng // associations. 152aa288fd9SJez Ng for (const Symbol *sym : osec->getEntries()) 153aa288fd9SJez Ng os << format("0x%08llX\t0x%08zX\t[ 0] non-lazy-pointer-to-local: %s\n", 154aa288fd9SJez Ng osec->addr + sym->gotIndex * target->wordSize, 155aa288fd9SJez Ng target->wordSize, sym->getName().str().data()); 156aa288fd9SJez Ng } 157aa288fd9SJez Ng 1582a3a79ceSalx32 static uint64_t getSymSizeForMap(Defined *sym) { 159d1756165Salx32 if (sym->identicalCodeFoldingKind == Symbol::ICFFoldKind::Body) 1602a3a79ceSalx32 return 0; 1612a3a79ceSalx32 return sym->size; 1622a3a79ceSalx32 } 1632a3a79ceSalx32 164ed8bff13Scaoming.roy void macho::writeMapFile() { 165ed8bff13Scaoming.roy if (config->mapFile.empty()) 166ed8bff13Scaoming.roy return; 167ed8bff13Scaoming.roy 1684bcaafebSJez Ng TimeTraceScope timeScope("Write map file"); 1694bcaafebSJez Ng 170ed8bff13Scaoming.roy // Open a map file for writing. 171ed8bff13Scaoming.roy std::error_code ec; 172ed8bff13Scaoming.roy raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); 173ed8bff13Scaoming.roy if (ec) { 174ed8bff13Scaoming.roy error("cannot open " + config->mapFile + ": " + ec.message()); 175ed8bff13Scaoming.roy return; 176ed8bff13Scaoming.roy } 177ed8bff13Scaoming.roy 178ed8bff13Scaoming.roy os << format("# Path: %s\n", config->outputFile.str().c_str()); 179ed4a4e33SJez Ng os << format("# Arch: %s\n", 180ed4a4e33SJez Ng getArchitectureName(config->arch()).str().c_str()); 181ed8bff13Scaoming.roy 182b9457330SJez Ng MapInfo info = gatherMapInfo(); 183b9457330SJez Ng 184ed8bff13Scaoming.roy os << "# Object files:\n"; 185ed8bff13Scaoming.roy os << format("[%3u] %s\n", 0, (const char *)"linker synthesized"); 186ed8bff13Scaoming.roy uint32_t fileIndex = 1; 187ed8bff13Scaoming.roy DenseMap<lld::macho::InputFile *, uint32_t> readerToFileOrdinal; 188b9457330SJez Ng for (InputFile *file : info.files) { 1895b21395cSJez Ng os << format("[%3u] ", fileIndex); 1905b21395cSJez Ng printFileName(os, file); 1915b21395cSJez Ng os << "\n"; 192ed8bff13Scaoming.roy readerToFileOrdinal[file] = fileIndex++; 193ed8bff13Scaoming.roy } 194ed8bff13Scaoming.roy 195ed8bff13Scaoming.roy os << "# Sections:\n"; 196ed8bff13Scaoming.roy os << "# Address\tSize \tSegment\tSection\n"; 197ed8bff13Scaoming.roy for (OutputSegment *seg : outputSegments) 198ed8bff13Scaoming.roy for (OutputSection *osec : seg->getSections()) { 199ed8bff13Scaoming.roy if (osec->isHidden()) 200ed8bff13Scaoming.roy continue; 201ed8bff13Scaoming.roy 202ed8bff13Scaoming.roy os << format("0x%08llX\t0x%08llX\t%s\t%s\n", osec->addr, osec->getSize(), 203ed8bff13Scaoming.roy seg->name.str().c_str(), osec->name.str().c_str()); 204ed8bff13Scaoming.roy } 205ed8bff13Scaoming.roy 206*95d21f60Salx32 // Helper lambda that prints all symbols from one ConcatInputSection. 207*95d21f60Salx32 auto printOne = [&](const ConcatInputSection *isec) { 208742a82a7Salx32 for (Defined *sym : isec->symbols) { 209*95d21f60Salx32 if (!(isPrivateLabel(sym->getName()) && getSymSizeForMap(sym) == 0)) { 2107ca32bd4SJez Ng os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(), 2112a3a79ceSalx32 getSymSizeForMap(sym), 212*95d21f60Salx32 readerToFileOrdinal.lookup(sym->getFile()), 2137ca32bd4SJez Ng sym->getName().str().data()); 2147ca32bd4SJez Ng } 215742a82a7Salx32 } 216742a82a7Salx32 }; 217*95d21f60Salx32 // Shared function to print one or two arrays of ConcatInputSection in 218*95d21f60Salx32 // ascending outSecOff order. The second array is optional; if provided, we 219*95d21f60Salx32 // interleave the printing in sorted order without allocating a merged temp 220*95d21f60Salx32 // array. 221*95d21f60Salx32 auto printIsecArrSyms = [&](ArrayRef<ConcatInputSection *> arr1, 222*95d21f60Salx32 ArrayRef<ConcatInputSection *> arr2 = {}) { 223*95d21f60Salx32 // Print both arrays in sorted order, interleaving as necessary. 224*95d21f60Salx32 while (!arr1.empty() || !arr2.empty()) { 225*95d21f60Salx32 if (!arr1.empty() && (arr2.empty() || arr1.front()->outSecOff <= 226*95d21f60Salx32 arr2.front()->outSecOff)) { 227*95d21f60Salx32 printOne(arr1.front()); 228*95d21f60Salx32 arr1 = arr1.drop_front(); 229*95d21f60Salx32 } else if (!arr2.empty()) { 230*95d21f60Salx32 printOne(arr2.front()); 231*95d21f60Salx32 arr2 = arr2.drop_front(); 232*95d21f60Salx32 } 233*95d21f60Salx32 } 234*95d21f60Salx32 }; 235742a82a7Salx32 236742a82a7Salx32 os << "# Symbols:\n"; 237742a82a7Salx32 os << "# Address\tSize \tFile Name\n"; 238742a82a7Salx32 for (const OutputSegment *seg : outputSegments) { 239742a82a7Salx32 for (const OutputSection *osec : seg->getSections()) { 240162814a7Salx32 if (auto *textOsec = dyn_cast<TextOutputSection>(osec)) { 241*95d21f60Salx32 printIsecArrSyms(textOsec->inputs, textOsec->getThunks()); 242162814a7Salx32 } else if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) { 243742a82a7Salx32 printIsecArrSyms(concatOsec->inputs); 2447ca32bd4SJez Ng } else if (osec == in.cStringSection || osec == in.objcMethnameSection) { 2457ca32bd4SJez Ng const auto &liveCStrings = info.liveCStringsForSection.lookup(osec); 2467ca32bd4SJez Ng uint64_t lastAddr = 0; // strings will never start at address 0, so this 2477ca32bd4SJez Ng // is a sentinel value 2487ca32bd4SJez Ng for (const auto &[addr, info] : liveCStrings) { 2497ca32bd4SJez Ng uint64_t size = 0; 2507ca32bd4SJez Ng if (addr != lastAddr) 2517ca32bd4SJez Ng size = info.str.size() + 1; // include null terminator 2527ca32bd4SJez Ng lastAddr = addr; 2537ca32bd4SJez Ng os << format("0x%08llX\t0x%08llX\t[%3u] literal string: ", addr, size, 2547ca32bd4SJez Ng info.fileIndex); 2557ca32bd4SJez Ng os.write_escaped(info.str) << "\n"; 2567ca32bd4SJez Ng } 25741f90e97SJez Ng } else if (osec == (void *)in.unwindInfo) { 25841f90e97SJez Ng os << format("0x%08llX\t0x%08llX\t[ 0] compact unwind info\n", 25941f90e97SJez Ng osec->addr, osec->getSize()); 260aa288fd9SJez Ng } else if (osec == in.stubs) { 261aa288fd9SJez Ng printStubsEntries(os, readerToFileOrdinal, osec, target->stubSize); 262aa288fd9SJez Ng } else if (osec == in.lazyPointers) { 263aa288fd9SJez Ng printStubsEntries(os, readerToFileOrdinal, osec, target->wordSize); 264aa288fd9SJez Ng } else if (osec == in.stubHelper) { 265aa288fd9SJez Ng // yes, ld64 calls it "helper helper"... 266aa288fd9SJez Ng os << format("0x%08llX\t0x%08llX\t[ 0] helper helper\n", osec->addr, 267aa288fd9SJez Ng osec->getSize()); 268aa288fd9SJez Ng } else if (osec == in.got) { 269aa288fd9SJez Ng printNonLazyPointerSection(os, in.got); 270aa288fd9SJez Ng } else if (osec == in.tlvPointers) { 271aa288fd9SJez Ng printNonLazyPointerSection(os, in.tlvPointers); 272742a82a7Salx32 } else if (osec == in.objcMethList) { 273742a82a7Salx32 printIsecArrSyms(in.objcMethList->getInputs()); 2747ca32bd4SJez Ng } 2757ca32bd4SJez Ng // TODO print other synthetic sections 2767ca32bd4SJez Ng } 277ed8bff13Scaoming.roy } 278ed8bff13Scaoming.roy 27942208433SRoger Kim if (config->deadStrip) { 28042208433SRoger Kim os << "# Dead Stripped Symbols:\n"; 281bdd0cec5SJez Ng os << "# \tSize \tFile Name\n"; 282b9457330SJez Ng for (Defined *sym : info.deadSymbols) { 28342208433SRoger Kim assert(!sym->isLive()); 2842a3a79ceSalx32 os << format("<<dead>>\t0x%08llX\t[%3u] %s\n", getSymSizeForMap(sym), 285bdd0cec5SJez Ng readerToFileOrdinal[sym->getFile()], 2867ca32bd4SJez Ng sym->getName().str().data()); 2877ca32bd4SJez Ng } 2887ca32bd4SJez Ng for (CStringInfo &cstrInfo : info.deadCStrings) { 2897c7e39dbSDavid Spickett os << format("<<dead>>\t0x%08zX\t[%3u] literal string: ", 2907ca32bd4SJez Ng cstrInfo.str.size() + 1, cstrInfo.fileIndex); 2917ca32bd4SJez Ng os.write_escaped(cstrInfo.str) << "\n"; 29242208433SRoger Kim } 29342208433SRoger Kim } 294ed8bff13Scaoming.roy } 295