16f24fdb6SPeter Collingbourne //===- MapFile.cpp --------------------------------------------------------===// 26f24fdb6SPeter Collingbourne // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 66f24fdb6SPeter Collingbourne // 76f24fdb6SPeter Collingbourne //===----------------------------------------------------------------------===// 86f24fdb6SPeter Collingbourne // 9b91905a2SSylvain Audi // This file implements the /map option in the same format as link.exe 10b91905a2SSylvain Audi // (based on observations) 116f24fdb6SPeter Collingbourne // 12b91905a2SSylvain Audi // Header (program name, timestamp info, preferred load address) 136f24fdb6SPeter Collingbourne // 14b91905a2SSylvain Audi // Section list (Start = Section index:Base address): 15b91905a2SSylvain Audi // Start Length Name Class 16b91905a2SSylvain Audi // 0001:00001000 00000015H .text CODE 17b91905a2SSylvain Audi // 18b91905a2SSylvain Audi // Symbols list: 19b91905a2SSylvain Audi // Address Publics by Value Rva + Base Lib:Object 20b91905a2SSylvain Audi // 0001:00001000 main 0000000140001000 main.obj 21b91905a2SSylvain Audi // 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj 22b91905a2SSylvain Audi // 23b91905a2SSylvain Audi // entry point at 0001:00000360 24b91905a2SSylvain Audi // 25b91905a2SSylvain Audi // Static symbols 26b91905a2SSylvain Audi // 27b91905a2SSylvain Audi // 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj 286f24fdb6SPeter Collingbourne //===----------------------------------------------------------------------===// 296f24fdb6SPeter Collingbourne 306f24fdb6SPeter Collingbourne #include "MapFile.h" 316f7483b1SAmy Huang #include "COFFLinkerContext.h" 323969acf8SRui Ueyama #include "SymbolTable.h" 336f24fdb6SPeter Collingbourne #include "Symbols.h" 346f24fdb6SPeter Collingbourne #include "Writer.h" 35b8a59c8aSBob Haarman #include "lld/Common/ErrorHandler.h" 36b91905a2SSylvain Audi #include "lld/Common/Timer.h" 37932f0276SReid Kleckner #include "llvm/Support/Parallel.h" 38b91905a2SSylvain Audi #include "llvm/Support/Path.h" 39356139bdSAlexandre Ganea #include "llvm/Support/TimeProfiler.h" 40543731f9SRui Ueyama #include "llvm/Support/raw_ostream.h" 416f24fdb6SPeter Collingbourne 426f24fdb6SPeter Collingbourne using namespace llvm; 436f24fdb6SPeter Collingbourne using namespace llvm::object; 448a310f40SReid Kleckner using namespace lld; 458a310f40SReid Kleckner using namespace lld::coff; 466f24fdb6SPeter Collingbourne 47b91905a2SSylvain Audi // Print out the first two columns of a line. 48b91905a2SSylvain Audi static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) { 49b91905a2SSylvain Audi os << format(" %04x:%08llx", sec, addr); 506f24fdb6SPeter Collingbourne } 516f24fdb6SPeter Collingbourne 52b91905a2SSylvain Audi // Write the time stamp with the format used by link.exe 53b91905a2SSylvain Audi // It seems identical to strftime with "%c" on msvc build, but we need a 54b91905a2SSylvain Audi // locale-agnostic version. 55b91905a2SSylvain Audi static void writeFormattedTimestamp(raw_ostream &os, time_t tds) { 56b91905a2SSylvain Audi constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed", 57b91905a2SSylvain Audi "Thu", "Fri", "Sat"}; 58b91905a2SSylvain Audi constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr", 59b91905a2SSylvain Audi "May", "Jun", "Jul", "Aug", 60b91905a2SSylvain Audi "Sep", "Oct", "Nov", "Dec"}; 61b91905a2SSylvain Audi tm *time = localtime(&tds); 62b91905a2SSylvain Audi os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday], 63b91905a2SSylvain Audi months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min, 64b91905a2SSylvain Audi time->tm_sec, time->tm_year + 1900); 656f24fdb6SPeter Collingbourne } 666f24fdb6SPeter Collingbourne 675a58b19fSAmy Huang static void sortUniqueSymbols(std::vector<Defined *> &syms, 685a58b19fSAmy Huang uint64_t imageBase) { 69b91905a2SSylvain Audi // Build helper vector 70b91905a2SSylvain Audi using SortEntry = std::pair<Defined *, size_t>; 71b91905a2SSylvain Audi std::vector<SortEntry> v; 72b91905a2SSylvain Audi v.resize(syms.size()); 73b91905a2SSylvain Audi for (size_t i = 0, e = syms.size(); i < e; ++i) 74b91905a2SSylvain Audi v[i] = SortEntry(syms[i], i); 753969acf8SRui Ueyama 76b91905a2SSylvain Audi // Remove duplicate symbol pointers 77b91905a2SSylvain Audi parallelSort(v, std::less<SortEntry>()); 78b91905a2SSylvain Audi auto end = std::unique(v.begin(), v.end(), 79b91905a2SSylvain Audi [](const SortEntry &a, const SortEntry &b) { 80b91905a2SSylvain Audi return a.first == b.first; 813969acf8SRui Ueyama }); 82b91905a2SSylvain Audi v.erase(end, v.end()); 83b91905a2SSylvain Audi 84b91905a2SSylvain Audi // Sort by RVA then original order 855a58b19fSAmy Huang parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) { 865a58b19fSAmy Huang // Add config.imageBase to avoid comparing "negative" RVAs. 87b91905a2SSylvain Audi // This can happen with symbols of Absolute kind 885a58b19fSAmy Huang uint64_t rvaa = imageBase + a.first->getRVA(); 895a58b19fSAmy Huang uint64_t rvab = imageBase + b.first->getRVA(); 90b91905a2SSylvain Audi return rvaa < rvab || (rvaa == rvab && a.second < b.second); 91b91905a2SSylvain Audi }); 92b91905a2SSylvain Audi 93b91905a2SSylvain Audi syms.resize(v.size()); 94b91905a2SSylvain Audi for (size_t i = 0, e = v.size(); i < e; ++i) 95b91905a2SSylvain Audi syms[i] = v[i].first; 963969acf8SRui Ueyama } 97b91905a2SSylvain Audi 98b91905a2SSylvain Audi // Returns the lists of all symbols that we want to print out. 996f7483b1SAmy Huang static void getSymbols(const COFFLinkerContext &ctx, 1006f7483b1SAmy Huang std::vector<Defined *> &syms, 101a2fd05adSAmy Huang std::vector<Defined *> &staticSyms) { 102b91905a2SSylvain Audi 1036f7483b1SAmy Huang for (ObjFile *file : ctx.objFileInstances) 104b91905a2SSylvain Audi for (Symbol *b : file->getSymbols()) { 105b91905a2SSylvain Audi if (!b || !b->isLive()) 106b91905a2SSylvain Audi continue; 107b91905a2SSylvain Audi if (auto *sym = dyn_cast<DefinedCOFF>(b)) { 108b91905a2SSylvain Audi COFFSymbolRef symRef = sym->getCOFFSymbol(); 109b91905a2SSylvain Audi if (!symRef.isSectionDefinition() && 110b91905a2SSylvain Audi symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) { 111b91905a2SSylvain Audi if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) 112b91905a2SSylvain Audi staticSyms.push_back(sym); 113b91905a2SSylvain Audi else 114b91905a2SSylvain Audi syms.push_back(sym); 115b91905a2SSylvain Audi } 116b91905a2SSylvain Audi } else if (auto *sym = dyn_cast<Defined>(b)) { 117b91905a2SSylvain Audi syms.push_back(sym); 118b91905a2SSylvain Audi } 119b91905a2SSylvain Audi } 120b91905a2SSylvain Audi 1216f7483b1SAmy Huang for (ImportFile *file : ctx.importFileInstances) { 122b91905a2SSylvain Audi if (!file->live) 123b91905a2SSylvain Audi continue; 124b91905a2SSylvain Audi 125912e821aSJacek Caban if (file->impSym) 126912e821aSJacek Caban syms.push_back(file->impSym); 127912e821aSJacek Caban if (file->thunkSym && file->thunkSym->isLive()) 12886d2abefSJacek Caban syms.push_back(file->thunkSym); 129486f790dSJacek Caban if (file->auxThunkSym && file->auxThunkSym->isLive()) 130486f790dSJacek Caban syms.push_back(file->auxThunkSym); 131486f790dSJacek Caban if (file->impchkThunk) 132486f790dSJacek Caban syms.push_back(file->impchkThunk->sym); 133486f790dSJacek Caban if (file->impECSym) 134486f790dSJacek Caban syms.push_back(file->impECSym); 135486f790dSJacek Caban if (file->auxImpCopySym) 136486f790dSJacek Caban syms.push_back(file->auxImpCopySym); 137b91905a2SSylvain Audi } 138b91905a2SSylvain Audi 1395a58b19fSAmy Huang sortUniqueSymbols(syms, ctx.config.imageBase); 1405a58b19fSAmy Huang sortUniqueSymbols(staticSyms, ctx.config.imageBase); 1416f24fdb6SPeter Collingbourne } 1426f24fdb6SPeter Collingbourne 1433969acf8SRui Ueyama // Construct a map from symbols to their stringified representations. 144b91905a2SSylvain Audi static DenseMap<Defined *, std::string> 1456f7483b1SAmy Huang getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) { 146136d27abSRui Ueyama std::vector<std::string> str(syms.size()); 1477effcbdaSNico Weber parallelFor((size_t)0, syms.size(), [&](size_t i) { 148136d27abSRui Ueyama raw_string_ostream os(str[i]); 149b91905a2SSylvain Audi Defined *sym = syms[i]; 150b91905a2SSylvain Audi 151b91905a2SSylvain Audi uint16_t sectionIdx = 0; 152b91905a2SSylvain Audi uint64_t address = 0; 153b91905a2SSylvain Audi SmallString<128> fileDescr; 154b91905a2SSylvain Audi 155b91905a2SSylvain Audi if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) { 156b91905a2SSylvain Audi address = absSym->getVA(); 157b91905a2SSylvain Audi fileDescr = "<absolute>"; 158b91905a2SSylvain Audi } else if (isa<DefinedSynthetic>(sym)) { 159b91905a2SSylvain Audi fileDescr = "<linker-defined>"; 160b91905a2SSylvain Audi } else if (isa<DefinedCommon>(sym)) { 161b91905a2SSylvain Audi fileDescr = "<common>"; 162b91905a2SSylvain Audi } else if (Chunk *chunk = sym->getChunk()) { 163b91905a2SSylvain Audi address = sym->getRVA(); 1646f7483b1SAmy Huang if (OutputSection *sec = ctx.getOutputSection(chunk)) 165b91905a2SSylvain Audi address -= sec->header.VirtualAddress; 166b91905a2SSylvain Audi 167b91905a2SSylvain Audi sectionIdx = chunk->getOutputSectionIdx(); 168b91905a2SSylvain Audi 169b91905a2SSylvain Audi InputFile *file; 170b91905a2SSylvain Audi if (auto *impSym = dyn_cast<DefinedImportData>(sym)) 171b91905a2SSylvain Audi file = impSym->file; 172b91905a2SSylvain Audi else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym)) 173b91905a2SSylvain Audi file = thunkSym->wrappedSym->file; 174b91905a2SSylvain Audi else 175b91905a2SSylvain Audi file = sym->getFile(); 176b91905a2SSylvain Audi 177b91905a2SSylvain Audi if (file) { 178b91905a2SSylvain Audi if (!file->parentName.empty()) { 179b91905a2SSylvain Audi fileDescr = sys::path::filename(file->parentName); 180b91905a2SSylvain Audi sys::path::replace_extension(fileDescr, ""); 181b91905a2SSylvain Audi fileDescr += ":"; 182b91905a2SSylvain Audi } 183b91905a2SSylvain Audi fileDescr += sys::path::filename(file->getName()); 184b91905a2SSylvain Audi } 185b91905a2SSylvain Audi } 186b91905a2SSylvain Audi writeHeader(os, sectionIdx, address); 187b91905a2SSylvain Audi os << " "; 188b91905a2SSylvain Audi os << left_justify(sym->getName(), 26); 189b91905a2SSylvain Audi os << " "; 1905a58b19fSAmy Huang os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16); 191b91905a2SSylvain Audi if (!fileDescr.empty()) { 192b91905a2SSylvain Audi os << " "; // FIXME : Handle "f" and "i" flags sometimes generated 193b91905a2SSylvain Audi // by link.exe in those spaces 194b91905a2SSylvain Audi os << fileDescr; 195b91905a2SSylvain Audi } 1963969acf8SRui Ueyama }); 1976f24fdb6SPeter Collingbourne 198b91905a2SSylvain Audi DenseMap<Defined *, std::string> ret; 199136d27abSRui Ueyama for (size_t i = 0, e = syms.size(); i < e; ++i) 200136d27abSRui Ueyama ret[syms[i]] = std::move(str[i]); 201136d27abSRui Ueyama return ret; 2026f24fdb6SPeter Collingbourne } 2036f24fdb6SPeter Collingbourne 2046f7483b1SAmy Huang void lld::coff::writeMapFile(COFFLinkerContext &ctx) { 2055a58b19fSAmy Huang if (ctx.config.mapFile.empty()) 2066f24fdb6SPeter Collingbourne return; 2076f24fdb6SPeter Collingbourne 208356139bdSAlexandre Ganea llvm::TimeTraceScope timeScope("Map file"); 209136d27abSRui Ueyama std::error_code ec; 2105a58b19fSAmy Huang raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None); 211136d27abSRui Ueyama if (ec) 2128b844de3SFangrui Song Fatal(ctx) << "cannot open " << ctx.config.mapFile << ": " << ec.message(); 2133969acf8SRui Ueyama 2146f7483b1SAmy Huang ScopedTimer t1(ctx.totalMapTimer); 215b91905a2SSylvain Audi 2163969acf8SRui Ueyama // Collect symbol info that we want to print out. 2176f7483b1SAmy Huang ScopedTimer t2(ctx.symbolGatherTimer); 218b91905a2SSylvain Audi std::vector<Defined *> syms; 219b91905a2SSylvain Audi std::vector<Defined *> staticSyms; 2206f7483b1SAmy Huang getSymbols(ctx, syms, staticSyms); 221b91905a2SSylvain Audi t2.stop(); 2223969acf8SRui Ueyama 2236f7483b1SAmy Huang ScopedTimer t3(ctx.symbolStringsTimer); 2246f7483b1SAmy Huang DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms); 2256f7483b1SAmy Huang DenseMap<Defined *, std::string> staticSymStr = 2266f7483b1SAmy Huang getSymbolStrings(ctx, staticSyms); 227b91905a2SSylvain Audi t3.stop(); 2283969acf8SRui Ueyama 2296f7483b1SAmy Huang ScopedTimer t4(ctx.writeTimer); 2305a58b19fSAmy Huang SmallString<128> AppName = sys::path::filename(ctx.config.outputFile); 231b91905a2SSylvain Audi sys::path::replace_extension(AppName, ""); 232b91905a2SSylvain Audi 233b91905a2SSylvain Audi // Print out the file header 234b91905a2SSylvain Audi os << " " << AppName << "\n"; 235b91905a2SSylvain Audi os << "\n"; 236b91905a2SSylvain Audi 2375a58b19fSAmy Huang os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8) 2385a58b19fSAmy Huang << " ("; 2395a58b19fSAmy Huang if (ctx.config.repro) { 240b91905a2SSylvain Audi os << "Repro mode"; 241b91905a2SSylvain Audi } else { 2425a58b19fSAmy Huang writeFormattedTimestamp(os, ctx.config.timestamp); 243b91905a2SSylvain Audi } 244b91905a2SSylvain Audi os << ")\n"; 245b91905a2SSylvain Audi 246b91905a2SSylvain Audi os << "\n"; 247b91905a2SSylvain Audi os << " Preferred load address is " 2485a58b19fSAmy Huang << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n"; 249b91905a2SSylvain Audi os << "\n"; 250b91905a2SSylvain Audi 251b91905a2SSylvain Audi // Print out section table. 252b91905a2SSylvain Audi os << " Start Length Name Class\n"; 253b91905a2SSylvain Audi 2546f7483b1SAmy Huang for (OutputSection *sec : ctx.outputSections) { 255b91905a2SSylvain Audi // Merge display of chunks with same sectionName 256b91905a2SSylvain Audi std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges; 257136d27abSRui Ueyama for (Chunk *c : sec->chunks) { 258136d27abSRui Ueyama auto *sc = dyn_cast<SectionChunk>(c); 259136d27abSRui Ueyama if (!sc) 2603969acf8SRui Ueyama continue; 2613969acf8SRui Ueyama 262b91905a2SSylvain Audi if (ChunkRanges.empty() || 263b91905a2SSylvain Audi c->getSectionName() != ChunkRanges.back().first->getSectionName()) { 264b91905a2SSylvain Audi ChunkRanges.emplace_back(sc, sc); 265b91905a2SSylvain Audi } else { 266b91905a2SSylvain Audi ChunkRanges.back().second = sc; 267b91905a2SSylvain Audi } 268b91905a2SSylvain Audi } 269b91905a2SSylvain Audi 270b91905a2SSylvain Audi const bool isCodeSection = 271b91905a2SSylvain Audi (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) && 272b91905a2SSylvain Audi (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) && 273b91905a2SSylvain Audi (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE); 274b91905a2SSylvain Audi StringRef SectionClass = (isCodeSection ? "CODE" : "DATA"); 275b91905a2SSylvain Audi 276b91905a2SSylvain Audi for (auto &cr : ChunkRanges) { 277b91905a2SSylvain Audi size_t size = 278b91905a2SSylvain Audi cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA(); 279b91905a2SSylvain Audi 280b91905a2SSylvain Audi auto address = cr.first->getRVA() - sec->header.VirtualAddress; 281b91905a2SSylvain Audi writeHeader(os, sec->sectionIndex, address); 282b91905a2SSylvain Audi os << " " << format_hex_no_prefix(size, 8) << "H"; 283b91905a2SSylvain Audi os << " " << left_justify(cr.first->getSectionName(), 23); 284b91905a2SSylvain Audi os << " " << SectionClass; 285b91905a2SSylvain Audi os << '\n'; 286b91905a2SSylvain Audi } 287b91905a2SSylvain Audi } 288b91905a2SSylvain Audi 289b91905a2SSylvain Audi // Print out the symbols table (without static symbols) 290b91905a2SSylvain Audi os << "\n"; 291b91905a2SSylvain Audi os << " Address Publics by Value Rva+Base" 292b91905a2SSylvain Audi " Lib:Object\n"; 293b91905a2SSylvain Audi os << "\n"; 294b91905a2SSylvain Audi for (Defined *sym : syms) 295136d27abSRui Ueyama os << symStr[sym] << '\n'; 296b91905a2SSylvain Audi 297b91905a2SSylvain Audi // Print out the entry point. 298b91905a2SSylvain Audi os << "\n"; 299b91905a2SSylvain Audi 300b91905a2SSylvain Audi uint16_t entrySecIndex = 0; 301b91905a2SSylvain Audi uint64_t entryAddress = 0; 302b91905a2SSylvain Audi 3035a58b19fSAmy Huang if (!ctx.config.noEntry) { 304d004947aSJacek Caban Defined *entry = dyn_cast_or_null<Defined>(ctx.symtab.entry); 305b91905a2SSylvain Audi if (entry) { 306b91905a2SSylvain Audi Chunk *chunk = entry->getChunk(); 307b91905a2SSylvain Audi entrySecIndex = chunk->getOutputSectionIdx(); 308b91905a2SSylvain Audi entryAddress = 3096f7483b1SAmy Huang entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress; 3103969acf8SRui Ueyama } 3113969acf8SRui Ueyama } 312b91905a2SSylvain Audi os << " entry point at "; 313b91905a2SSylvain Audi os << format("%04x:%08llx", entrySecIndex, entryAddress); 314b91905a2SSylvain Audi os << "\n"; 315b91905a2SSylvain Audi 316b91905a2SSylvain Audi // Print out the static symbols 317b91905a2SSylvain Audi os << "\n"; 318b91905a2SSylvain Audi os << " Static symbols\n"; 319b91905a2SSylvain Audi os << "\n"; 320b91905a2SSylvain Audi for (Defined *sym : staticSyms) 321b91905a2SSylvain Audi os << staticSymStr[sym] << '\n'; 322b91905a2SSylvain Audi 3237b317563SPengxuan Zheng // Print out the exported functions 3245a58b19fSAmy Huang if (ctx.config.mapInfo) { 3257b317563SPengxuan Zheng os << "\n"; 3267b317563SPengxuan Zheng os << " Exports\n"; 3277b317563SPengxuan Zheng os << "\n"; 3287b317563SPengxuan Zheng os << " ordinal name\n\n"; 329*455b3d6dSJacek Caban for (Export &e : ctx.symtab.exports) { 3307b317563SPengxuan Zheng os << format(" %7d", e.ordinal) << " " << e.name << "\n"; 3317b317563SPengxuan Zheng if (!e.extName.empty() && e.extName != e.name) 3327b317563SPengxuan Zheng os << " exported name: " << e.extName << "\n"; 3337b317563SPengxuan Zheng } 3347b317563SPengxuan Zheng } 3357b317563SPengxuan Zheng 336b91905a2SSylvain Audi t4.stop(); 337b91905a2SSylvain Audi t1.stop(); 3386f24fdb6SPeter Collingbourne } 339