1275970Scy//===- MapFile.cpp --------------------------------------------------------===// 2275970Scy// 3275970Scy// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4275970Scy// See https://llvm.org/LICENSE.txt for license information. 5275970Scy// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6275970Scy// 7275970Scy//===----------------------------------------------------------------------===// 8275970Scy// 9275970Scy// This file implements the /map option in the same format as link.exe 10275970Scy// (based on observations) 11275970Scy// 12275970Scy// Header (program name, timestamp info, preferred load address) 13275970Scy// 14275970Scy// Section list (Start = Section index:Base address): 15275970Scy// Start Length Name Class 16275970Scy// 0001:00001000 00000015H .text CODE 17275970Scy// 18275970Scy// Symbols list: 19275970Scy// Address Publics by Value Rva + Base Lib:Object 20275970Scy// 0001:00001000 main 0000000140001000 main.obj 21275970Scy// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj 22275970Scy// 23275970Scy// entry point at 0001:00000360 24275970Scy// 25275970Scy// Static symbols 26275970Scy// 27275970Scy// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj 28275970Scy//===----------------------------------------------------------------------===// 29275970Scy 30275970Scy#include "MapFile.h" 31275970Scy#include "COFFLinkerContext.h" 32275970Scy#include "SymbolTable.h" 33275970Scy#include "Symbols.h" 34275970Scy#include "Writer.h" 35275970Scy#include "lld/Common/ErrorHandler.h" 36275970Scy#include "lld/Common/Timer.h" 37275970Scy#include "llvm/Support/Parallel.h" 38275970Scy#include "llvm/Support/Path.h" 39275970Scy#include "llvm/Support/raw_ostream.h" 40275970Scy 41275970Scyusing namespace llvm; 42275970Scyusing namespace llvm::object; 43275970Scyusing namespace lld; 44275970Scyusing namespace lld::coff; 45275970Scy 46275970Scy// Print out the first two columns of a line. 47275970Scystatic void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) { 48275970Scy os << format(" %04x:%08llx", sec, addr); 49275970Scy} 50275970Scy 51275970Scy// Write the time stamp with the format used by link.exe 52275970Scy// It seems identical to strftime with "%c" on msvc build, but we need a 53275970Scy// locale-agnostic version. 54275970Scystatic void writeFormattedTimestamp(raw_ostream &os, time_t tds) { 55275970Scy constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed", 56275970Scy "Thu", "Fri", "Sat"}; 57275970Scy constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr", 58275970Scy "May", "Jun", "Jul", "Aug", 59275970Scy "Sep", "Oct", "Nov", "Dec"}; 60275970Scy tm *time = localtime(&tds); 61275970Scy os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday], 62282408Scy months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min, 63282408Scy time->tm_sec, time->tm_year + 1900); 64282408Scy} 65282408Scy 66275970Scystatic void sortUniqueSymbols(std::vector<Defined *> &syms, 67275970Scy uint64_t imageBase) { 68275970Scy // Build helper vector 69275970Scy using SortEntry = std::pair<Defined *, size_t>; 70275970Scy std::vector<SortEntry> v; 71275970Scy v.resize(syms.size()); 72275970Scy for (size_t i = 0, e = syms.size(); i < e; ++i) 73275970Scy v[i] = SortEntry(syms[i], i); 74275970Scy 75275970Scy // Remove duplicate symbol pointers 76275970Scy parallelSort(v, std::less<SortEntry>()); 77275970Scy auto end = std::unique(v.begin(), v.end(), 78275970Scy [](const SortEntry &a, const SortEntry &b) { 79275970Scy return a.first == b.first; 80275970Scy }); 81275970Scy v.erase(end, v.end()); 82275970Scy 83275970Scy // Sort by RVA then original order 84275970Scy parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) { 85275970Scy // Add config.imageBase to avoid comparing "negative" RVAs. 86275970Scy // This can happen with symbols of Absolute kind 87275970Scy uint64_t rvaa = imageBase + a.first->getRVA(); 88275970Scy uint64_t rvab = imageBase + b.first->getRVA(); 89275970Scy return rvaa < rvab || (rvaa == rvab && a.second < b.second); 90275970Scy }); 91275970Scy 92275970Scy syms.resize(v.size()); 93275970Scy for (size_t i = 0, e = v.size(); i < e; ++i) 94275970Scy syms[i] = v[i].first; 95275970Scy} 96275970Scy 97275970Scy// Returns the lists of all symbols that we want to print out. 98275970Scystatic void getSymbols(const COFFLinkerContext &ctx, 99275970Scy std::vector<Defined *> &syms, 100275970Scy std::vector<Defined *> &staticSyms) { 101275970Scy 102275970Scy for (ObjFile *file : ctx.objFileInstances) 103275970Scy for (Symbol *b : file->getSymbols()) { 104275970Scy if (!b || !b->isLive()) 105275970Scy continue; 106275970Scy if (auto *sym = dyn_cast<DefinedCOFF>(b)) { 107275970Scy COFFSymbolRef symRef = sym->getCOFFSymbol(); 108275970Scy if (!symRef.isSectionDefinition() && 109275970Scy symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) { 110275970Scy if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) 111275970Scy staticSyms.push_back(sym); 112275970Scy else 113275970Scy syms.push_back(sym); 114275970Scy } 115275970Scy } else if (auto *sym = dyn_cast<Defined>(b)) { 116275970Scy syms.push_back(sym); 117275970Scy } 118275970Scy } 119275970Scy 120275970Scy for (ImportFile *file : ctx.importFileInstances) { 121275970Scy if (!file->live) 122275970Scy continue; 123275970Scy 124275970Scy if (!file->thunkSym) 125275970Scy continue; 126275970Scy 127275970Scy if (!file->thunkLive) 128275970Scy continue; 129275970Scy 130275970Scy if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym)) 131275970Scy syms.push_back(thunkSym); 132275970Scy 133275970Scy if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym)) 134275970Scy syms.push_back(impSym); 135275970Scy } 136280849Scy 137275970Scy sortUniqueSymbols(syms, ctx.config.imageBase); 138275970Scy sortUniqueSymbols(staticSyms, ctx.config.imageBase); 139275970Scy} 140275970Scy 141275970Scy// Construct a map from symbols to their stringified representations. 142275970Scystatic DenseMap<Defined *, std::string> 143275970ScygetSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) { 144275970Scy std::vector<std::string> str(syms.size()); 145275970Scy parallelFor((size_t)0, syms.size(), [&](size_t i) { 146275970Scy raw_string_ostream os(str[i]); 147275970Scy Defined *sym = syms[i]; 148275970Scy 149275970Scy uint16_t sectionIdx = 0; 150275970Scy uint64_t address = 0; 151275970Scy SmallString<128> fileDescr; 152275970Scy 153275970Scy if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) { 154275970Scy address = absSym->getVA(); 155275970Scy fileDescr = "<absolute>"; 156275970Scy } else if (isa<DefinedSynthetic>(sym)) { 157275970Scy fileDescr = "<linker-defined>"; 158275970Scy } else if (isa<DefinedCommon>(sym)) { 159275970Scy fileDescr = "<common>"; 160275970Scy } else if (Chunk *chunk = sym->getChunk()) { 161275970Scy address = sym->getRVA(); 162275970Scy if (OutputSection *sec = ctx.getOutputSection(chunk)) 163275970Scy address -= sec->header.VirtualAddress; 164275970Scy 165275970Scy sectionIdx = chunk->getOutputSectionIdx(); 166275970Scy 167275970Scy InputFile *file; 168275970Scy if (auto *impSym = dyn_cast<DefinedImportData>(sym)) 169275970Scy file = impSym->file; 170275970Scy else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym)) 171275970Scy file = thunkSym->wrappedSym->file; 172275970Scy else 173275970Scy file = sym->getFile(); 174275970Scy 175275970Scy if (file) { 176275970Scy if (!file->parentName.empty()) { 177275970Scy fileDescr = sys::path::filename(file->parentName); 178275970Scy sys::path::replace_extension(fileDescr, ""); 179275970Scy fileDescr += ":"; 180275970Scy } 181275970Scy fileDescr += sys::path::filename(file->getName()); 182275970Scy } 183275970Scy } 184275970Scy writeHeader(os, sectionIdx, address); 185275970Scy os << " "; 186275970Scy os << left_justify(sym->getName(), 26); 187275970Scy os << " "; 188275970Scy os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16); 189275970Scy if (!fileDescr.empty()) { 190275970Scy os << " "; // FIXME : Handle "f" and "i" flags sometimes generated 191275970Scy // by link.exe in those spaces 192275970Scy os << fileDescr; 193275970Scy } 194275970Scy }); 195275970Scy 196275970Scy DenseMap<Defined *, std::string> ret; 197275970Scy for (size_t i = 0, e = syms.size(); i < e; ++i) 198275970Scy ret[syms[i]] = std::move(str[i]); 199275970Scy return ret; 200275970Scy} 201275970Scy 202275970Scyvoid lld::coff::writeMapFile(COFFLinkerContext &ctx) { 203275970Scy if (ctx.config.mapFile.empty()) 204275970Scy return; 205275970Scy 206275970Scy std::error_code ec; 207275970Scy raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None); 208275970Scy if (ec) 209275970Scy fatal("cannot open " + ctx.config.mapFile + ": " + ec.message()); 210275970Scy 211275970Scy ScopedTimer t1(ctx.totalMapTimer); 212275970Scy 213275970Scy // Collect symbol info that we want to print out. 214275970Scy ScopedTimer t2(ctx.symbolGatherTimer); 215275970Scy std::vector<Defined *> syms; 216275970Scy std::vector<Defined *> staticSyms; 217275970Scy getSymbols(ctx, syms, staticSyms); 218275970Scy t2.stop(); 219275970Scy 220275970Scy ScopedTimer t3(ctx.symbolStringsTimer); 221275970Scy DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms); 222275970Scy DenseMap<Defined *, std::string> staticSymStr = 223275970Scy getSymbolStrings(ctx, staticSyms); 224275970Scy t3.stop(); 225275970Scy 226275970Scy ScopedTimer t4(ctx.writeTimer); 227275970Scy SmallString<128> AppName = sys::path::filename(ctx.config.outputFile); 228275970Scy sys::path::replace_extension(AppName, ""); 229275970Scy 230275970Scy // Print out the file header 231275970Scy os << " " << AppName << "\n"; 232275970Scy os << "\n"; 233275970Scy 234275970Scy os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8) 235275970Scy << " ("; 236275970Scy if (ctx.config.repro) { 237275970Scy os << "Repro mode"; 238275970Scy } else { 239275970Scy writeFormattedTimestamp(os, ctx.config.timestamp); 240275970Scy } 241275970Scy os << ")\n"; 242275970Scy 243275970Scy os << "\n"; 244275970Scy os << " Preferred load address is " 245275970Scy << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n"; 246275970Scy os << "\n"; 247275970Scy 248275970Scy // Print out section table. 249275970Scy os << " Start Length Name Class\n"; 250275970Scy 251275970Scy for (OutputSection *sec : ctx.outputSections) { 252282408Scy // Merge display of chunks with same sectionName 253282408Scy std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges; 254282408Scy for (Chunk *c : sec->chunks) { 255282408Scy auto *sc = dyn_cast<SectionChunk>(c); 256282408Scy if (!sc) 257275970Scy continue; 258275970Scy 259275970Scy if (ChunkRanges.empty() || 260275970Scy c->getSectionName() != ChunkRanges.back().first->getSectionName()) { 261275970Scy ChunkRanges.emplace_back(sc, sc); 262275970Scy } else { 263275970Scy ChunkRanges.back().second = sc; 264275970Scy } 265275970Scy } 266275970Scy 267275970Scy const bool isCodeSection = 268275970Scy (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) && 269275970Scy (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) && 270275970Scy (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE); 271275970Scy StringRef SectionClass = (isCodeSection ? "CODE" : "DATA"); 272275970Scy 273275970Scy for (auto &cr : ChunkRanges) { 274275970Scy size_t size = 275275970Scy cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA(); 276275970Scy 277275970Scy auto address = cr.first->getRVA() - sec->header.VirtualAddress; 278275970Scy writeHeader(os, sec->sectionIndex, address); 279275970Scy os << " " << format_hex_no_prefix(size, 8) << "H"; 280275970Scy os << " " << left_justify(cr.first->getSectionName(), 23); 281275970Scy os << " " << SectionClass; 282275970Scy os << '\n'; 283275970Scy } 284275970Scy } 285275970Scy 286275970Scy // Print out the symbols table (without static symbols) 287275970Scy os << "\n"; 288275970Scy os << " Address Publics by Value Rva+Base" 289275970Scy " Lib:Object\n"; 290275970Scy os << "\n"; 291275970Scy for (Defined *sym : syms) 292275970Scy os << symStr[sym] << '\n'; 293275970Scy 294275970Scy // Print out the entry point. 295275970Scy os << "\n"; 296275970Scy 297275970Scy uint16_t entrySecIndex = 0; 298275970Scy uint64_t entryAddress = 0; 299275970Scy 300275970Scy if (!ctx.config.noEntry) { 301275970Scy Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry); 302275970Scy if (entry) { 303275970Scy Chunk *chunk = entry->getChunk(); 304275970Scy entrySecIndex = chunk->getOutputSectionIdx(); 305275970Scy entryAddress = 306275970Scy entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress; 307275970Scy } 308275970Scy } 309275970Scy os << " entry point at "; 310275970Scy os << format("%04x:%08llx", entrySecIndex, entryAddress); 311275970Scy os << "\n"; 312275970Scy 313275970Scy // Print out the static symbols 314275970Scy os << "\n"; 315275970Scy os << " Static symbols\n"; 316275970Scy os << "\n"; 317275970Scy for (Defined *sym : staticSyms) 318275970Scy os << staticSymStr[sym] << '\n'; 319275970Scy 320275970Scy // Print out the exported functions 321275970Scy if (ctx.config.mapInfo) { 322275970Scy os << "\n"; 323275970Scy os << " Exports\n"; 324275970Scy os << "\n"; 325275970Scy os << " ordinal name\n\n"; 326275970Scy for (Export &e : ctx.config.exports) { 327275970Scy os << format(" %7d", e.ordinal) << " " << e.name << "\n"; 328275970Scy if (!e.extName.empty() && e.extName != e.name) 329275970Scy os << " exported name: " << e.extName << "\n"; 330275970Scy } 331275970Scy } 332275970Scy 333275970Scy t4.stop(); 334275970Scy t1.stop(); 335275970Scy} 336275970Scy