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/ErrorHandler.h" 36 #include "lld/Common/Timer.h" 37 #include "llvm/Support/Parallel.h" 38 #include "llvm/Support/Path.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 // Build helper vector 68 using SortEntry = std::pair<Defined *, size_t>; 69 std::vector<SortEntry> v; 70 v.resize(syms.size()); 71 for (size_t i = 0, e = syms.size(); i < e; ++i) 72 v[i] = SortEntry(syms[i], i); 73 74 // Remove duplicate symbol pointers 75 parallelSort(v, std::less<SortEntry>()); 76 auto end = std::unique(v.begin(), v.end(), 77 [](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, [](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 = config->imageBase + a.first->getRVA(); 87 uint64_t rvab = config->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->thunkSym) 124 continue; 125 126 if (!file->thunkLive) 127 continue; 128 129 if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym)) 130 syms.push_back(thunkSym); 131 132 if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym)) 133 syms.push_back(impSym); 134 } 135 136 sortUniqueSymbols(syms); 137 sortUniqueSymbols(staticSyms); 138 } 139 140 // Construct a map from symbols to their stringified representations. 141 static DenseMap<Defined *, std::string> 142 getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) { 143 std::vector<std::string> str(syms.size()); 144 parallelForEachN((size_t)0, syms.size(), [&](size_t i) { 145 raw_string_ostream os(str[i]); 146 Defined *sym = syms[i]; 147 148 uint16_t sectionIdx = 0; 149 uint64_t address = 0; 150 SmallString<128> fileDescr; 151 152 if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) { 153 address = absSym->getVA(); 154 fileDescr = "<absolute>"; 155 } else if (isa<DefinedSynthetic>(sym)) { 156 fileDescr = "<linker-defined>"; 157 } else if (isa<DefinedCommon>(sym)) { 158 fileDescr = "<common>"; 159 } else if (Chunk *chunk = sym->getChunk()) { 160 address = sym->getRVA(); 161 if (OutputSection *sec = ctx.getOutputSection(chunk)) 162 address -= sec->header.VirtualAddress; 163 164 sectionIdx = chunk->getOutputSectionIdx(); 165 166 InputFile *file; 167 if (auto *impSym = dyn_cast<DefinedImportData>(sym)) 168 file = impSym->file; 169 else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym)) 170 file = thunkSym->wrappedSym->file; 171 else 172 file = sym->getFile(); 173 174 if (file) { 175 if (!file->parentName.empty()) { 176 fileDescr = sys::path::filename(file->parentName); 177 sys::path::replace_extension(fileDescr, ""); 178 fileDescr += ":"; 179 } 180 fileDescr += sys::path::filename(file->getName()); 181 } 182 } 183 writeHeader(os, sectionIdx, address); 184 os << " "; 185 os << left_justify(sym->getName(), 26); 186 os << " "; 187 os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16); 188 if (!fileDescr.empty()) { 189 os << " "; // FIXME : Handle "f" and "i" flags sometimes generated 190 // by link.exe in those spaces 191 os << fileDescr; 192 } 193 }); 194 195 DenseMap<Defined *, std::string> ret; 196 for (size_t i = 0, e = syms.size(); i < e; ++i) 197 ret[syms[i]] = std::move(str[i]); 198 return ret; 199 } 200 201 void lld::coff::writeMapFile(COFFLinkerContext &ctx) { 202 if (config->mapFile.empty()) 203 return; 204 205 std::error_code ec; 206 raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); 207 if (ec) 208 fatal("cannot open " + config->mapFile + ": " + ec.message()); 209 210 ScopedTimer t1(ctx.totalMapTimer); 211 212 // Collect symbol info that we want to print out. 213 ScopedTimer t2(ctx.symbolGatherTimer); 214 std::vector<Defined *> syms; 215 std::vector<Defined *> staticSyms; 216 getSymbols(ctx, syms, staticSyms); 217 t2.stop(); 218 219 ScopedTimer t3(ctx.symbolStringsTimer); 220 DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms); 221 DenseMap<Defined *, std::string> staticSymStr = 222 getSymbolStrings(ctx, staticSyms); 223 t3.stop(); 224 225 ScopedTimer t4(ctx.writeTimer); 226 SmallString<128> AppName = sys::path::filename(config->outputFile); 227 sys::path::replace_extension(AppName, ""); 228 229 // Print out the file header 230 os << " " << AppName << "\n"; 231 os << "\n"; 232 233 os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " ("; 234 if (config->repro) { 235 os << "Repro mode"; 236 } else { 237 writeFormattedTimestamp(os, config->timestamp); 238 } 239 os << ")\n"; 240 241 os << "\n"; 242 os << " Preferred load address is " 243 << format_hex_no_prefix(config->imageBase, 16) << "\n"; 244 os << "\n"; 245 246 // Print out section table. 247 os << " Start Length Name Class\n"; 248 249 for (OutputSection *sec : ctx.outputSections) { 250 // Merge display of chunks with same sectionName 251 std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges; 252 for (Chunk *c : sec->chunks) { 253 auto *sc = dyn_cast<SectionChunk>(c); 254 if (!sc) 255 continue; 256 257 if (ChunkRanges.empty() || 258 c->getSectionName() != ChunkRanges.back().first->getSectionName()) { 259 ChunkRanges.emplace_back(sc, sc); 260 } else { 261 ChunkRanges.back().second = sc; 262 } 263 } 264 265 const bool isCodeSection = 266 (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) && 267 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) && 268 (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE); 269 StringRef SectionClass = (isCodeSection ? "CODE" : "DATA"); 270 271 for (auto &cr : ChunkRanges) { 272 size_t size = 273 cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA(); 274 275 auto address = cr.first->getRVA() - sec->header.VirtualAddress; 276 writeHeader(os, sec->sectionIndex, address); 277 os << " " << format_hex_no_prefix(size, 8) << "H"; 278 os << " " << left_justify(cr.first->getSectionName(), 23); 279 os << " " << SectionClass; 280 os << '\n'; 281 } 282 } 283 284 // Print out the symbols table (without static symbols) 285 os << "\n"; 286 os << " Address Publics by Value Rva+Base" 287 " Lib:Object\n"; 288 os << "\n"; 289 for (Defined *sym : syms) 290 os << symStr[sym] << '\n'; 291 292 // Print out the entry point. 293 os << "\n"; 294 295 uint16_t entrySecIndex = 0; 296 uint64_t entryAddress = 0; 297 298 if (!config->noEntry) { 299 Defined *entry = dyn_cast_or_null<Defined>(config->entry); 300 if (entry) { 301 Chunk *chunk = entry->getChunk(); 302 entrySecIndex = chunk->getOutputSectionIdx(); 303 entryAddress = 304 entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress; 305 } 306 } 307 os << " entry point at "; 308 os << format("%04x:%08llx", entrySecIndex, entryAddress); 309 os << "\n"; 310 311 // Print out the static symbols 312 os << "\n"; 313 os << " Static symbols\n"; 314 os << "\n"; 315 for (Defined *sym : staticSyms) 316 os << staticSymStr[sym] << '\n'; 317 318 t4.stop(); 319 t1.stop(); 320 } 321