[lld] b91905a - [lld-link] Support /map option, matching link.exe 's /map output format
Sylvain Audi via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 24 06:49:35 PDT 2020
Author: Sylvain Audi
Date: 2020-03-24T09:48:00-04:00
New Revision: b91905a26378f03c9b56126207771829d89cdcb7
URL: https://github.com/llvm/llvm-project/commit/b91905a26378f03c9b56126207771829d89cdcb7
DIFF: https://github.com/llvm/llvm-project/commit/b91905a26378f03c9b56126207771829d89cdcb7.diff
LOG: [lld-link] Support /map option, matching link.exe 's /map output format
Added support for /map and /map:[filepath].
The output was derived from Microsoft's Link.exe output when using that same option.
Note that /MAPINFO support was not added.
The previous implementation of MapFile.cpp/.h was meant for /lldmap, and was renamed to LLDMapFile.cpp/.h
MapFile.cpp/.h is now for /MAP
However, a small fix was added to lldmap, replacing a std::sort with std::stable_sort to enforce reproducibility.
Differential Revision: https://reviews.llvm.org/D70557
Added:
lld/COFF/LLDMapFile.cpp
lld/COFF/LLDMapFile.h
lld/test/COFF/Inputs/map.yaml
lld/test/COFF/map.test
Modified:
lld/COFF/CMakeLists.txt
lld/COFF/Config.h
lld/COFF/Driver.cpp
lld/COFF/MapFile.cpp
lld/COFF/Options.td
lld/COFF/Writer.cpp
Removed:
lld/test/COFF/lldmap.test
################################################################################
diff --git a/lld/COFF/CMakeLists.txt b/lld/COFF/CMakeLists.txt
index 1d310f8419f5..4592ace373ef 100644
--- a/lld/COFF/CMakeLists.txt
+++ b/lld/COFF/CMakeLists.txt
@@ -14,6 +14,7 @@ add_lld_library(lldCOFF
DriverUtils.cpp
ICF.cpp
InputFiles.cpp
+ LLDMapFile.cpp
LTO.cpp
MapFile.cpp
MarkLive.cpp
diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index af86b6992272..e376ea51f133 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -182,6 +182,9 @@ struct Configuration {
llvm::StringMap<int> order;
// Used for /lldmap.
+ std::string lldmapFile;
+
+ // Used for /map.
std::string mapFile;
// Used for /thinlto-index-only:
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 5320b8b83ce0..8b3ba1cdf24b 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -707,14 +707,15 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
return debugTypes;
}
-static std::string getMapFile(const opt::InputArgList &args) {
- auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+static std::string getMapFile(const opt::InputArgList &args,
+ opt::OptSpecifier os, opt::OptSpecifier osFile) {
+ auto *arg = args.getLastArg(os, osFile);
if (!arg)
return "";
- if (arg->getOption().getID() == OPT_lldmap_file)
+ if (arg->getOption().getID() == osFile.getID())
return arg->getValue();
- assert(arg->getOption().getID() == OPT_lldmap);
+ assert(arg->getOption().getID() == os.getID());
StringRef outFile = config->outputFile;
return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
@@ -1564,7 +1565,14 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
if (config->mingw || config->debugDwarf)
config->warnLongSectionNames = false;
- config->mapFile = getMapFile(args);
+ config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
+ config->mapFile = getMapFile(args, OPT_map, OPT_map_file);
+
+ if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
+ warn("/lldmap and /map have the same output file '" + config->mapFile +
+ "'.\n>>> ignoring /lldmap");
+ config->lldmapFile.clear();
+ }
if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
diff --git a/lld/COFF/LLDMapFile.cpp b/lld/COFF/LLDMapFile.cpp
new file mode 100644
index 000000000000..0e069724f816
--- /dev/null
+++ b/lld/COFF/LLDMapFile.cpp
@@ -0,0 +1,123 @@
+//===- LLDMapFile.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the /lldmap option. It shows lists in order and
+// hierarchically the output sections, input sections, input files and
+// symbol:
+//
+// Address Size Align Out File Symbol
+// 00201000 00000015 4 .text
+// 00201000 0000000e 4 test.o:(.text)
+// 0020100e 00000000 0 local
+// 00201005 00000000 0 f(int)
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDMapFile.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Threads.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
+
+using SymbolMapTy =
+ DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+
+static constexpr char indent8[] = " "; // 8 spaces
+static constexpr char indent16[] = " "; // 16 spaces
+
+// Print out the first three columns of a line.
+static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
+ uint64_t align) {
+ os << format("%08llx %08llx %5lld ", addr, size, align);
+}
+
+// Returns a list of all symbols that we want to print out.
+static std::vector<DefinedRegular *> getSymbols() {
+ std::vector<DefinedRegular *> v;
+ for (ObjFile *file : ObjFile::instances)
+ for (Symbol *b : file->getSymbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ if (sym && !sym->getCOFFSymbol().isSectionDefinition())
+ v.push_back(sym);
+ return v;
+}
+
+// Returns a map from sections to their symbols.
+static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
+ SymbolMapTy ret;
+ for (DefinedRegular *s : syms)
+ ret[s->getChunk()].push_back(s);
+
+ // Sort symbols by address.
+ for (auto &it : ret) {
+ SmallVectorImpl<DefinedRegular *> &v = it.second;
+ std::stable_sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
+ return a->getRVA() < b->getRVA();
+ });
+ }
+ return ret;
+}
+
+// Construct a map from symbols to their stringified representations.
+static DenseMap<DefinedRegular *, std::string>
+getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ writeHeader(os, syms[i]->getRVA(), 0, 0);
+ os << indent16 << toString(*syms[i]);
+ });
+
+ DenseMap<DefinedRegular *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
+}
+
+void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
+ if (config->lldmapFile.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
+ if (ec)
+ fatal("cannot open " + config->lldmapFile + ": " + ec.message());
+
+ // Collect symbol info that we want to print out.
+ std::vector<DefinedRegular *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+
+ // Print out the header line.
+ os << "Address Size Align Out In Symbol\n";
+
+ // Print out file contents.
+ for (OutputSection *sec : outputSections) {
+ writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
+ os << sec->name << '\n';
+
+ for (Chunk *c : sec->chunks) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
+ continue;
+
+ writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
+ os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
+ << ")\n";
+ for (DefinedRegular *sym : sectionSyms[sc])
+ os << symStr[sym] << '\n';
+ }
+ }
+}
diff --git a/lld/COFF/LLDMapFile.h b/lld/COFF/LLDMapFile.h
new file mode 100644
index 000000000000..b731293a8625
--- /dev/null
+++ b/lld/COFF/LLDMapFile.h
@@ -0,0 +1,21 @@
+//===- LLDMapFile.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_LLDMAPFILE_H
+#define LLD_COFF_LLDMAPFILE_H
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lld {
+namespace coff {
+class OutputSection;
+void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
+}
+}
+
+#endif
diff --git a/lld/COFF/MapFile.cpp b/lld/COFF/MapFile.cpp
index a80c553637aa..0958a79b1402 100644
--- a/lld/COFF/MapFile.cpp
+++ b/lld/COFF/MapFile.cpp
@@ -6,16 +6,25 @@
//
//===----------------------------------------------------------------------===//
//
-// This file implements the /lldmap option. It shows lists in order and
-// hierarchically the output sections, input sections, input files and
-// symbol:
+// This file implements the /map option in the same format as link.exe
+// (based on observations)
//
-// Address Size Align Out File Symbol
-// 00201000 00000015 4 .text
-// 00201000 0000000e 4 test.o:(.text)
-// 0020100e 00000000 0 local
-// 00201005 00000000 0 f(int)
+// Header (program name, timestamp info, preferred load address)
//
+// Section list (Start = Section index:Base address):
+// Start Length Name Class
+// 0001:00001000 00000015H .text CODE
+//
+// Symbols list:
+// Address Publics by Value Rva + Base Lib:Object
+// 0001:00001000 main 0000000140001000 main.obj
+// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj
+//
+// entry point at 0001:00000360
+//
+// Static symbols
+//
+// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
//===----------------------------------------------------------------------===//
#include "MapFile.h"
@@ -24,6 +33,8 @@
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Threads.h"
+#include "lld/Common/Timer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -31,56 +42,160 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::coff;
-using SymbolMapTy =
- DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root());
+static Timer symbolGatherTimer("Gather symbols", totalMapTimer);
+static Timer symbolStringsTimer("Build symbol strings", totalMapTimer);
+static Timer writeTimer("Write to file", totalMapTimer);
-static constexpr char indent8[] = " "; // 8 spaces
-static constexpr char indent16[] = " "; // 16 spaces
+// Print out the first two columns of a line.
+static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
+ os << format(" %04x:%08llx", sec, addr);
+}
-// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
- uint64_t align) {
- os << format("%08llx %08llx %5lld ", addr, size, align);
+// Write the time stamp with the format used by link.exe
+// It seems identical to strftime with "%c" on msvc build, but we need a
+// locale-agnostic version.
+static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
+ constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+ constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec"};
+ tm *time = localtime(&tds);
+ os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
+ months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
+ time->tm_sec, time->tm_year + 1900);
}
-// Returns a list of all symbols that we want to print out.
-static std::vector<DefinedRegular *> getSymbols() {
- std::vector<DefinedRegular *> v;
- for (ObjFile *file : ObjFile::instances)
- for (Symbol *b : file->getSymbols())
- if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
- if (sym && !sym->getCOFFSymbol().isSectionDefinition())
- v.push_back(sym);
- return v;
+static void sortUniqueSymbols(std::vector<Defined *> &syms) {
+ // Build helper vector
+ using SortEntry = std::pair<Defined *, size_t>;
+ std::vector<SortEntry> v;
+ v.resize(syms.size());
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ v[i] = SortEntry(syms[i], i);
+
+ // Remove duplicate symbol pointers
+ parallelSort(v, std::less<SortEntry>());
+ auto end = std::unique(v.begin(), v.end(),
+ [](const SortEntry &a, const SortEntry &b) {
+ return a.first == b.first;
+ });
+ v.erase(end, v.end());
+
+ // Sort by RVA then original order
+ parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
+ // Add config->imageBase to avoid comparing "negative" RVAs.
+ // This can happen with symbols of Absolute kind
+ uint64_t rvaa = config->imageBase + a.first->getRVA();
+ uint64_t rvab = config->imageBase + b.first->getRVA();
+ return rvaa < rvab || (rvaa == rvab && a.second < b.second);
+ });
+
+ syms.resize(v.size());
+ for (size_t i = 0, e = v.size(); i < e; ++i)
+ syms[i] = v[i].first;
}
-// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
- SymbolMapTy ret;
- for (DefinedRegular *s : syms)
- ret[s->getChunk()].push_back(s);
-
- // Sort symbols by address.
- for (auto &it : ret) {
- SmallVectorImpl<DefinedRegular *> &v = it.second;
- std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
- return a->getRVA() < b->getRVA();
- });
+// Returns the lists of all symbols that we want to print out.
+static void getSymbols(std::vector<Defined *> &syms,
+ std::vector<Defined *> &staticSyms) {
+
+ for (ObjFile *file : ObjFile::instances)
+ for (Symbol *b : file->getSymbols()) {
+ if (!b || !b->isLive())
+ continue;
+ if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
+ COFFSymbolRef symRef = sym->getCOFFSymbol();
+ if (!symRef.isSectionDefinition() &&
+ symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
+ if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
+ staticSyms.push_back(sym);
+ else
+ syms.push_back(sym);
+ }
+ } else if (auto *sym = dyn_cast<Defined>(b)) {
+ syms.push_back(sym);
+ }
+ }
+
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
+ continue;
+
+ if (!file->thunkSym)
+ continue;
+
+ if (!file->thunkLive)
+ continue;
+
+ if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
+ syms.push_back(thunkSym);
+
+ if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
+ syms.push_back(impSym);
}
- return ret;
+
+ sortUniqueSymbols(syms);
+ sortUniqueSymbols(staticSyms);
}
// Construct a map from symbols to their stringified representations.
-static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+static DenseMap<Defined *, std::string>
+getSymbolStrings(ArrayRef<Defined *> syms) {
std::vector<std::string> str(syms.size());
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
- writeHeader(os, syms[i]->getRVA(), 0, 0);
- os << indent16 << toString(*syms[i]);
+ Defined *sym = syms[i];
+
+ uint16_t sectionIdx = 0;
+ uint64_t address = 0;
+ SmallString<128> fileDescr;
+
+ if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
+ address = absSym->getVA();
+ fileDescr = "<absolute>";
+ } else if (isa<DefinedSynthetic>(sym)) {
+ fileDescr = "<linker-defined>";
+ } else if (isa<DefinedCommon>(sym)) {
+ fileDescr = "<common>";
+ } else if (Chunk *chunk = sym->getChunk()) {
+ address = sym->getRVA();
+ if (OutputSection *sec = chunk->getOutputSection())
+ address -= sec->header.VirtualAddress;
+
+ sectionIdx = chunk->getOutputSectionIdx();
+
+ InputFile *file;
+ if (auto *impSym = dyn_cast<DefinedImportData>(sym))
+ file = impSym->file;
+ else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
+ file = thunkSym->wrappedSym->file;
+ else
+ file = sym->getFile();
+
+ if (file) {
+ if (!file->parentName.empty()) {
+ fileDescr = sys::path::filename(file->parentName);
+ sys::path::replace_extension(fileDescr, "");
+ fileDescr += ":";
+ }
+ fileDescr += sys::path::filename(file->getName());
+ }
+ }
+ writeHeader(os, sectionIdx, address);
+ os << " ";
+ os << left_justify(sym->getName(), 26);
+ os << " ";
+ os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
+ if (!fileDescr.empty()) {
+ os << " "; // FIXME : Handle "f" and "i" flags sometimes generated
+ // by link.exe in those spaces
+ os << fileDescr;
+ }
});
- DenseMap<DefinedRegular *, std::string> ret;
+ DenseMap<Defined *, std::string> ret;
for (size_t i = 0, e = syms.size(); i < e; ++i)
ret[syms[i]] = std::move(str[i]);
return ret;
@@ -95,29 +210,113 @@ void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (ec)
fatal("cannot open " + config->mapFile + ": " + ec.message());
+ ScopedTimer t1(totalMapTimer);
+
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> syms = getSymbols();
- SymbolMapTy sectionSyms = getSectionSyms(syms);
- DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+ ScopedTimer t2(symbolGatherTimer);
+ std::vector<Defined *> syms;
+ std::vector<Defined *> staticSyms;
+ getSymbols(syms, staticSyms);
+ t2.stop();
- // Print out the header line.
- os << "Address Size Align Out In Symbol\n";
+ ScopedTimer t3(symbolStringsTimer);
+ DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
+ DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
+ t3.stop();
- // Print out file contents.
- for (OutputSection *sec : outputSections) {
- writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
- os << sec->name << '\n';
+ ScopedTimer t4(writeTimer);
+ SmallString<128> AppName = sys::path::filename(config->outputFile);
+ sys::path::replace_extension(AppName, "");
+ // Print out the file header
+ os << " " << AppName << "\n";
+ os << "\n";
+
+ os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
+ if (config->repro) {
+ os << "Repro mode";
+ } else {
+ writeFormattedTimestamp(os, config->timestamp);
+ }
+ os << ")\n";
+
+ os << "\n";
+ os << " Preferred load address is "
+ << format_hex_no_prefix(config->imageBase, 16) << "\n";
+ os << "\n";
+
+ // Print out section table.
+ os << " Start Length Name Class\n";
+
+ for (OutputSection *sec : outputSections) {
+ // Merge display of chunks with same sectionName
+ std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
for (Chunk *c : sec->chunks) {
auto *sc = dyn_cast<SectionChunk>(c);
if (!sc)
continue;
- writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
- os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
- << ")\n";
- for (DefinedRegular *sym : sectionSyms[sc])
- os << symStr[sym] << '\n';
+ if (ChunkRanges.empty() ||
+ c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
+ ChunkRanges.emplace_back(sc, sc);
+ } else {
+ ChunkRanges.back().second = sc;
+ }
+ }
+
+ const bool isCodeSection =
+ (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
+ StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
+
+ for (auto &cr : ChunkRanges) {
+ size_t size =
+ cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
+
+ auto address = cr.first->getRVA() - sec->header.VirtualAddress;
+ writeHeader(os, sec->sectionIndex, address);
+ os << " " << format_hex_no_prefix(size, 8) << "H";
+ os << " " << left_justify(cr.first->getSectionName(), 23);
+ os << " " << SectionClass;
+ os << '\n';
+ }
+ }
+
+ // Print out the symbols table (without static symbols)
+ os << "\n";
+ os << " Address Publics by Value Rva+Base"
+ " Lib:Object\n";
+ os << "\n";
+ for (Defined *sym : syms)
+ os << symStr[sym] << '\n';
+
+ // Print out the entry point.
+ os << "\n";
+
+ uint16_t entrySecIndex = 0;
+ uint64_t entryAddress = 0;
+
+ if (!config->noEntry) {
+ Defined *entry = dyn_cast_or_null<Defined>(config->entry);
+ if (entry) {
+ Chunk *chunk = entry->getChunk();
+ entrySecIndex = chunk->getOutputSectionIdx();
+ entryAddress =
+ entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
}
}
+ os << " entry point at ";
+ os << format("%04x:%08llx", entrySecIndex, entryAddress);
+ os << "\n";
+
+ // Print out the static symbols
+ os << "\n";
+ os << " Static symbols\n";
+ os << "\n";
+ for (Defined *sym : staticSyms)
+ os << staticSymStr[sym] << '\n';
+
+ t4.stop();
+ t1.stop();
}
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index cea02e7a0042..72fe9ce8c118 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -226,6 +226,8 @@ defm threads: B<"threads",
// Flags for debugging
def lldmap : F<"lldmap">;
def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
+def map : F<"map">;
+def map_file : Joined<["/", "-", "/?", "-?"], "map:">;
def show_timing : F<"time">;
def summary : F<"summary">;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 7e7aaafe18ed..d5e2b59027b4 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -10,6 +10,7 @@
#include "Config.h"
#include "DLL.h"
#include "InputFiles.h"
+#include "LLDMapFile.h"
#include "MapFile.h"
#include "PDB.h"
#include "SymbolTable.h"
@@ -633,6 +634,7 @@ void Writer::run() {
}
writeBuildId();
+ writeLLDMapFile(outputSections);
writeMapFile(outputSections);
if (errorCount())
diff --git a/lld/test/COFF/Inputs/map.yaml b/lld/test/COFF/Inputs/map.yaml
new file mode 100644
index 000000000000..7a834344cc5b
--- /dev/null
+++ b/lld/test/COFF/Inputs/map.yaml
@@ -0,0 +1,60 @@
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: []
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 0000000000000000
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: exportfn1
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: exportfn2
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn1
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn2
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: absolute
+ Value: 0x00000042
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: staticdef
+ Value: 0x00000043
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/lld/test/COFF/lldmap.test b/lld/test/COFF/lldmap.test
deleted file mode 100644
index d705a16c6c2a..000000000000
--- a/lld/test/COFF/lldmap.test
+++ /dev/null
@@ -1,10 +0,0 @@
-# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
-# RUN: lld-link /out:%t.exe /entry:main /lldmap:%T/foo.map %t.obj
-# RUN: FileCheck -strict-whitespace %s < %T/foo.map
-# RUN: lld-link /out:%T/bar.exe /entry:main /lldmap %t.obj
-# RUN: FileCheck -strict-whitespace %s < %T/bar.map
-
-# CHECK: Address Size Align Out In Symbol
-# CHECK-NEXT: 00001000 00000006 4096 .text
-# CHECK-NEXT: 00001000 00000006 16 {{.*}}lldmap.test.tmp.obj:(.text$mn)
-# CHECK-NEXT: 00001000 00000000 0 main
diff --git a/lld/test/COFF/map.test b/lld/test/COFF/map.test
new file mode 100644
index 000000000000..0ec27fb33bdc
--- /dev/null
+++ b/lld/test/COFF/map.test
@@ -0,0 +1,40 @@
+# RUN: yaml2obj < %p/Inputs/export.yaml > %t-dll.obj
+# RUN: lld-link /out:%t.dll /dll %t-dll.obj /implib:%t-dll.lib \
+# RUN: /export:exportfn1 /export:exportfn2
+# RUN: yaml2obj < %p/Inputs/map.yaml > %t.obj
+# RUN: lld-link /out:%t.exe /entry:main %t.obj %t-dll.lib /map:%T/foo.map /lldmap
+# RUN: FileCheck -check-prefix=MAP -strict-whitespace %s < %T/foo.map
+# RUN: FileCheck -check-prefix=LLDMAP -strict-whitespace %s < %t.map
+# RUN: lld-link /out:%t.exe /entry:main %t.obj %t-dll.lib /map /lldmap:%T/foo-lld.map
+# RUN: FileCheck -check-prefix=MAP -strict-whitespace %s < %t.map
+# RUN: FileCheck -check-prefix=LLDMAP -strict-whitespace %s < %T/foo-lld.map
+
+# MAP: {{.*}}
+# MAP-EMPTY:
+# MAP-NEXT: Timestamp is {{.*}}
+# MAP-EMPTY:
+# MAP-NEXT: Preferred load address is 0000000140000000
+# MAP-EMPTY:
+# MAP-NEXT: Start Length Name Class
+# MAP-NEXT: 0001:00000000 00000008H .text CODE
+# MAP-EMPTY:
+# MAP-NEXT: Address Publics by Value Rva+Base Lib:Object
+# MAP-EMPTY:
+# MAP-NEXT: 0000:00000042 absolute 0000000000000042 <absolute>
+# MAP-NEXT: 0001:00000000 main 0000000140001000 map.test.tmp.obj
+# MAP-NEXT: 0001:00000010 exportfn1 0000000140001010 map.test.tmp-dll:map.test.tmp.dll
+# MAP-NEXT: 0001:00000020 exportfn2 0000000140001020 map.test.tmp-dll:map.test.tmp.dll
+# MAP-NEXT: 0002:00000040 __imp_exportfn1 0000000140002040 map.test.tmp-dll:map.test.tmp.dll
+# MAP-NEXT: 0002:00000048 __imp_exportfn2 0000000140002048 map.test.tmp-dll:map.test.tmp.dll
+# MAP-EMPTY:
+# MAP-NEXT: entry point at 0001:00000000
+# MAP-EMPTY:
+# MAP-NEXT: Static symbols
+# MAP-EMPTY:
+# MAP-NEXT: 0001:00000043 staticdef 0000000140001043 map.test.tmp.obj
+
+
+# LLDMAP: Address Size Align Out In Symbol
+# LLDMAP-NEXT: 00001000 00000026 4096 .text
+# LLDMAP-NEXT: 00001000 00000008 4 {{.*}}map.test.tmp.obj:(.text)
+# LLDMAP-NEXT: 00001000 00000000 0 main
More information about the llvm-commits
mailing list