1 //===- MapFile.cpp --------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the /map option in the same format as link.exe 10 // (based on observations) 11 // 12 // Header (program name, timestamp info, preferred load address) 13 // 14 // Section list (Start = Section index:Base address): 15 // Start Length Name Class 16 // 0001:00001000 00000015H .text CODE 17 // 18 // Symbols list: 19 // Address Publics by Value Rva + Base Lib:Object 20 // 0001:00001000 main 0000000140001000 main.obj 21 // 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj 22 // 23 // entry point at 0001:00000360 24 // 25 // Static symbols 26 // 27 // 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj 28 //===----------------------------------------------------------------------===// 29 30 #include "MapFile.h" 31 #include "COFFLinkerContext.h" 32 #include "SymbolTable.h" 33 #include "Symbols.h" 34 #include "Writer.h" 35 #include "lld/Common/Timer.h" 36 #include "llvm/Support/Parallel.h" 37 #include "llvm/Support/Path.h" 38 #include "llvm/Support/TimeProfiler.h" 39 #include "llvm/Support/raw_ostream.h" 40 41 using namespace llvm; 42 using namespace llvm::object; 43 using namespace lld; 44 using namespace lld::coff; 45 46 // Print out the first two columns of a line. 47 static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) { 48 os << format(" %04x:%08llx", sec, addr); 49 } 50 51 // Write the time stamp with the format used by link.exe 52 // It seems identical to strftime with "%c" on msvc build, but we need a 53 // locale-agnostic version. 54 static void writeFormattedTimestamp(raw_ostream &os, time_t tds) { 55 constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed", 56 "Thu", "Fri", "Sat"}; 57 constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr", 58 "May", "Jun", "Jul", "Aug", 59 "Sep", "Oct", "Nov", "Dec"}; 60 tm *time = localtime(&tds); 61 os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday], 62 months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min, 63 time->tm_sec, time->tm_year + 1900); 64 } 65 66 static void sortUniqueSymbols(std::vector<Defined *> &syms, 67 uint64_t imageBase) { 68 // Build helper vector 69 using SortEntry = std::pair<Defined *, size_t>; 70 std::vector<SortEntry> v; 71 v.resize(syms.size()); 72 for (size_t i = 0, e = syms.size(); i < e; ++i) 73 v[i] = SortEntry(syms[i], i); 74 75 // Remove duplicate symbol pointers 76 parallelSort(v, std::less<SortEntry>()); 77 auto end = llvm::unique(v, [](const SortEntry &a, const SortEntry &b) { 78 return a.first == b.first; 79 }); 80 v.erase(end, v.end()); 81 82 // Sort by RVA then original order 83 parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) { 84 // Add config.imageBase to avoid comparing "negative" RVAs. 85 // This can happen with symbols of Absolute kind 86 uint64_t rvaa = imageBase + a.first->getRVA(); 87 uint64_t rvab = imageBase + b.first->getRVA(); 88 return rvaa < rvab || (rvaa == rvab && a.second < b.second); 89 }); 90 91 syms.resize(v.size()); 92 for (size_t i = 0, e = v.size(); i < e; ++i) 93 syms[i] = v[i].first; 94 } 95 96 // Returns the lists of all symbols that we want to print out. 97 static void getSymbols(const COFFLinkerContext &ctx, 98 std::vector<Defined *> &syms, 99 std::vector<Defined *> &staticSyms) { 100 101 for (ObjFile *file : ctx.objFileInstances) 102 for (Symbol *b : file->getSymbols()) { 103 if (!b || !b->isLive()) 104 continue; 105 if (auto *sym = dyn_cast<DefinedCOFF>(b)) { 106 COFFSymbolRef symRef = sym->getCOFFSymbol(); 107 if (!symRef.isSectionDefinition() && 108 symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) { 109 if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) 110 staticSyms.push_back(sym); 111 else 112 syms.push_back(sym); 113 } 114 } else if (auto *sym = dyn_cast<Defined>(b)) { 115 syms.push_back(sym); 116 } 117 } 118 119 for (ImportFile *file : ctx.importFileInstances) { 120 if (!file->live) 121 continue; 122 123 if (file->impSym) 124 syms.push_back(file->impSym); 125 if (file->thunkSym && file->thunkSym->isLive()) 126 syms.push_back(file->thunkSym); 127 if (file->auxThunkSym && file->auxThunkSym->isLive()) 128 syms.push_back(file->auxThunkSym); 129 if (file->impchkThunk) 130 syms.push_back(file->impchkThunk->sym); 131 if (file->impECSym) 132 syms.push_back(file->impECSym); 133 if (file->auxImpCopySym) 134 syms.push_back(file->auxImpCopySym); 135 } 136 137 sortUniqueSymbols(syms, ctx.config.imageBase); 138 sortUniqueSymbols(staticSyms, ctx.config.imageBase); 139 } 140 141 // Construct a map from symbols to their stringified representations. 142 static DenseMap<Defined *, std::string> 143 getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) { 144 std::vector<std::string> str(syms.size()); 145 parallelFor((size_t)0, syms.size(), [&](size_t i) { 146 raw_string_ostream os(str[i]); 147 Defined *sym = syms[i]; 148 149 uint16_t sectionIdx = 0; 150 uint64_t address = 0; 151 SmallString<128> fileDescr; 152 153 if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) { 154 address = absSym->getVA(); 155 fileDescr = "<absolute>"; 156 } else if (isa<DefinedSynthetic>(sym)) { 157 fileDescr = "<linker-defined>"; 158 } else if (isa<DefinedCommon>(sym)) { 159 fileDescr = "<common>"; 160 } else if (Chunk *chunk = sym->getChunk()) { 161 address = sym->getRVA(); 162 if (OutputSection *sec = ctx.getOutputSection(chunk)) 163 address -= sec->header.VirtualAddress; 164 165 sectionIdx = chunk->getOutputSectionIdx(); 166 167 InputFile *file; 168 if (auto *impSym = dyn_cast<DefinedImportData>(sym)) 169 file = impSym->file; 170 else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym)) 171 file = thunkSym->wrappedSym->file; 172 else 173 file = sym->getFile(); 174 175 if (file) { 176 if (!file->parentName.empty()) { 177 fileDescr = sys::path::filename(file->parentName); 178 sys::path::replace_extension(fileDescr, ""); 179 fileDescr += ":"; 180 } 181 fileDescr += sys::path::filename(file->getName()); 182 } 183 } 184 writeHeader(os, sectionIdx, address); 185 os << " "; 186 os << left_justify(sym->getName(), 26); 187 os << " "; 188 os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16); 189 if (!fileDescr.empty()) { 190 os << " "; // FIXME : Handle "f" and "i" flags sometimes generated 191 // by link.exe in those spaces 192 os << fileDescr; 193 } 194 }); 195 196 DenseMap<Defined *, std::string> ret; 197 for (size_t i = 0, e = syms.size(); i < e; ++i) 198 ret[syms[i]] = std::move(str[i]); 199 return ret; 200 } 201 202 void lld::coff::writeMapFile(COFFLinkerContext &ctx) { 203 if (ctx.config.mapFile.empty()) 204 return; 205 206 llvm::TimeTraceScope timeScope("Map file"); 207 std::error_code ec; 208 raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None); 209 if (ec) 210 Fatal(ctx) << "cannot open " << ctx.config.mapFile << ": " << ec.message(); 211 212 ScopedTimer t1(ctx.totalMapTimer); 213 214 // Collect symbol info that we want to print out. 215 ScopedTimer t2(ctx.symbolGatherTimer); 216 std::vector<Defined *> syms; 217 std::vector<Defined *> staticSyms; 218 getSymbols(ctx, syms, staticSyms); 219 t2.stop(); 220 221 ScopedTimer t3(ctx.symbolStringsTimer); 222 DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms); 223 DenseMap<Defined *, std::string> staticSymStr = 224 getSymbolStrings(ctx, staticSyms); 225 t3.stop(); 226 227 ScopedTimer t4(ctx.writeTimer); 228 SmallString<128> AppName = sys::path::filename(ctx.config.outputFile); 229 sys::path::replace_extension(AppName, ""); 230 231 // Print out the file header 232 os << " " << AppName << "\n"; 233 os << "\n"; 234 235 os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8) 236 << " ("; 237 if (ctx.config.repro) { 238 os << "Repro mode"; 239 } else { 240 writeFormattedTimestamp(os, ctx.config.timestamp); 241 } 242 os << ")\n"; 243 244 os << "\n"; 245 os << " Preferred load address is " 246 << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n"; 247 os << "\n"; 248 249 // Print out section table. 250 os << " Start Length Name Class\n"; 251 252 for (OutputSection *sec : ctx.outputSections) { 253 // Merge display of chunks with same sectionName 254 std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges; 255 for (Chunk *c : sec->chunks) { 256 auto *sc = dyn_cast<SectionChunk>(c); 257 if (!sc) 258 continue; 259 260 if (ChunkRanges.empty() || 261 c->getSectionName() != ChunkRanges.back().first->getSectionName()) { 262 ChunkRanges.emplace_back(sc, sc); 263 } else { 264 ChunkRanges.back().second = sc; 265 } 266 } 267 268 const bool isCodeSection = 269 (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) && 270 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) && 271 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE); 272 StringRef SectionClass = (isCodeSection ? "CODE" : "DATA"); 273 274 for (auto &cr : ChunkRanges) { 275 size_t size = 276 cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA(); 277 278 auto address = cr.first->getRVA() - sec->header.VirtualAddress; 279 writeHeader(os, sec->sectionIndex, address); 280 os << " " << format_hex_no_prefix(size, 8) << "H"; 281 os << " " << left_justify(cr.first->getSectionName(), 23); 282 os << " " << SectionClass; 283 os << '\n'; 284 } 285 } 286 287 // Print out the symbols table (without static symbols) 288 os << "\n"; 289 os << " Address Publics by Value Rva+Base" 290 " Lib:Object\n"; 291 os << "\n"; 292 for (Defined *sym : syms) 293 os << symStr[sym] << '\n'; 294 295 // Print out the entry point. 296 os << "\n"; 297 298 uint16_t entrySecIndex = 0; 299 uint64_t entryAddress = 0; 300 301 if (!ctx.config.noEntry) { 302 Defined *entry = dyn_cast_or_null<Defined>(ctx.symtab.entry); 303 if (entry) { 304 Chunk *chunk = entry->getChunk(); 305 entrySecIndex = chunk->getOutputSectionIdx(); 306 entryAddress = 307 entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress; 308 } 309 } 310 os << " entry point at "; 311 os << format("%04x:%08llx", entrySecIndex, entryAddress); 312 os << "\n"; 313 314 // Print out the static symbols 315 os << "\n"; 316 os << " Static symbols\n"; 317 os << "\n"; 318 for (Defined *sym : staticSyms) 319 os << staticSymStr[sym] << '\n'; 320 321 // Print out the exported functions 322 if (ctx.config.mapInfo) { 323 os << "\n"; 324 os << " Exports\n"; 325 os << "\n"; 326 os << " ordinal name\n\n"; 327 for (Export &e : ctx.symtab.exports) { 328 os << format(" %7d", e.ordinal) << " " << e.name << "\n"; 329 if (!e.extName.empty() && e.extName != e.name) 330 os << " exported name: " << e.extName << "\n"; 331 } 332 } 333 334 t4.stop(); 335 t1.stop(); 336 } 337