Path: blob/main/contrib/llvm-project/lld/COFF/MapFile.cpp
34879 views
//===- MapFile.cpp --------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the /map option in the same format as link.exe9// (based on observations)10//11// Header (program name, timestamp info, preferred load address)12//13// Section list (Start = Section index:Base address):14// Start Length Name Class15// 0001:00001000 00000015H .text CODE16//17// Symbols list:18// Address Publics by Value Rva + Base Lib:Object19// 0001:00001000 main 0000000140001000 main.obj20// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj21//22// entry point at 0001:0000036023//24// Static symbols25//26// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj27//===----------------------------------------------------------------------===//2829#include "MapFile.h"30#include "COFFLinkerContext.h"31#include "SymbolTable.h"32#include "Symbols.h"33#include "Writer.h"34#include "lld/Common/ErrorHandler.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"4041using namespace llvm;42using namespace llvm::object;43using namespace lld;44using namespace lld::coff;4546// Print out the first two columns of a line.47static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {48os << format(" %04x:%08llx", sec, addr);49}5051// Write the time stamp with the format used by link.exe52// It seems identical to strftime with "%c" on msvc build, but we need a53// locale-agnostic version.54static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {55constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",56"Thu", "Fri", "Sat"};57constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",58"May", "Jun", "Jul", "Aug",59"Sep", "Oct", "Nov", "Dec"};60tm *time = localtime(&tds);61os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],62months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,63time->tm_sec, time->tm_year + 1900);64}6566static void sortUniqueSymbols(std::vector<Defined *> &syms,67uint64_t imageBase) {68// Build helper vector69using SortEntry = std::pair<Defined *, size_t>;70std::vector<SortEntry> v;71v.resize(syms.size());72for (size_t i = 0, e = syms.size(); i < e; ++i)73v[i] = SortEntry(syms[i], i);7475// Remove duplicate symbol pointers76parallelSort(v, std::less<SortEntry>());77auto end = std::unique(v.begin(), v.end(),78[](const SortEntry &a, const SortEntry &b) {79return a.first == b.first;80});81v.erase(end, v.end());8283// Sort by RVA then original order84parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {85// Add config.imageBase to avoid comparing "negative" RVAs.86// This can happen with symbols of Absolute kind87uint64_t rvaa = imageBase + a.first->getRVA();88uint64_t rvab = imageBase + b.first->getRVA();89return rvaa < rvab || (rvaa == rvab && a.second < b.second);90});9192syms.resize(v.size());93for (size_t i = 0, e = v.size(); i < e; ++i)94syms[i] = v[i].first;95}9697// Returns the lists of all symbols that we want to print out.98static void getSymbols(const COFFLinkerContext &ctx,99std::vector<Defined *> &syms,100std::vector<Defined *> &staticSyms) {101102for (ObjFile *file : ctx.objFileInstances)103for (Symbol *b : file->getSymbols()) {104if (!b || !b->isLive())105continue;106if (auto *sym = dyn_cast<DefinedCOFF>(b)) {107COFFSymbolRef symRef = sym->getCOFFSymbol();108if (!symRef.isSectionDefinition() &&109symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {110if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)111staticSyms.push_back(sym);112else113syms.push_back(sym);114}115} else if (auto *sym = dyn_cast<Defined>(b)) {116syms.push_back(sym);117}118}119120for (ImportFile *file : ctx.importFileInstances) {121if (!file->live)122continue;123124if (!file->thunkSym)125continue;126127if (!file->thunkLive)128continue;129130if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))131syms.push_back(thunkSym);132133if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))134syms.push_back(impSym);135}136137sortUniqueSymbols(syms, ctx.config.imageBase);138sortUniqueSymbols(staticSyms, ctx.config.imageBase);139}140141// Construct a map from symbols to their stringified representations.142static DenseMap<Defined *, std::string>143getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {144std::vector<std::string> str(syms.size());145parallelFor((size_t)0, syms.size(), [&](size_t i) {146raw_string_ostream os(str[i]);147Defined *sym = syms[i];148149uint16_t sectionIdx = 0;150uint64_t address = 0;151SmallString<128> fileDescr;152153if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {154address = absSym->getVA();155fileDescr = "<absolute>";156} else if (isa<DefinedSynthetic>(sym)) {157fileDescr = "<linker-defined>";158} else if (isa<DefinedCommon>(sym)) {159fileDescr = "<common>";160} else if (Chunk *chunk = sym->getChunk()) {161address = sym->getRVA();162if (OutputSection *sec = ctx.getOutputSection(chunk))163address -= sec->header.VirtualAddress;164165sectionIdx = chunk->getOutputSectionIdx();166167InputFile *file;168if (auto *impSym = dyn_cast<DefinedImportData>(sym))169file = impSym->file;170else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))171file = thunkSym->wrappedSym->file;172else173file = sym->getFile();174175if (file) {176if (!file->parentName.empty()) {177fileDescr = sys::path::filename(file->parentName);178sys::path::replace_extension(fileDescr, "");179fileDescr += ":";180}181fileDescr += sys::path::filename(file->getName());182}183}184writeHeader(os, sectionIdx, address);185os << " ";186os << left_justify(sym->getName(), 26);187os << " ";188os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);189if (!fileDescr.empty()) {190os << " "; // FIXME : Handle "f" and "i" flags sometimes generated191// by link.exe in those spaces192os << fileDescr;193}194});195196DenseMap<Defined *, std::string> ret;197for (size_t i = 0, e = syms.size(); i < e; ++i)198ret[syms[i]] = std::move(str[i]);199return ret;200}201202void lld::coff::writeMapFile(COFFLinkerContext &ctx) {203if (ctx.config.mapFile.empty())204return;205206llvm::TimeTraceScope timeScope("Map file");207std::error_code ec;208raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);209if (ec)210fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());211212ScopedTimer t1(ctx.totalMapTimer);213214// Collect symbol info that we want to print out.215ScopedTimer t2(ctx.symbolGatherTimer);216std::vector<Defined *> syms;217std::vector<Defined *> staticSyms;218getSymbols(ctx, syms, staticSyms);219t2.stop();220221ScopedTimer t3(ctx.symbolStringsTimer);222DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);223DenseMap<Defined *, std::string> staticSymStr =224getSymbolStrings(ctx, staticSyms);225t3.stop();226227ScopedTimer t4(ctx.writeTimer);228SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);229sys::path::replace_extension(AppName, "");230231// Print out the file header232os << " " << AppName << "\n";233os << "\n";234235os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)236<< " (";237if (ctx.config.repro) {238os << "Repro mode";239} else {240writeFormattedTimestamp(os, ctx.config.timestamp);241}242os << ")\n";243244os << "\n";245os << " Preferred load address is "246<< format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";247os << "\n";248249// Print out section table.250os << " Start Length Name Class\n";251252for (OutputSection *sec : ctx.outputSections) {253// Merge display of chunks with same sectionName254std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;255for (Chunk *c : sec->chunks) {256auto *sc = dyn_cast<SectionChunk>(c);257if (!sc)258continue;259260if (ChunkRanges.empty() ||261c->getSectionName() != ChunkRanges.back().first->getSectionName()) {262ChunkRanges.emplace_back(sc, sc);263} else {264ChunkRanges.back().second = sc;265}266}267268const 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);272StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");273274for (auto &cr : ChunkRanges) {275size_t size =276cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();277278auto address = cr.first->getRVA() - sec->header.VirtualAddress;279writeHeader(os, sec->sectionIndex, address);280os << " " << format_hex_no_prefix(size, 8) << "H";281os << " " << left_justify(cr.first->getSectionName(), 23);282os << " " << SectionClass;283os << '\n';284}285}286287// Print out the symbols table (without static symbols)288os << "\n";289os << " Address Publics by Value Rva+Base"290" Lib:Object\n";291os << "\n";292for (Defined *sym : syms)293os << symStr[sym] << '\n';294295// Print out the entry point.296os << "\n";297298uint16_t entrySecIndex = 0;299uint64_t entryAddress = 0;300301if (!ctx.config.noEntry) {302Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);303if (entry) {304Chunk *chunk = entry->getChunk();305entrySecIndex = chunk->getOutputSectionIdx();306entryAddress =307entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;308}309}310os << " entry point at ";311os << format("%04x:%08llx", entrySecIndex, entryAddress);312os << "\n";313314// Print out the static symbols315os << "\n";316os << " Static symbols\n";317os << "\n";318for (Defined *sym : staticSyms)319os << staticSymStr[sym] << '\n';320321// Print out the exported functions322if (ctx.config.mapInfo) {323os << "\n";324os << " Exports\n";325os << "\n";326os << " ordinal name\n\n";327for (Export &e : ctx.config.exports) {328os << format(" %7d", e.ordinal) << " " << e.name << "\n";329if (!e.extName.empty() && e.extName != e.name)330os << " exported name: " << e.extName << "\n";331}332}333334t4.stop();335t1.stop();336}337338339