forked from OSchip/llvm-project
Temporarily revert "[LLD] Remove global state in lld/COFF" and "[lld] Add test to
check for timer output" Seems to be causing a number of asan test failures. This reverts commitb4fa71eed3
ande03c7e367a
.
This commit is contained in:
parent
5de8c7f138
commit
a2fd05ada9
|
@ -5,7 +5,6 @@ add_public_tablegen_target(COFFOptionsTableGen)
|
||||||
add_lld_library(lldCOFF
|
add_lld_library(lldCOFF
|
||||||
CallGraphSort.cpp
|
CallGraphSort.cpp
|
||||||
Chunks.cpp
|
Chunks.cpp
|
||||||
COFFLinkerContext.cpp
|
|
||||||
DebugTypes.cpp
|
DebugTypes.cpp
|
||||||
DLL.cpp
|
DLL.cpp
|
||||||
Driver.cpp
|
Driver.cpp
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
//===- COFFContext.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
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// Description
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "lld/Common/Memory.h"
|
|
||||||
#include "llvm/DebugInfo/CodeView/TypeHashing.h"
|
|
||||||
|
|
||||||
namespace lld {
|
|
||||||
namespace coff {
|
|
||||||
|
|
||||||
COFFLinkerContext::COFFLinkerContext()
|
|
||||||
: symtab(*this), rootTimer("Total Linking Time"),
|
|
||||||
inputFileTimer("Input File Reading", rootTimer),
|
|
||||||
ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer),
|
|
||||||
icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer),
|
|
||||||
outputCommitTimer("Commit Output File", rootTimer),
|
|
||||||
totalMapTimer("MAP Emission (Cumulative)", rootTimer),
|
|
||||||
symbolGatherTimer("Gather Symbols", totalMapTimer),
|
|
||||||
symbolStringsTimer("Build Symbol Strings", totalMapTimer),
|
|
||||||
writeTimer("Write to File", totalMapTimer),
|
|
||||||
totalPdbLinkTimer("PDB Emission (Cumulative)", rootTimer),
|
|
||||||
addObjectsTimer("Add Objects", totalPdbLinkTimer),
|
|
||||||
typeMergingTimer("Type Merging", addObjectsTimer),
|
|
||||||
loadGHashTimer("Global Type Hashing", addObjectsTimer),
|
|
||||||
mergeGHashTimer("GHash Type Merging", addObjectsTimer),
|
|
||||||
symbolMergingTimer("Symbol Merging", addObjectsTimer),
|
|
||||||
publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer),
|
|
||||||
tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer),
|
|
||||||
diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {}
|
|
||||||
|
|
||||||
COFFLinkerContext::~COFFLinkerContext() { clearGHashes(); }
|
|
||||||
|
|
||||||
void COFFLinkerContext::clearGHashes() {
|
|
||||||
for (TpiSource *src : tpiSourceList) {
|
|
||||||
if (src->ownedGHashes)
|
|
||||||
delete[] src->ghashes.data();
|
|
||||||
src->ghashes = {};
|
|
||||||
src->isItemIndex.clear();
|
|
||||||
src->uniqueTypes.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace coff
|
|
||||||
} // namespace lld
|
|
|
@ -1,88 +0,0 @@
|
||||||
//===- COFFLinkerContext.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_COFFLinkerContext_H
|
|
||||||
#define LLD_COFF_COFFLinkerContext_H
|
|
||||||
|
|
||||||
#include "Chunks.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DebugTypes.h"
|
|
||||||
#include "InputFiles.h"
|
|
||||||
#include "SymbolTable.h"
|
|
||||||
#include "Writer.h"
|
|
||||||
#include "lld/Common/Timer.h"
|
|
||||||
|
|
||||||
namespace lld {
|
|
||||||
namespace coff {
|
|
||||||
|
|
||||||
class COFFLinkerContext {
|
|
||||||
public:
|
|
||||||
COFFLinkerContext();
|
|
||||||
COFFLinkerContext(const COFFLinkerContext &) = delete;
|
|
||||||
COFFLinkerContext &operator=(const COFFLinkerContext &) = delete;
|
|
||||||
~COFFLinkerContext();
|
|
||||||
|
|
||||||
void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); }
|
|
||||||
|
|
||||||
/// Free heap allocated ghashes.
|
|
||||||
void clearGHashes();
|
|
||||||
|
|
||||||
SymbolTable symtab;
|
|
||||||
|
|
||||||
std::vector<ObjFile *> objFileInstances;
|
|
||||||
std::map<std::string, PDBInputFile *> pdbInputFileInstances;
|
|
||||||
std::vector<ImportFile *> importFileInstances;
|
|
||||||
std::vector<BitcodeFile *> bitcodeFileInstances;
|
|
||||||
|
|
||||||
MergeChunk *mergeChunkInstances[Log2MaxSectionAlignment + 1] = {};
|
|
||||||
|
|
||||||
/// All sources of type information in the program.
|
|
||||||
std::vector<TpiSource *> tpiSourceList;
|
|
||||||
|
|
||||||
std::map<llvm::codeview::GUID, TpiSource *> typeServerSourceMappings;
|
|
||||||
std::map<uint32_t, TpiSource *> precompSourceMappings;
|
|
||||||
|
|
||||||
/// List of all output sections. After output sections are finalized, this
|
|
||||||
/// can be indexed by getOutputSection.
|
|
||||||
std::vector<OutputSection *> outputSections;
|
|
||||||
|
|
||||||
OutputSection *getOutputSection(const Chunk *c) const {
|
|
||||||
return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// All timers used in the COFF linker.
|
|
||||||
Timer rootTimer;
|
|
||||||
Timer inputFileTimer;
|
|
||||||
Timer ltoTimer;
|
|
||||||
Timer gcTimer;
|
|
||||||
Timer icfTimer;
|
|
||||||
|
|
||||||
// Writer timers.
|
|
||||||
Timer codeLayoutTimer;
|
|
||||||
Timer outputCommitTimer;
|
|
||||||
Timer totalMapTimer;
|
|
||||||
Timer symbolGatherTimer;
|
|
||||||
Timer symbolStringsTimer;
|
|
||||||
Timer writeTimer;
|
|
||||||
|
|
||||||
// PDB timers.
|
|
||||||
Timer totalPdbLinkTimer;
|
|
||||||
Timer addObjectsTimer;
|
|
||||||
Timer typeMergingTimer;
|
|
||||||
Timer loadGHashTimer;
|
|
||||||
Timer mergeGHashTimer;
|
|
||||||
Timer symbolMergingTimer;
|
|
||||||
Timer publicsLayoutTimer;
|
|
||||||
Timer tpiStreamLayoutTimer;
|
|
||||||
Timer diskCommitTimer;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace coff
|
|
||||||
} // namespace lld
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -12,7 +12,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "CallGraphSort.h"
|
#include "CallGraphSort.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "InputFiles.h"
|
#include "InputFiles.h"
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
@ -49,7 +48,7 @@ struct Cluster {
|
||||||
|
|
||||||
class CallGraphSort {
|
class CallGraphSort {
|
||||||
public:
|
public:
|
||||||
CallGraphSort(const COFFLinkerContext &ctx);
|
CallGraphSort();
|
||||||
|
|
||||||
DenseMap<const SectionChunk *, int> run();
|
DenseMap<const SectionChunk *, int> run();
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ using SectionPair = std::pair<const SectionChunk *, const SectionChunk *>;
|
||||||
// Take the edge list in Config->CallGraphProfile, resolve symbol names to
|
// Take the edge list in Config->CallGraphProfile, resolve symbol names to
|
||||||
// Symbols, and generate a graph between InputSections with the provided
|
// Symbols, and generate a graph between InputSections with the provided
|
||||||
// weights.
|
// weights.
|
||||||
CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) {
|
CallGraphSort::CallGraphSort() {
|
||||||
MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
|
MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
|
||||||
DenseMap<const SectionChunk *, int> secToCluster;
|
DenseMap<const SectionChunk *, int> secToCluster;
|
||||||
|
|
||||||
|
@ -96,7 +95,7 @@ CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) {
|
||||||
// output. This messes with the cluster size and density calculations. We
|
// output. This messes with the cluster size and density calculations. We
|
||||||
// would also end up moving input sections in other output sections without
|
// would also end up moving input sections in other output sections without
|
||||||
// moving them closer to what calls them.
|
// moving them closer to what calls them.
|
||||||
if (ctx.getOutputSection(fromSec) != ctx.getOutputSection(toSec))
|
if (fromSec->getOutputSection() != toSec->getOutputSection())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int from = getOrCreateNode(fromSec);
|
int from = getOrCreateNode(fromSec);
|
||||||
|
@ -241,7 +240,6 @@ DenseMap<const SectionChunk *, int> CallGraphSort::run() {
|
||||||
// This first builds a call graph based on the profile data then merges sections
|
// This first builds a call graph based on the profile data then merges sections
|
||||||
// according to the C³ heuristic. All clusters are then sorted by a density
|
// according to the C³ heuristic. All clusters are then sorted by a density
|
||||||
// metric to further improve locality.
|
// metric to further improve locality.
|
||||||
DenseMap<const SectionChunk *, int>
|
DenseMap<const SectionChunk *, int> coff::computeCallGraphProfileOrder() {
|
||||||
coff::computeCallGraphProfileOrder(const COFFLinkerContext &ctx) {
|
return CallGraphSort().run();
|
||||||
return CallGraphSort(ctx).run();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,8 @@
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
class SectionChunk;
|
class SectionChunk;
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
llvm::DenseMap<const SectionChunk *, int>
|
llvm::DenseMap<const SectionChunk *, int> computeCallGraphProfileOrder();
|
||||||
computeCallGraphProfileOrder(const COFFLinkerContext &ctx);
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "InputFiles.h"
|
#include "InputFiles.h"
|
||||||
#include "SymbolTable.h"
|
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include "Writer.h"
|
#include "Writer.h"
|
||||||
|
#include "SymbolTable.h"
|
||||||
#include "lld/Common/ErrorHandler.h"
|
#include "lld/Common/ErrorHandler.h"
|
||||||
#include "llvm/ADT/Twine.h"
|
#include "llvm/ADT/Twine.h"
|
||||||
#include "llvm/BinaryFormat/COFF.h"
|
#include "llvm/BinaryFormat/COFF.h"
|
||||||
|
@ -386,7 +385,7 @@ void SectionChunk::applyRelocation(uint8_t *off,
|
||||||
// section is needed to compute SECREL and SECTION relocations used in debug
|
// section is needed to compute SECREL and SECTION relocations used in debug
|
||||||
// info.
|
// info.
|
||||||
Chunk *c = sym ? sym->getChunk() : nullptr;
|
Chunk *c = sym ? sym->getChunk() : nullptr;
|
||||||
OutputSection *os = c ? file->ctx.getOutputSection(c) : nullptr;
|
OutputSection *os = c ? c->getOutputSection() : nullptr;
|
||||||
|
|
||||||
// Skip the relocation if it refers to a discarded section, and diagnose it
|
// Skip the relocation if it refers to a discarded section, and diagnose it
|
||||||
// as an error if appropriate. If a symbol was discarded early, it may be
|
// as an error if appropriate. If a symbol was discarded early, it may be
|
||||||
|
@ -939,16 +938,18 @@ uint8_t Baserel::getDefaultType() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {};
|
||||||
|
|
||||||
MergeChunk::MergeChunk(uint32_t alignment)
|
MergeChunk::MergeChunk(uint32_t alignment)
|
||||||
: builder(StringTableBuilder::RAW, alignment) {
|
: builder(StringTableBuilder::RAW, alignment) {
|
||||||
setAlignment(alignment);
|
setAlignment(alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeChunk::addSection(COFFLinkerContext &ctx, SectionChunk *c) {
|
void MergeChunk::addSection(SectionChunk *c) {
|
||||||
assert(isPowerOf2_32(c->getAlignment()));
|
assert(isPowerOf2_32(c->getAlignment()));
|
||||||
uint8_t p2Align = llvm::Log2_32(c->getAlignment());
|
uint8_t p2Align = llvm::Log2_32(c->getAlignment());
|
||||||
assert(p2Align < array_lengthof(ctx.mergeChunkInstances));
|
assert(p2Align < array_lengthof(instances));
|
||||||
auto *&mc = ctx.mergeChunkInstances[p2Align];
|
auto *&mc = instances[p2Align];
|
||||||
if (!mc)
|
if (!mc)
|
||||||
mc = make<MergeChunk>(c->getAlignment());
|
mc = make<MergeChunk>(c->getAlignment());
|
||||||
mc->sections.push_back(c);
|
mc->sections.push_back(c);
|
||||||
|
|
|
@ -101,6 +101,7 @@ public:
|
||||||
// chunk has a back pointer to an output section.
|
// chunk has a back pointer to an output section.
|
||||||
void setOutputSectionIdx(uint16_t o) { osidx = o; }
|
void setOutputSectionIdx(uint16_t o) { osidx = o; }
|
||||||
uint16_t getOutputSectionIdx() const { return osidx; }
|
uint16_t getOutputSectionIdx() const { return osidx; }
|
||||||
|
OutputSection *getOutputSection() const;
|
||||||
|
|
||||||
// Windows-specific.
|
// Windows-specific.
|
||||||
// Collect all locations that contain absolute addresses for base relocations.
|
// Collect all locations that contain absolute addresses for base relocations.
|
||||||
|
@ -414,7 +415,7 @@ inline StringRef Chunk::getDebugName() const {
|
||||||
class MergeChunk : public NonSectionChunk {
|
class MergeChunk : public NonSectionChunk {
|
||||||
public:
|
public:
|
||||||
MergeChunk(uint32_t alignment);
|
MergeChunk(uint32_t alignment);
|
||||||
static void addSection(COFFLinkerContext &ctx, SectionChunk *c);
|
static void addSection(SectionChunk *c);
|
||||||
void finalizeContents();
|
void finalizeContents();
|
||||||
void assignSubsectionRVAs();
|
void assignSubsectionRVAs();
|
||||||
|
|
||||||
|
@ -423,6 +424,7 @@ public:
|
||||||
size_t getSize() const override;
|
size_t getSize() const override;
|
||||||
void writeTo(uint8_t *buf) const override;
|
void writeTo(uint8_t *buf) const override;
|
||||||
|
|
||||||
|
static MergeChunk *instances[Log2MaxSectionAlignment + 1];
|
||||||
std::vector<SectionChunk *> sections;
|
std::vector<SectionChunk *> sections;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "DLL.h"
|
#include "DLL.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "llvm/Object/COFF.h"
|
#include "llvm/Object/COFF.h"
|
||||||
|
@ -632,7 +631,7 @@ uint64_t DelayLoadContents::getDirSize() {
|
||||||
return dirs.size() * sizeof(delay_import_directory_table_entry);
|
return dirs.size() * sizeof(delay_import_directory_table_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) {
|
void DelayLoadContents::create(Defined *h) {
|
||||||
helper = h;
|
helper = h;
|
||||||
std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
|
std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
|
||||||
|
|
||||||
|
@ -661,13 +660,13 @@ void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) {
|
||||||
// call targets for Control Flow Guard.
|
// call targets for Control Flow Guard.
|
||||||
StringRef symName = saver.save("__imp_load_" + extName);
|
StringRef symName = saver.save("__imp_load_" + extName);
|
||||||
s->loadThunkSym =
|
s->loadThunkSym =
|
||||||
cast<DefinedSynthetic>(ctx.symtab.addSynthetic(symName, t));
|
cast<DefinedSynthetic>(symtab->addSynthetic(symName, t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thunks.push_back(tm);
|
thunks.push_back(tm);
|
||||||
StringRef tmName =
|
StringRef tmName =
|
||||||
saver.save("__tailMerge_" + syms[0]->getDLLName().lower());
|
saver.save("__tailMerge_" + syms[0]->getDLLName().lower());
|
||||||
ctx.symtab.addSynthetic(tmName, tm);
|
symtab->addSynthetic(tmName, tm);
|
||||||
// Terminate with null values.
|
// Terminate with null values.
|
||||||
addresses.push_back(make<NullChunk>(8));
|
addresses.push_back(make<NullChunk>(8));
|
||||||
names.push_back(make<NullChunk>(8));
|
names.push_back(make<NullChunk>(8));
|
||||||
|
|
|
@ -40,7 +40,7 @@ class DelayLoadContents {
|
||||||
public:
|
public:
|
||||||
void add(DefinedImportData *sym) { imports.push_back(sym); }
|
void add(DefinedImportData *sym) { imports.push_back(sym); }
|
||||||
bool empty() { return imports.empty(); }
|
bool empty() { return imports.empty(); }
|
||||||
void create(COFFLinkerContext &ctx, Defined *helper);
|
void create(Defined *helper);
|
||||||
std::vector<Chunk *> getChunks();
|
std::vector<Chunk *> getChunks();
|
||||||
std::vector<Chunk *> getDataChunks();
|
std::vector<Chunk *> getDataChunks();
|
||||||
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
|
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "DebugTypes.h"
|
#include "DebugTypes.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "Driver.h"
|
#include "Driver.h"
|
||||||
#include "InputFiles.h"
|
#include "InputFiles.h"
|
||||||
|
@ -15,6 +14,7 @@
|
||||||
#include "TypeMerger.h"
|
#include "TypeMerger.h"
|
||||||
#include "lld/Common/ErrorHandler.h"
|
#include "lld/Common/ErrorHandler.h"
|
||||||
#include "lld/Common/Memory.h"
|
#include "lld/Common/Memory.h"
|
||||||
|
#include "lld/Common/Timer.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
|
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
|
||||||
|
@ -46,8 +46,8 @@ class TypeServerIpiSource;
|
||||||
// before any dependent OBJ.
|
// before any dependent OBJ.
|
||||||
class TypeServerSource : public TpiSource {
|
class TypeServerSource : public TpiSource {
|
||||||
public:
|
public:
|
||||||
explicit TypeServerSource(COFFLinkerContext &ctx, PDBInputFile *f)
|
explicit TypeServerSource(PDBInputFile *f)
|
||||||
: TpiSource(ctx, PDB, nullptr), pdbInputFile(f) {
|
: TpiSource(PDB, nullptr), pdbInputFile(f) {
|
||||||
if (f->loadErr && *f->loadErr)
|
if (f->loadErr && *f->loadErr)
|
||||||
return;
|
return;
|
||||||
pdb::PDBFile &file = f->session->getPDBFile();
|
pdb::PDBFile &file = f->session->getPDBFile();
|
||||||
|
@ -55,7 +55,7 @@ public:
|
||||||
if (!expectedInfo)
|
if (!expectedInfo)
|
||||||
return;
|
return;
|
||||||
Guid = expectedInfo->getGuid();
|
Guid = expectedInfo->getGuid();
|
||||||
auto it = ctx.typeServerSourceMappings.emplace(Guid, this);
|
auto it = mappings.emplace(Guid, this);
|
||||||
assert(it.second);
|
assert(it.second);
|
||||||
(void)it;
|
(void)it;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,8 @@ public:
|
||||||
|
|
||||||
// The PDB signature GUID.
|
// The PDB signature GUID.
|
||||||
codeview::GUID Guid;
|
codeview::GUID Guid;
|
||||||
|
|
||||||
|
static std::map<codeview::GUID, TypeServerSource *> mappings;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Companion to TypeServerSource. Stores the index map for the IPI stream in the
|
// Companion to TypeServerSource. Stores the index map for the IPI stream in the
|
||||||
|
@ -81,8 +83,7 @@ public:
|
||||||
// invariant of one type index space per source.
|
// invariant of one type index space per source.
|
||||||
class TypeServerIpiSource : public TpiSource {
|
class TypeServerIpiSource : public TpiSource {
|
||||||
public:
|
public:
|
||||||
explicit TypeServerIpiSource(COFFLinkerContext &ctx)
|
explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {}
|
||||||
: TpiSource(ctx, PDBIpi, nullptr) {}
|
|
||||||
|
|
||||||
friend class TypeServerSource;
|
friend class TypeServerSource;
|
||||||
|
|
||||||
|
@ -100,8 +101,8 @@ class UseTypeServerSource : public TpiSource {
|
||||||
Expected<TypeServerSource *> getTypeServerSource();
|
Expected<TypeServerSource *> getTypeServerSource();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UseTypeServerSource(COFFLinkerContext &ctx, ObjFile *f, TypeServer2Record ts)
|
UseTypeServerSource(ObjFile *f, TypeServer2Record ts)
|
||||||
: TpiSource(ctx, UsingPDB, f), typeServerDependency(ts) {}
|
: TpiSource(UsingPDB, f), typeServerDependency(ts) {}
|
||||||
|
|
||||||
Error mergeDebugT(TypeMerger *m) override;
|
Error mergeDebugT(TypeMerger *m) override;
|
||||||
|
|
||||||
|
@ -120,11 +121,11 @@ public:
|
||||||
// such files, clang does not.
|
// such files, clang does not.
|
||||||
class PrecompSource : public TpiSource {
|
class PrecompSource : public TpiSource {
|
||||||
public:
|
public:
|
||||||
PrecompSource(COFFLinkerContext &ctx, ObjFile *f) : TpiSource(ctx, PCH, f) {
|
PrecompSource(ObjFile *f) : TpiSource(PCH, f) {
|
||||||
if (!f->pchSignature || !*f->pchSignature)
|
if (!f->pchSignature || !*f->pchSignature)
|
||||||
fatal(toString(f) +
|
fatal(toString(f) +
|
||||||
" claims to be a PCH object, but does not have a valid signature");
|
" claims to be a PCH object, but does not have a valid signature");
|
||||||
auto it = ctx.precompSourceMappings.emplace(*f->pchSignature, this);
|
auto it = mappings.emplace(*f->pchSignature, this);
|
||||||
if (!it.second)
|
if (!it.second)
|
||||||
fatal("a PCH object with the same signature has already been provided (" +
|
fatal("a PCH object with the same signature has already been provided (" +
|
||||||
toString(it.first->second->file) + " and " + toString(file) + ")");
|
toString(it.first->second->file) + " and " + toString(file) + ")");
|
||||||
|
@ -133,14 +134,16 @@ public:
|
||||||
void loadGHashes() override;
|
void loadGHashes() override;
|
||||||
|
|
||||||
bool isDependency() const override { return true; }
|
bool isDependency() const override { return true; }
|
||||||
|
|
||||||
|
static std::map<uint32_t, PrecompSource *> mappings;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class represents the debug type stream of an OBJ file that depends on a
|
// This class represents the debug type stream of an OBJ file that depends on a
|
||||||
// Microsoft precompiled headers OBJ (see PrecompSource).
|
// Microsoft precompiled headers OBJ (see PrecompSource).
|
||||||
class UsePrecompSource : public TpiSource {
|
class UsePrecompSource : public TpiSource {
|
||||||
public:
|
public:
|
||||||
UsePrecompSource(COFFLinkerContext &ctx, ObjFile *f, PrecompRecord precomp)
|
UsePrecompSource(ObjFile *f, PrecompRecord precomp)
|
||||||
: TpiSource(ctx, UsingPCH, f), precompDependency(precomp) {}
|
: TpiSource(UsingPCH, f), precompDependency(precomp) {}
|
||||||
|
|
||||||
Error mergeDebugT(TypeMerger *m) override;
|
Error mergeDebugT(TypeMerger *m) override;
|
||||||
|
|
||||||
|
@ -150,10 +153,6 @@ public:
|
||||||
private:
|
private:
|
||||||
Error mergeInPrecompHeaderObj();
|
Error mergeInPrecompHeaderObj();
|
||||||
|
|
||||||
PrecompSource *findObjByName(StringRef fileNameOnly);
|
|
||||||
PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr);
|
|
||||||
Expected<PrecompSource *> findPrecompMap(ObjFile *file, PrecompRecord &pr);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Information about the Precomp OBJ dependency, that needs to be loaded in
|
// Information about the Precomp OBJ dependency, that needs to be loaded in
|
||||||
// before merging this OBJ.
|
// before merging this OBJ.
|
||||||
|
@ -161,9 +160,13 @@ public:
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TpiSource::TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f)
|
std::vector<TpiSource *> TpiSource::instances;
|
||||||
: ctx(ctx), kind(k), tpiSrcIdx(ctx.tpiSourceList.size()), file(f) {
|
ArrayRef<TpiSource *> TpiSource::dependencySources;
|
||||||
ctx.addTpiSource(this);
|
ArrayRef<TpiSource *> TpiSource::objectSources;
|
||||||
|
|
||||||
|
TpiSource::TpiSource(TpiKind k, ObjFile *f)
|
||||||
|
: kind(k), tpiSrcIdx(instances.size()), file(f) {
|
||||||
|
instances.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vtable key method.
|
// Vtable key method.
|
||||||
|
@ -172,35 +175,52 @@ TpiSource::~TpiSource() {
|
||||||
consumeError(std::move(typeMergingError));
|
consumeError(std::move(typeMergingError));
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeTpiSource(COFFLinkerContext &ctx, ObjFile *file) {
|
void TpiSource::sortDependencies() {
|
||||||
return make<TpiSource>(ctx, TpiSource::Regular, file);
|
// Order dependencies first, but preserve the existing order.
|
||||||
|
std::vector<TpiSource *> deps;
|
||||||
|
std::vector<TpiSource *> objs;
|
||||||
|
for (TpiSource *s : instances)
|
||||||
|
(s->isDependency() ? deps : objs).push_back(s);
|
||||||
|
uint32_t numDeps = deps.size();
|
||||||
|
uint32_t numObjs = objs.size();
|
||||||
|
instances = std::move(deps);
|
||||||
|
instances.insert(instances.end(), objs.begin(), objs.end());
|
||||||
|
for (uint32_t i = 0, e = instances.size(); i < e; ++i)
|
||||||
|
instances[i]->tpiSrcIdx = i;
|
||||||
|
dependencySources = makeArrayRef(instances.data(), numDeps);
|
||||||
|
objectSources = makeArrayRef(instances.data() + numDeps, numObjs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeTypeServerSource(COFFLinkerContext &ctx,
|
TpiSource *lld::coff::makeTpiSource(ObjFile *file) {
|
||||||
PDBInputFile *pdbInputFile) {
|
return make<TpiSource>(TpiSource::Regular, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) {
|
||||||
// Type server sources come in pairs: the TPI stream, and the IPI stream.
|
// Type server sources come in pairs: the TPI stream, and the IPI stream.
|
||||||
auto *tpiSource = make<TypeServerSource>(ctx, pdbInputFile);
|
auto *tpiSource = make<TypeServerSource>(pdbInputFile);
|
||||||
if (pdbInputFile->session->getPDBFile().hasPDBIpiStream())
|
if (pdbInputFile->session->getPDBFile().hasPDBIpiStream())
|
||||||
tpiSource->ipiSrc = make<TypeServerIpiSource>(ctx);
|
tpiSource->ipiSrc = make<TypeServerIpiSource>();
|
||||||
return tpiSource;
|
return tpiSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeUseTypeServerSource(COFFLinkerContext &ctx,
|
TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file,
|
||||||
ObjFile *file,
|
|
||||||
TypeServer2Record ts) {
|
TypeServer2Record ts) {
|
||||||
return make<UseTypeServerSource>(ctx, file, ts);
|
return make<UseTypeServerSource>(file, ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makePrecompSource(COFFLinkerContext &ctx, ObjFile *file) {
|
TpiSource *lld::coff::makePrecompSource(ObjFile *file) {
|
||||||
return make<PrecompSource>(ctx, file);
|
return make<PrecompSource>(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeUsePrecompSource(COFFLinkerContext &ctx,
|
TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file,
|
||||||
ObjFile *file,
|
|
||||||
PrecompRecord precomp) {
|
PrecompRecord precomp) {
|
||||||
return make<UsePrecompSource>(ctx, file, precomp);
|
return make<UsePrecompSource>(file, precomp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<codeview::GUID, TypeServerSource *> TypeServerSource::mappings;
|
||||||
|
|
||||||
|
std::map<uint32_t, PrecompSource *> PrecompSource::mappings;
|
||||||
|
|
||||||
bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const {
|
bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const {
|
||||||
if (ti.isSimple())
|
if (ti.isSimple())
|
||||||
return true;
|
return true;
|
||||||
|
@ -399,12 +419,12 @@ Expected<TypeServerSource *> UseTypeServerSource::getTypeServerSource() {
|
||||||
StringRef tsPath = typeServerDependency.getName();
|
StringRef tsPath = typeServerDependency.getName();
|
||||||
|
|
||||||
TypeServerSource *tsSrc;
|
TypeServerSource *tsSrc;
|
||||||
auto it = ctx.typeServerSourceMappings.find(tsId);
|
auto it = TypeServerSource::mappings.find(tsId);
|
||||||
if (it != ctx.typeServerSourceMappings.end()) {
|
if (it != TypeServerSource::mappings.end()) {
|
||||||
tsSrc = (TypeServerSource *)it->second;
|
tsSrc = it->second;
|
||||||
} else {
|
} else {
|
||||||
// The file failed to load, lookup by name
|
// The file failed to load, lookup by name
|
||||||
PDBInputFile *pdb = PDBInputFile::findFromRecordPath(ctx, tsPath, file);
|
PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file);
|
||||||
if (!pdb)
|
if (!pdb)
|
||||||
return createFileError(tsPath, errorCodeToError(std::error_code(
|
return createFileError(tsPath, errorCodeToError(std::error_code(
|
||||||
ENOENT, std::generic_category())));
|
ENOENT, std::generic_category())));
|
||||||
|
@ -451,37 +471,36 @@ static bool equalsPath(StringRef path1, StringRef path2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find by name an OBJ provided on the command line
|
// Find by name an OBJ provided on the command line
|
||||||
PrecompSource *UsePrecompSource::findObjByName(StringRef fileNameOnly) {
|
static PrecompSource *findObjByName(StringRef fileNameOnly) {
|
||||||
SmallString<128> currentPath;
|
SmallString<128> currentPath;
|
||||||
for (auto kv : ctx.precompSourceMappings) {
|
for (auto kv : PrecompSource::mappings) {
|
||||||
StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
|
StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
|
||||||
sys::path::Style::windows);
|
sys::path::Style::windows);
|
||||||
|
|
||||||
// Compare based solely on the file name (link.exe behavior)
|
// Compare based solely on the file name (link.exe behavior)
|
||||||
if (equalsPath(currentFileName, fileNameOnly))
|
if (equalsPath(currentFileName, fileNameOnly))
|
||||||
return (PrecompSource *)kv.second;
|
return kv.second;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrecompSource *UsePrecompSource::findPrecompSource(ObjFile *file,
|
static PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr) {
|
||||||
PrecompRecord &pr) {
|
|
||||||
// Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
|
// Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
|
||||||
// records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
|
// records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
|
||||||
// the paths embedded in the OBJs are in the Windows format.
|
// the paths embedded in the OBJs are in the Windows format.
|
||||||
SmallString<128> prFileName =
|
SmallString<128> prFileName =
|
||||||
sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
|
sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
|
||||||
|
|
||||||
auto it = ctx.precompSourceMappings.find(pr.getSignature());
|
auto it = PrecompSource::mappings.find(pr.getSignature());
|
||||||
if (it != ctx.precompSourceMappings.end()) {
|
if (it != PrecompSource::mappings.end()) {
|
||||||
return (PrecompSource *)it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
// Lookup by name
|
// Lookup by name
|
||||||
return findObjByName(prFileName);
|
return findObjByName(prFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<PrecompSource *> UsePrecompSource::findPrecompMap(ObjFile *file,
|
static Expected<PrecompSource *> findPrecompMap(ObjFile *file,
|
||||||
PrecompRecord &pr) {
|
PrecompRecord &pr) {
|
||||||
PrecompSource *precomp = findPrecompSource(file, pr);
|
PrecompSource *precomp = findPrecompSource(file, pr);
|
||||||
|
|
||||||
if (!precomp)
|
if (!precomp)
|
||||||
|
@ -536,6 +555,22 @@ Error UsePrecompSource::mergeDebugT(TypeMerger *m) {
|
||||||
return TpiSource::mergeDebugT(m);
|
return TpiSource::mergeDebugT(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t TpiSource::countTypeServerPDBs() {
|
||||||
|
return TypeServerSource::mappings.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TpiSource::countPrecompObjs() {
|
||||||
|
return PrecompSource::mappings.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TpiSource::clear() {
|
||||||
|
// Clean up any owned ghash allocations.
|
||||||
|
clearGHashes();
|
||||||
|
TpiSource::instances.clear();
|
||||||
|
TypeServerSource::mappings.clear();
|
||||||
|
PrecompSource::mappings.clear();
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Parellel GHash type merging implementation.
|
// Parellel GHash type merging implementation.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -891,8 +926,7 @@ struct GHashTable {
|
||||||
/// Insert the cell with the given ghash into the table. Return the insertion
|
/// Insert the cell with the given ghash into the table. Return the insertion
|
||||||
/// position in the table. It is safe for the caller to store the insertion
|
/// position in the table. It is safe for the caller to store the insertion
|
||||||
/// position because the table cannot be resized.
|
/// position because the table cannot be resized.
|
||||||
uint32_t insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
|
uint32_t insert(GloballyHashedType ghash, GHashCell newCell);
|
||||||
GHashCell newCell);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A ghash table cell for deduplicating types from TpiSources.
|
/// A ghash table cell for deduplicating types from TpiSources.
|
||||||
|
@ -931,8 +965,8 @@ public:
|
||||||
bool isItem() const { return data & (1ULL << 63U); }
|
bool isItem() const { return data & (1ULL << 63U); }
|
||||||
|
|
||||||
/// Get the ghash key for this cell.
|
/// Get the ghash key for this cell.
|
||||||
GloballyHashedType getGHash(const COFFLinkerContext &ctx) const {
|
GloballyHashedType getGHash() const {
|
||||||
return ctx.tpiSourceList[getTpiSrcIdx()]->ghashes[getGHashIdx()];
|
return TpiSource::instances[getTpiSrcIdx()]->ghashes[getGHashIdx()];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The priority function for the cell. The data is stored such that lower
|
/// The priority function for the cell. The data is stored such that lower
|
||||||
|
@ -962,8 +996,7 @@ void GHashTable::init(uint32_t newTableSize) {
|
||||||
tableSize = newTableSize;
|
tableSize = newTableSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
|
uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) {
|
||||||
GHashCell newCell) {
|
|
||||||
assert(!newCell.isEmpty() && "cannot insert empty cell value");
|
assert(!newCell.isEmpty() && "cannot insert empty cell value");
|
||||||
|
|
||||||
// FIXME: The low bytes of SHA1 have low entropy for short records, which
|
// FIXME: The low bytes of SHA1 have low entropy for short records, which
|
||||||
|
@ -982,7 +1015,7 @@ uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
|
||||||
// - cell has non-matching key: hash collision, probe next cell
|
// - cell has non-matching key: hash collision, probe next cell
|
||||||
auto *cellPtr = reinterpret_cast<std::atomic<GHashCell> *>(&table[idx]);
|
auto *cellPtr = reinterpret_cast<std::atomic<GHashCell> *>(&table[idx]);
|
||||||
GHashCell oldCell(cellPtr->load());
|
GHashCell oldCell(cellPtr->load());
|
||||||
while (oldCell.isEmpty() || oldCell.getGHash(ctx) == ghash) {
|
while (oldCell.isEmpty() || oldCell.getGHash() == ghash) {
|
||||||
// Check if there is an existing ghash entry with a higher priority
|
// Check if there is an existing ghash entry with a higher priority
|
||||||
// (earlier ordering). If so, this is a duplicate, we are done.
|
// (earlier ordering). If so, this is a duplicate, we are done.
|
||||||
if (!oldCell.isEmpty() && oldCell < newCell)
|
if (!oldCell.isEmpty() && oldCell < newCell)
|
||||||
|
@ -1007,22 +1040,22 @@ uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
|
||||||
llvm_unreachable("left infloop");
|
llvm_unreachable("left infloop");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeMerger::TypeMerger(COFFLinkerContext &c, llvm::BumpPtrAllocator &alloc)
|
TypeMerger::TypeMerger(llvm::BumpPtrAllocator &alloc)
|
||||||
: typeTable(alloc), idTable(alloc), ctx(c) {}
|
: typeTable(alloc), idTable(alloc) {}
|
||||||
|
|
||||||
TypeMerger::~TypeMerger() = default;
|
TypeMerger::~TypeMerger() = default;
|
||||||
|
|
||||||
void TypeMerger::mergeTypesWithGHash() {
|
void TypeMerger::mergeTypesWithGHash() {
|
||||||
// Load ghashes. Do type servers and PCH objects first.
|
// Load ghashes. Do type servers and PCH objects first.
|
||||||
{
|
{
|
||||||
ScopedTimer t1(ctx.loadGHashTimer);
|
ScopedTimer t1(loadGHashTimer);
|
||||||
parallelForEach(dependencySources,
|
parallelForEach(TpiSource::dependencySources,
|
||||||
[&](TpiSource *source) { source->loadGHashes(); });
|
[&](TpiSource *source) { source->loadGHashes(); });
|
||||||
parallelForEach(objectSources,
|
parallelForEach(TpiSource::objectSources,
|
||||||
[&](TpiSource *source) { source->loadGHashes(); });
|
[&](TpiSource *source) { source->loadGHashes(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedTimer t2(ctx.mergeGHashTimer);
|
ScopedTimer t2(mergeGHashTimer);
|
||||||
GHashState ghashState;
|
GHashState ghashState;
|
||||||
|
|
||||||
// Estimate the size of hash table needed to deduplicate ghashes. This *must*
|
// Estimate the size of hash table needed to deduplicate ghashes. This *must*
|
||||||
|
@ -1033,7 +1066,7 @@ void TypeMerger::mergeTypesWithGHash() {
|
||||||
// small compared to total memory usage, at eight bytes per input type record,
|
// small compared to total memory usage, at eight bytes per input type record,
|
||||||
// and most input type records are larger than eight bytes.
|
// and most input type records are larger than eight bytes.
|
||||||
size_t tableSize = 0;
|
size_t tableSize = 0;
|
||||||
for (TpiSource *source : ctx.tpiSourceList)
|
for (TpiSource *source : TpiSource::instances)
|
||||||
tableSize += source->ghashes.size();
|
tableSize += source->ghashes.size();
|
||||||
|
|
||||||
// Cap the table size so that we can use 32-bit cell indices. Type indices are
|
// Cap the table size so that we can use 32-bit cell indices. Type indices are
|
||||||
|
@ -1047,8 +1080,8 @@ void TypeMerger::mergeTypesWithGHash() {
|
||||||
// position. Because the table does not rehash, the position will not change
|
// position. Because the table does not rehash, the position will not change
|
||||||
// under insertion. After insertion is done, the value of the cell can be read
|
// under insertion. After insertion is done, the value of the cell can be read
|
||||||
// to retrieve the final PDB type index.
|
// to retrieve the final PDB type index.
|
||||||
parallelForEachN(0, ctx.tpiSourceList.size(), [&](size_t tpiSrcIdx) {
|
parallelForEachN(0, TpiSource::instances.size(), [&](size_t tpiSrcIdx) {
|
||||||
TpiSource *source = ctx.tpiSourceList[tpiSrcIdx];
|
TpiSource *source = TpiSource::instances[tpiSrcIdx];
|
||||||
source->indexMapStorage.resize(source->ghashes.size());
|
source->indexMapStorage.resize(source->ghashes.size());
|
||||||
for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) {
|
for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) {
|
||||||
if (source->shouldOmitFromPdb(i)) {
|
if (source->shouldOmitFromPdb(i)) {
|
||||||
|
@ -1058,7 +1091,7 @@ void TypeMerger::mergeTypesWithGHash() {
|
||||||
GloballyHashedType ghash = source->ghashes[i];
|
GloballyHashedType ghash = source->ghashes[i];
|
||||||
bool isItem = source->isItemIndex.test(i);
|
bool isItem = source->isItemIndex.test(i);
|
||||||
uint32_t cellIdx =
|
uint32_t cellIdx =
|
||||||
ghashState.table.insert(ctx, ghash, GHashCell(isItem, tpiSrcIdx, i));
|
ghashState.table.insert(ghash, GHashCell(isItem, tpiSrcIdx, i));
|
||||||
|
|
||||||
// Store the ghash cell index as a type index in indexMapStorage. Later
|
// Store the ghash cell index as a type index in indexMapStorage. Later
|
||||||
// we will replace it with the PDB type index.
|
// we will replace it with the PDB type index.
|
||||||
|
@ -1104,7 +1137,7 @@ void TypeMerger::mergeTypesWithGHash() {
|
||||||
for (uint32_t i = 0, e = entries.size(); i < e; ++i) {
|
for (uint32_t i = 0, e = entries.size(); i < e; ++i) {
|
||||||
auto &cell = entries[i];
|
auto &cell = entries[i];
|
||||||
uint32_t tpiSrcIdx = cell.getTpiSrcIdx();
|
uint32_t tpiSrcIdx = cell.getTpiSrcIdx();
|
||||||
TpiSource *source = ctx.tpiSourceList[tpiSrcIdx];
|
TpiSource *source = TpiSource::instances[tpiSrcIdx];
|
||||||
source->uniqueTypes.push_back(cell.getGHashIdx());
|
source->uniqueTypes.push_back(cell.getGHashIdx());
|
||||||
|
|
||||||
// Update the ghash table to store the destination PDB type index in the
|
// Update the ghash table to store the destination PDB type index in the
|
||||||
|
@ -1117,37 +1150,21 @@ void TypeMerger::mergeTypesWithGHash() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// In parallel, remap all types.
|
// In parallel, remap all types.
|
||||||
for_each(dependencySources, [&](TpiSource *source) {
|
for_each(TpiSource::dependencySources, [&](TpiSource *source) {
|
||||||
source->remapTpiWithGHashes(&ghashState);
|
source->remapTpiWithGHashes(&ghashState);
|
||||||
});
|
});
|
||||||
parallelForEach(objectSources, [&](TpiSource *source) {
|
parallelForEach(TpiSource::objectSources, [&](TpiSource *source) {
|
||||||
source->remapTpiWithGHashes(&ghashState);
|
source->remapTpiWithGHashes(&ghashState);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build a global map of from function ID to function type.
|
// Build a global map of from function ID to function type.
|
||||||
for (TpiSource *source : ctx.tpiSourceList) {
|
for (TpiSource *source : TpiSource::instances) {
|
||||||
for (auto idToType : source->funcIdToType)
|
for (auto idToType : source->funcIdToType)
|
||||||
funcIdToType.insert(idToType);
|
funcIdToType.insert(idToType);
|
||||||
source->funcIdToType.clear();
|
source->funcIdToType.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.clearGHashes();
|
TpiSource::clearGHashes();
|
||||||
}
|
|
||||||
|
|
||||||
void TypeMerger::sortDependencies() {
|
|
||||||
// Order dependencies first, but preserve the existing order.
|
|
||||||
std::vector<TpiSource *> deps;
|
|
||||||
std::vector<TpiSource *> objs;
|
|
||||||
for (TpiSource *s : ctx.tpiSourceList)
|
|
||||||
(s->isDependency() ? deps : objs).push_back(s);
|
|
||||||
uint32_t numDeps = deps.size();
|
|
||||||
uint32_t numObjs = objs.size();
|
|
||||||
ctx.tpiSourceList = std::move(deps);
|
|
||||||
ctx.tpiSourceList.insert(ctx.tpiSourceList.end(), objs.begin(), objs.end());
|
|
||||||
for (uint32_t i = 0, e = ctx.tpiSourceList.size(); i < e; ++i)
|
|
||||||
ctx.tpiSourceList[i]->tpiSrcIdx = i;
|
|
||||||
dependencySources = makeArrayRef(ctx.tpiSourceList.data(), numDeps);
|
|
||||||
objectSources = makeArrayRef(ctx.tpiSourceList.data() + numDeps, numObjs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the index into the ghash table for a particular type, return the type
|
/// Given the index into the ghash table for a particular type, return the type
|
||||||
|
@ -1170,3 +1187,13 @@ void TpiSource::fillMapFromGHashes(GHashState *g) {
|
||||||
loadPdbTypeIndexFromCell(g, fakeCellIndex.toArrayIndex());
|
loadPdbTypeIndexFromCell(g, fakeCellIndex.toArrayIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TpiSource::clearGHashes() {
|
||||||
|
for (TpiSource *src : TpiSource::instances) {
|
||||||
|
if (src->ownedGHashes)
|
||||||
|
delete[] src->ghashes.data();
|
||||||
|
src->ghashes = {};
|
||||||
|
src->isItemIndex.clear();
|
||||||
|
src->uniqueTypes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -37,13 +37,12 @@ class ObjFile;
|
||||||
class PDBInputFile;
|
class PDBInputFile;
|
||||||
class TypeMerger;
|
class TypeMerger;
|
||||||
struct GHashState;
|
struct GHashState;
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
class TpiSource {
|
class TpiSource {
|
||||||
public:
|
public:
|
||||||
enum TpiKind : uint8_t { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB };
|
enum TpiKind : uint8_t { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB };
|
||||||
|
|
||||||
TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f);
|
TpiSource(TpiKind k, ObjFile *f);
|
||||||
virtual ~TpiSource();
|
virtual ~TpiSource();
|
||||||
|
|
||||||
/// Produce a mapping from the type and item indices used in the object
|
/// Produce a mapping from the type and item indices used in the object
|
||||||
|
@ -94,8 +93,6 @@ protected:
|
||||||
// Walk over file->debugTypes and fill in the isItemIndex bit vector.
|
// Walk over file->debugTypes and fill in the isItemIndex bit vector.
|
||||||
void fillIsItemIndexFromDebugT();
|
void fillIsItemIndexFromDebugT();
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool remapTypesInSymbolRecord(MutableArrayRef<uint8_t> rec);
|
bool remapTypesInSymbolRecord(MutableArrayRef<uint8_t> rec);
|
||||||
|
|
||||||
|
@ -112,6 +109,29 @@ public:
|
||||||
return ghashIdx == endPrecompGHashIdx;
|
return ghashIdx == endPrecompGHashIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All sources of type information in the program.
|
||||||
|
static std::vector<TpiSource *> instances;
|
||||||
|
|
||||||
|
/// Dependency type sources, such as type servers or PCH object files. These
|
||||||
|
/// must be processed before objects that rely on them. Set by
|
||||||
|
/// TpiSources::sortDependencies.
|
||||||
|
static ArrayRef<TpiSource *> dependencySources;
|
||||||
|
|
||||||
|
/// Object file sources. These must be processed after dependencySources.
|
||||||
|
static ArrayRef<TpiSource *> objectSources;
|
||||||
|
|
||||||
|
/// Sorts the dependencies and reassigns TpiSource indices.
|
||||||
|
static void sortDependencies();
|
||||||
|
|
||||||
|
static uint32_t countTypeServerPDBs();
|
||||||
|
static uint32_t countPrecompObjs();
|
||||||
|
|
||||||
|
/// Free heap allocated ghashes.
|
||||||
|
static void clearGHashes();
|
||||||
|
|
||||||
|
/// Clear global data structures for TpiSources.
|
||||||
|
static void clear();
|
||||||
|
|
||||||
const TpiKind kind;
|
const TpiKind kind;
|
||||||
bool ownedGHashes = true;
|
bool ownedGHashes = true;
|
||||||
uint32_t tpiSrcIdx = 0;
|
uint32_t tpiSrcIdx = 0;
|
||||||
|
@ -166,13 +186,12 @@ public:
|
||||||
uint64_t nbTypeRecordsBytes = 0;
|
uint64_t nbTypeRecordsBytes = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
TpiSource *makeTpiSource(COFFLinkerContext &ctx, ObjFile *f);
|
TpiSource *makeTpiSource(ObjFile *file);
|
||||||
TpiSource *makeTypeServerSource(COFFLinkerContext &ctx,
|
TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile);
|
||||||
PDBInputFile *pdbInputFile);
|
TpiSource *makeUseTypeServerSource(ObjFile *file,
|
||||||
TpiSource *makeUseTypeServerSource(COFFLinkerContext &ctx, ObjFile *file,
|
|
||||||
llvm::codeview::TypeServer2Record ts);
|
llvm::codeview::TypeServer2Record ts);
|
||||||
TpiSource *makePrecompSource(COFFLinkerContext &ctx, ObjFile *file);
|
TpiSource *makePrecompSource(ObjFile *file);
|
||||||
TpiSource *makeUsePrecompSource(COFFLinkerContext &ctx, ObjFile *file,
|
TpiSource *makeUsePrecompSource(ObjFile *file,
|
||||||
llvm::codeview::PrecompRecord ts);
|
llvm::codeview::PrecompRecord ts);
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "Driver.h"
|
#include "Driver.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DebugTypes.h"
|
#include "DebugTypes.h"
|
||||||
#include "ICF.h"
|
#include "ICF.h"
|
||||||
|
@ -60,6 +59,8 @@ using namespace llvm::sys;
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
|
static Timer inputFileTimer("Input File Reading", Timer::root());
|
||||||
|
|
||||||
Configuration *config;
|
Configuration *config;
|
||||||
LinkerDriver *driver;
|
LinkerDriver *driver;
|
||||||
|
|
||||||
|
@ -69,7 +70,14 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
|
||||||
lld::stderrOS = &stderrOS;
|
lld::stderrOS = &stderrOS;
|
||||||
|
|
||||||
errorHandler().cleanupCallback = []() {
|
errorHandler().cleanupCallback = []() {
|
||||||
|
TpiSource::clear();
|
||||||
freeArena();
|
freeArena();
|
||||||
|
ObjFile::instances.clear();
|
||||||
|
PDBInputFile::instances.clear();
|
||||||
|
ImportFile::instances.clear();
|
||||||
|
BitcodeFile::instances.clear();
|
||||||
|
memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
|
||||||
|
OutputSection::clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
||||||
|
@ -79,9 +87,9 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
|
||||||
errorHandler().exitEarly = canExitEarly;
|
errorHandler().exitEarly = canExitEarly;
|
||||||
stderrOS.enable_colors(stderrOS.has_colors());
|
stderrOS.enable_colors(stderrOS.has_colors());
|
||||||
|
|
||||||
COFFLinkerContext ctx;
|
|
||||||
config = make<Configuration>();
|
config = make<Configuration>();
|
||||||
driver = make<LinkerDriver>(ctx);
|
symtab = make<SymbolTable>();
|
||||||
|
driver = make<LinkerDriver>();
|
||||||
|
|
||||||
driver->linkerMain(args);
|
driver->linkerMain(args);
|
||||||
|
|
||||||
|
@ -166,8 +174,8 @@ static StringRef mangle(StringRef sym) {
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LinkerDriver::findUnderscoreMangle(StringRef sym) {
|
static bool findUnderscoreMangle(StringRef sym) {
|
||||||
Symbol *s = ctx.symtab.findMangle(mangle(sym));
|
Symbol *s = symtab->findMangle(mangle(sym));
|
||||||
return s && !isa<Undefined>(s);
|
return s && !isa<Undefined>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,30 +213,30 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||||
addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
|
addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.symtab.addFile(make<ArchiveFile>(ctx, mbref));
|
symtab->addFile(make<ArchiveFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::bitcode:
|
case file_magic::bitcode:
|
||||||
if (lazy)
|
if (lazy)
|
||||||
ctx.symtab.addFile(make<LazyObjFile>(ctx, mbref));
|
symtab->addFile(make<LazyObjFile>(mbref));
|
||||||
else
|
else
|
||||||
ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0));
|
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
||||||
break;
|
break;
|
||||||
case file_magic::coff_object:
|
case file_magic::coff_object:
|
||||||
case file_magic::coff_import_library:
|
case file_magic::coff_import_library:
|
||||||
if (lazy)
|
if (lazy)
|
||||||
ctx.symtab.addFile(make<LazyObjFile>(ctx, mbref));
|
symtab->addFile(make<LazyObjFile>(mbref));
|
||||||
else
|
else
|
||||||
ctx.symtab.addFile(make<ObjFile>(ctx, mbref));
|
symtab->addFile(make<ObjFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::pdb:
|
case file_magic::pdb:
|
||||||
ctx.symtab.addFile(make<PDBInputFile>(ctx, mbref));
|
symtab->addFile(make<PDBInputFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::coff_cl_gl_object:
|
case file_magic::coff_cl_gl_object:
|
||||||
error(filename + ": is not a native COFF file. Recompile without /GL");
|
error(filename + ": is not a native COFF file. Recompile without /GL");
|
||||||
break;
|
break;
|
||||||
case file_magic::pecoff_executable:
|
case file_magic::pecoff_executable:
|
||||||
if (config->mingw) {
|
if (config->mingw) {
|
||||||
ctx.symtab.addFile(make<DLLFile>(ctx, mbref));
|
symtab->addFile(make<DLLFile>(mbref));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (filename.endswith_insensitive(".dll")) {
|
if (filename.endswith_insensitive(".dll")) {
|
||||||
|
@ -272,24 +280,24 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
|
||||||
uint64_t offsetInArchive) {
|
uint64_t offsetInArchive) {
|
||||||
file_magic magic = identify_magic(mb.getBuffer());
|
file_magic magic = identify_magic(mb.getBuffer());
|
||||||
if (magic == file_magic::coff_import_library) {
|
if (magic == file_magic::coff_import_library) {
|
||||||
InputFile *imp = make<ImportFile>(ctx, mb);
|
InputFile *imp = make<ImportFile>(mb);
|
||||||
imp->parentName = parentName;
|
imp->parentName = parentName;
|
||||||
ctx.symtab.addFile(imp);
|
symtab->addFile(imp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputFile *obj;
|
InputFile *obj;
|
||||||
if (magic == file_magic::coff_object) {
|
if (magic == file_magic::coff_object) {
|
||||||
obj = make<ObjFile>(ctx, mb);
|
obj = make<ObjFile>(mb);
|
||||||
} else if (magic == file_magic::bitcode) {
|
} else if (magic == file_magic::bitcode) {
|
||||||
obj = make<BitcodeFile>(ctx, mb, parentName, offsetInArchive);
|
obj = make<BitcodeFile>(mb, parentName, offsetInArchive);
|
||||||
} else {
|
} else {
|
||||||
error("unknown file type: " + mb.getBufferIdentifier());
|
error("unknown file type: " + mb.getBufferIdentifier());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj->parentName = parentName;
|
obj->parentName = parentName;
|
||||||
ctx.symtab.addFile(obj);
|
symtab->addFile(obj);
|
||||||
log("Loaded " + toString(obj) + " for " + symName);
|
log("Loaded " + toString(obj) + " for " + symName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,7 +547,7 @@ void LinkerDriver::addLibSearchPaths() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *LinkerDriver::addUndefined(StringRef name) {
|
Symbol *LinkerDriver::addUndefined(StringRef name) {
|
||||||
Symbol *b = ctx.symtab.addUndefined(name);
|
Symbol *b = symtab->addUndefined(name);
|
||||||
if (!b->isGCRoot) {
|
if (!b->isGCRoot) {
|
||||||
b->isGCRoot = true;
|
b->isGCRoot = true;
|
||||||
config->gcroot.push_back(b);
|
config->gcroot.push_back(b);
|
||||||
|
@ -554,14 +562,14 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) {
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
// Otherwise, see if a similar, mangled symbol exists in the symbol table.
|
// Otherwise, see if a similar, mangled symbol exists in the symbol table.
|
||||||
Symbol *mangled = ctx.symtab.findMangle(unmangled->getName());
|
Symbol *mangled = symtab->findMangle(unmangled->getName());
|
||||||
if (!mangled)
|
if (!mangled)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
// If we find a similar mangled symbol, make this an alias to it and return
|
// If we find a similar mangled symbol, make this an alias to it and return
|
||||||
// its name.
|
// its name.
|
||||||
log(unmangled->getName() + " aliased to " + mangled->getName());
|
log(unmangled->getName() + " aliased to " + mangled->getName());
|
||||||
unmangled->weakAlias = ctx.symtab.addUndefined(mangled->getName());
|
unmangled->weakAlias = symtab->addUndefined(mangled->getName());
|
||||||
return mangled->getName();
|
return mangled->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,7 +939,7 @@ void LinkerDriver::enqueueTask(std::function<void()> task) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LinkerDriver::run() {
|
bool LinkerDriver::run() {
|
||||||
ScopedTimer t(ctx.inputFileTimer);
|
ScopedTimer t(inputFileTimer);
|
||||||
|
|
||||||
bool didWork = !taskQueue.empty();
|
bool didWork = !taskQueue.empty();
|
||||||
while (!taskQueue.empty()) {
|
while (!taskQueue.empty()) {
|
||||||
|
@ -944,7 +952,7 @@ bool LinkerDriver::run() {
|
||||||
// Parse an /order file. If an option is given, the linker places
|
// Parse an /order file. If an option is given, the linker places
|
||||||
// COMDAT sections in the same order as their names appear in the
|
// COMDAT sections in the same order as their names appear in the
|
||||||
// given file.
|
// given file.
|
||||||
static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) {
|
static void parseOrderFile(StringRef arg) {
|
||||||
// For some reason, the MSVC linker requires a filename to be
|
// For some reason, the MSVC linker requires a filename to be
|
||||||
// preceded by "@".
|
// preceded by "@".
|
||||||
if (!arg.startswith("@")) {
|
if (!arg.startswith("@")) {
|
||||||
|
@ -954,7 +962,7 @@ static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) {
|
||||||
|
|
||||||
// Get a list of all comdat sections for error checking.
|
// Get a list of all comdat sections for error checking.
|
||||||
DenseSet<StringRef> set;
|
DenseSet<StringRef> set;
|
||||||
for (Chunk *c : ctx.symtab.getChunks())
|
for (Chunk *c : symtab->getChunks())
|
||||||
if (auto *sec = dyn_cast<SectionChunk>(c))
|
if (auto *sec = dyn_cast<SectionChunk>(c))
|
||||||
if (sec->sym)
|
if (sec->sym)
|
||||||
set.insert(sec->sym->getName());
|
set.insert(sec->sym->getName());
|
||||||
|
@ -988,7 +996,7 @@ static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) {
|
||||||
driver->takeBuffer(std::move(mb));
|
driver->takeBuffer(std::move(mb));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
|
static void parseCallGraphFile(StringRef path) {
|
||||||
std::unique_ptr<MemoryBuffer> mb =
|
std::unique_ptr<MemoryBuffer> mb =
|
||||||
CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
|
CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
|
||||||
/*RequiresNullTerminator=*/false,
|
/*RequiresNullTerminator=*/false,
|
||||||
|
@ -997,7 +1005,7 @@ static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
|
||||||
|
|
||||||
// Build a map from symbol name to section.
|
// Build a map from symbol name to section.
|
||||||
DenseMap<StringRef, Symbol *> map;
|
DenseMap<StringRef, Symbol *> map;
|
||||||
for (ObjFile *file : ctx.objFileInstances)
|
for (ObjFile *file : ObjFile::instances)
|
||||||
for (Symbol *sym : file->getSymbols())
|
for (Symbol *sym : file->getSymbols())
|
||||||
if (sym)
|
if (sym)
|
||||||
map[sym->getName()] = sym;
|
map[sym->getName()] = sym;
|
||||||
|
@ -1034,8 +1042,8 @@ static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
|
||||||
driver->takeBuffer(std::move(mb));
|
driver->takeBuffer(std::move(mb));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) {
|
static void readCallGraphsFromObjectFiles() {
|
||||||
for (ObjFile *obj : ctx.objFileInstances) {
|
for (ObjFile *obj : ObjFile::instances) {
|
||||||
if (obj->callgraphSec) {
|
if (obj->callgraphSec) {
|
||||||
ArrayRef<uint8_t> contents;
|
ArrayRef<uint8_t> contents;
|
||||||
cantFail(
|
cantFail(
|
||||||
|
@ -1069,7 +1077,7 @@ static void markAddrsig(Symbol *s) {
|
||||||
c->keepUnique = true;
|
c->keepUnique = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void findKeepUniqueSections(COFFLinkerContext &ctx) {
|
static void findKeepUniqueSections() {
|
||||||
// Exported symbols could be address-significant in other executables or DSOs,
|
// Exported symbols could be address-significant in other executables or DSOs,
|
||||||
// so we conservatively mark them as address-significant.
|
// so we conservatively mark them as address-significant.
|
||||||
for (Export &r : config->exports)
|
for (Export &r : config->exports)
|
||||||
|
@ -1077,7 +1085,7 @@ static void findKeepUniqueSections(COFFLinkerContext &ctx) {
|
||||||
|
|
||||||
// Visit the address-significance table in each object file and mark each
|
// Visit the address-significance table in each object file and mark each
|
||||||
// referenced symbol as address-significant.
|
// referenced symbol as address-significant.
|
||||||
for (ObjFile *obj : ctx.objFileInstances) {
|
for (ObjFile *obj : ObjFile::instances) {
|
||||||
ArrayRef<Symbol *> syms = obj->getSymbols();
|
ArrayRef<Symbol *> syms = obj->getSymbols();
|
||||||
if (obj->addrsigSec) {
|
if (obj->addrsigSec) {
|
||||||
ArrayRef<uint8_t> contents;
|
ArrayRef<uint8_t> contents;
|
||||||
|
@ -1161,7 +1169,7 @@ static void parsePDBAltPath(StringRef altPath) {
|
||||||
void LinkerDriver::convertResources() {
|
void LinkerDriver::convertResources() {
|
||||||
std::vector<ObjFile *> resourceObjFiles;
|
std::vector<ObjFile *> resourceObjFiles;
|
||||||
|
|
||||||
for (ObjFile *f : ctx.objFileInstances) {
|
for (ObjFile *f : ObjFile::instances) {
|
||||||
if (f->isResourceObjFile())
|
if (f->isResourceObjFile())
|
||||||
resourceObjFiles.push_back(f);
|
resourceObjFiles.push_back(f);
|
||||||
}
|
}
|
||||||
|
@ -1183,9 +1191,8 @@ void LinkerDriver::convertResources() {
|
||||||
f->includeResourceChunks();
|
f->includeResourceChunks();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ObjFile *f =
|
ObjFile *f = make<ObjFile>(convertResToCOFF(resources, resourceObjFiles));
|
||||||
make<ObjFile>(ctx, convertResToCOFF(resources, resourceObjFiles));
|
symtab->addFile(f);
|
||||||
ctx.symtab.addFile(f);
|
|
||||||
f->includeResourceChunks();
|
f->includeResourceChunks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,9 +1219,9 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
|
||||||
if (Optional<StringRef> path = doFindFile(arg->getValue()))
|
if (Optional<StringRef> path = doFindFile(arg->getValue()))
|
||||||
exporter.addWholeArchive(*path);
|
exporter.addWholeArchive(*path);
|
||||||
|
|
||||||
ctx.symtab.forEachSymbol([&](Symbol *s) {
|
symtab->forEachSymbol([&](Symbol *s) {
|
||||||
auto *def = dyn_cast<Defined>(s);
|
auto *def = dyn_cast<Defined>(s);
|
||||||
if (!exporter.shouldExport(ctx, def))
|
if (!exporter.shouldExport(def))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!def->isGCRoot) {
|
if (!def->isGCRoot) {
|
||||||
|
@ -1259,7 +1266,7 @@ Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
ScopedTimer rootTimer(ctx.rootTimer);
|
ScopedTimer rootTimer(Timer::root());
|
||||||
|
|
||||||
// Needed for LTO.
|
// Needed for LTO.
|
||||||
InitializeAllTargetInfos();
|
InitializeAllTargetInfos();
|
||||||
|
@ -2011,32 +2018,32 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
if (config->imageBase == uint64_t(-1))
|
if (config->imageBase == uint64_t(-1))
|
||||||
config->imageBase = getDefaultImageBase();
|
config->imageBase = getDefaultImageBase();
|
||||||
|
|
||||||
ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr);
|
symtab->addSynthetic(mangle("__ImageBase"), nullptr);
|
||||||
if (config->machine == I386) {
|
if (config->machine == I386) {
|
||||||
ctx.symtab.addAbsolute("___safe_se_handler_table", 0);
|
symtab->addAbsolute("___safe_se_handler_table", 0);
|
||||||
ctx.symtab.addAbsolute("___safe_se_handler_count", 0);
|
symtab->addAbsolute("___safe_se_handler_count", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_fids_count"), 0);
|
symtab->addAbsolute(mangle("__guard_fids_count"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_fids_table"), 0);
|
symtab->addAbsolute(mangle("__guard_fids_table"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_flags"), 0);
|
symtab->addAbsolute(mangle("__guard_flags"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_iat_count"), 0);
|
symtab->addAbsolute(mangle("__guard_iat_count"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_iat_table"), 0);
|
symtab->addAbsolute(mangle("__guard_iat_table"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
|
symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
|
symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
|
||||||
// Needed for MSVC 2017 15.5 CRT.
|
// Needed for MSVC 2017 15.5 CRT.
|
||||||
ctx.symtab.addAbsolute(mangle("__enclave_config"), 0);
|
symtab->addAbsolute(mangle("__enclave_config"), 0);
|
||||||
// Needed for MSVC 2019 16.8 CRT.
|
// Needed for MSVC 2019 16.8 CRT.
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
|
symtab->addAbsolute(mangle("__guard_eh_cont_count"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
|
symtab->addAbsolute(mangle("__guard_eh_cont_table"), 0);
|
||||||
|
|
||||||
if (config->pseudoRelocs) {
|
if (config->pseudoRelocs) {
|
||||||
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
|
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
|
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
|
||||||
}
|
}
|
||||||
if (config->mingw) {
|
if (config->mingw) {
|
||||||
ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0);
|
symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
|
||||||
ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0);
|
symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This code may add new undefined symbols to the link, which may enqueue more
|
// This code may add new undefined symbols to the link, which may enqueue more
|
||||||
|
@ -2062,12 +2069,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
for (auto pair : config->alternateNames) {
|
for (auto pair : config->alternateNames) {
|
||||||
StringRef from = pair.first;
|
StringRef from = pair.first;
|
||||||
StringRef to = pair.second;
|
StringRef to = pair.second;
|
||||||
Symbol *sym = ctx.symtab.find(from);
|
Symbol *sym = symtab->find(from);
|
||||||
if (!sym)
|
if (!sym)
|
||||||
continue;
|
continue;
|
||||||
if (auto *u = dyn_cast<Undefined>(sym))
|
if (auto *u = dyn_cast<Undefined>(sym))
|
||||||
if (!u->weakAlias)
|
if (!u->weakAlias)
|
||||||
u->weakAlias = ctx.symtab.addUndefined(to);
|
u->weakAlias = symtab->addUndefined(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any inputs are bitcode files, the LTO code generator may create
|
// If any inputs are bitcode files, the LTO code generator may create
|
||||||
|
@ -2075,25 +2082,25 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
// file's symbol table. If any of those library functions are defined in a
|
// file's symbol table. If any of those library functions are defined in a
|
||||||
// bitcode file in an archive member, we need to arrange to use LTO to
|
// bitcode file in an archive member, we need to arrange to use LTO to
|
||||||
// compile those archive members by adding them to the link beforehand.
|
// compile those archive members by adding them to the link beforehand.
|
||||||
if (!ctx.bitcodeFileInstances.empty())
|
if (!BitcodeFile::instances.empty())
|
||||||
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
|
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
|
||||||
ctx.symtab.addLibcall(s);
|
symtab->addLibcall(s);
|
||||||
|
|
||||||
// Windows specific -- if __load_config_used can be resolved, resolve it.
|
// Windows specific -- if __load_config_used can be resolved, resolve it.
|
||||||
if (ctx.symtab.findUnderscore("_load_config_used"))
|
if (symtab->findUnderscore("_load_config_used"))
|
||||||
addUndefined(mangle("_load_config_used"));
|
addUndefined(mangle("_load_config_used"));
|
||||||
} while (run());
|
} while (run());
|
||||||
|
|
||||||
if (args.hasArg(OPT_include_optional)) {
|
if (args.hasArg(OPT_include_optional)) {
|
||||||
// Handle /includeoptional
|
// Handle /includeoptional
|
||||||
for (auto *arg : args.filtered(OPT_include_optional))
|
for (auto *arg : args.filtered(OPT_include_optional))
|
||||||
if (dyn_cast_or_null<LazyArchive>(ctx.symtab.find(arg->getValue())))
|
if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
|
||||||
addUndefined(arg->getValue());
|
addUndefined(arg->getValue());
|
||||||
while (run());
|
while (run());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create wrapped symbols for -wrap option.
|
// Create wrapped symbols for -wrap option.
|
||||||
std::vector<WrappedSymbol> wrapped = addWrappedSymbols(ctx, args);
|
std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
|
||||||
// Load more object files that might be needed for wrapped symbols.
|
// Load more object files that might be needed for wrapped symbols.
|
||||||
if (!wrapped.empty())
|
if (!wrapped.empty())
|
||||||
while (run());
|
while (run());
|
||||||
|
@ -2119,7 +2126,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
// If it ends up pulling in more object files from static libraries,
|
// If it ends up pulling in more object files from static libraries,
|
||||||
// (and maybe doing more stdcall fixups along the way), this would need
|
// (and maybe doing more stdcall fixups along the way), this would need
|
||||||
// to loop these two calls.
|
// to loop these two calls.
|
||||||
ctx.symtab.loadMinGWSymbols();
|
symtab->loadMinGWSymbols();
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2127,8 +2134,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
// If we are going to do codegen for link-time optimization, check for
|
// If we are going to do codegen for link-time optimization, check for
|
||||||
// unresolvable symbols first, so we don't spend time generating code that
|
// unresolvable symbols first, so we don't spend time generating code that
|
||||||
// will fail to link anyway.
|
// will fail to link anyway.
|
||||||
if (!ctx.bitcodeFileInstances.empty() && !config->forceUnresolved)
|
if (!BitcodeFile::instances.empty() && !config->forceUnresolved)
|
||||||
ctx.symtab.reportUnresolvable();
|
symtab->reportUnresolvable();
|
||||||
if (errorCount())
|
if (errorCount())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2142,7 +2149,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||||
// link those files (unless -thinlto-index-only was given, in which case we
|
// link those files (unless -thinlto-index-only was given, in which case we
|
||||||
// resolve symbols and write indices, but don't generate native code or link).
|
// resolve symbols and write indices, but don't generate native code or link).
|
||||||
ctx.symtab.addCombinedLTOObjects();
|
symtab->addCombinedLTOObjects();
|
||||||
|
|
||||||
// If -thinlto-index-only is given, we should create only "index
|
// If -thinlto-index-only is given, we should create only "index
|
||||||
// files" and not object files. Index file creation is already done
|
// files" and not object files. Index file creation is already done
|
||||||
|
@ -2156,10 +2163,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
|
|
||||||
// Apply symbol renames for -wrap.
|
// Apply symbol renames for -wrap.
|
||||||
if (!wrapped.empty())
|
if (!wrapped.empty())
|
||||||
wrapSymbols(ctx, wrapped);
|
wrapSymbols(wrapped);
|
||||||
|
|
||||||
// Resolve remaining undefined symbols and warn about imported locals.
|
// Resolve remaining undefined symbols and warn about imported locals.
|
||||||
ctx.symtab.resolveRemainingUndefines();
|
symtab->resolveRemainingUndefines();
|
||||||
if (errorCount())
|
if (errorCount())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2170,12 +2177,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
// order provided on the command line, while lld will pull in needed
|
// order provided on the command line, while lld will pull in needed
|
||||||
// files from static libraries only after the last object file on the
|
// files from static libraries only after the last object file on the
|
||||||
// command line.
|
// command line.
|
||||||
for (auto i = ctx.objFileInstances.begin(), e = ctx.objFileInstances.end();
|
for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end();
|
||||||
i != e; i++) {
|
i != e; i++) {
|
||||||
ObjFile *file = *i;
|
ObjFile *file = *i;
|
||||||
if (isCrtend(file->getName())) {
|
if (isCrtend(file->getName())) {
|
||||||
ctx.objFileInstances.erase(i);
|
ObjFile::instances.erase(i);
|
||||||
ctx.objFileInstances.push_back(file);
|
ObjFile::instances.push_back(file);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2200,7 +2207,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
StringRef name = pair.first;
|
StringRef name = pair.first;
|
||||||
uint32_t alignment = pair.second;
|
uint32_t alignment = pair.second;
|
||||||
|
|
||||||
Symbol *sym = ctx.symtab.find(name);
|
Symbol *sym = symtab->find(name);
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
warn("/aligncomm symbol " + name + " not found");
|
warn("/aligncomm symbol " + name + " not found");
|
||||||
continue;
|
continue;
|
||||||
|
@ -2232,16 +2239,16 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
if (auto *arg = args.getLastArg(OPT_order)) {
|
if (auto *arg = args.getLastArg(OPT_order)) {
|
||||||
if (args.hasArg(OPT_call_graph_ordering_file))
|
if (args.hasArg(OPT_call_graph_ordering_file))
|
||||||
error("/order and /call-graph-order-file may not be used together");
|
error("/order and /call-graph-order-file may not be used together");
|
||||||
parseOrderFile(ctx, arg->getValue());
|
parseOrderFile(arg->getValue());
|
||||||
config->callGraphProfileSort = false;
|
config->callGraphProfileSort = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle /call-graph-ordering-file and /call-graph-profile-sort (default on).
|
// Handle /call-graph-ordering-file and /call-graph-profile-sort (default on).
|
||||||
if (config->callGraphProfileSort) {
|
if (config->callGraphProfileSort) {
|
||||||
if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) {
|
if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) {
|
||||||
parseCallGraphFile(ctx, arg->getValue());
|
parseCallGraphFile(arg->getValue());
|
||||||
}
|
}
|
||||||
readCallGraphsFromObjectFiles(ctx);
|
readCallGraphsFromObjectFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle /print-symbol-order.
|
// Handle /print-symbol-order.
|
||||||
|
@ -2258,7 +2265,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
// functions. This doesn't bring in more object files, but only marks
|
// functions. This doesn't bring in more object files, but only marks
|
||||||
// functions that already have been included to be retained.
|
// functions that already have been included to be retained.
|
||||||
for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0"}) {
|
for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0"}) {
|
||||||
Defined *d = dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore(n));
|
Defined *d = dyn_cast_or_null<Defined>(symtab->findUnderscore(n));
|
||||||
if (d && !d->isGCRoot) {
|
if (d && !d->isGCRoot) {
|
||||||
d->isGCRoot = true;
|
d->isGCRoot = true;
|
||||||
config->gcroot.push_back(d);
|
config->gcroot.push_back(d);
|
||||||
|
@ -2266,7 +2273,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
markLive(ctx);
|
markLive(symtab->getChunks());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs to happen after the last call to addFile().
|
// Needs to happen after the last call to addFile().
|
||||||
|
@ -2274,17 +2281,17 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||||
|
|
||||||
// Identify identical COMDAT sections to merge them.
|
// Identify identical COMDAT sections to merge them.
|
||||||
if (config->doICF != ICFLevel::None) {
|
if (config->doICF != ICFLevel::None) {
|
||||||
findKeepUniqueSections(ctx);
|
findKeepUniqueSections();
|
||||||
doICF(ctx, config->doICF);
|
doICF(symtab->getChunks(), config->doICF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the result.
|
// Write the result.
|
||||||
writeResult(ctx);
|
writeResult();
|
||||||
|
|
||||||
// Stop early so we can print the results.
|
// Stop early so we can print the results.
|
||||||
rootTimer.stop();
|
rootTimer.stop();
|
||||||
if (config->showTiming)
|
if (config->showTiming)
|
||||||
ctx.rootTimer.print();
|
Timer::root().print();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#ifndef LLD_COFF_DRIVER_H
|
#ifndef LLD_COFF_DRIVER_H
|
||||||
#define LLD_COFF_DRIVER_H
|
#define LLD_COFF_DRIVER_H
|
||||||
|
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "lld/Common/LLVM.h"
|
#include "lld/Common/LLVM.h"
|
||||||
|
@ -79,8 +78,6 @@ private:
|
||||||
|
|
||||||
class LinkerDriver {
|
class LinkerDriver {
|
||||||
public:
|
public:
|
||||||
LinkerDriver(COFFLinkerContext &c) : ctx(c) {}
|
|
||||||
|
|
||||||
void linkerMain(llvm::ArrayRef<const char *> args);
|
void linkerMain(llvm::ArrayRef<const char *> args);
|
||||||
|
|
||||||
// Used by the resolver to parse .drectve section contents.
|
// Used by the resolver to parse .drectve section contents.
|
||||||
|
@ -106,8 +103,6 @@ private:
|
||||||
StringRef doFindLib(StringRef filename);
|
StringRef doFindLib(StringRef filename);
|
||||||
StringRef doFindLibMinGW(StringRef filename);
|
StringRef doFindLibMinGW(StringRef filename);
|
||||||
|
|
||||||
bool findUnderscoreMangle(StringRef sym);
|
|
||||||
|
|
||||||
// Parses LIB environment which contains a list of search paths.
|
// Parses LIB environment which contains a list of search paths.
|
||||||
void addLibSearchPaths();
|
void addLibSearchPaths();
|
||||||
|
|
||||||
|
@ -153,8 +148,6 @@ private:
|
||||||
std::vector<MemoryBufferRef> resources;
|
std::vector<MemoryBufferRef> resources;
|
||||||
|
|
||||||
llvm::StringSet<> directivesExports;
|
llvm::StringSet<> directivesExports;
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Functions below this line are defined in DriverUtils.cpp.
|
// Functions below this line are defined in DriverUtils.cpp.
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "ICF.h"
|
#include "ICF.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include "lld/Common/ErrorHandler.h"
|
#include "lld/Common/ErrorHandler.h"
|
||||||
|
@ -37,10 +36,12 @@ using namespace llvm;
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
|
static Timer icfTimer("ICF", Timer::root());
|
||||||
|
|
||||||
class ICF {
|
class ICF {
|
||||||
public:
|
public:
|
||||||
ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){};
|
ICF(ICFLevel icfLevel) : icfLevel(icfLevel){};
|
||||||
void run();
|
void run(ArrayRef<Chunk *> v);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void segregate(size_t begin, size_t end, bool constant);
|
void segregate(size_t begin, size_t end, bool constant);
|
||||||
|
@ -63,8 +64,6 @@ private:
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
std::atomic<bool> repeat = {false};
|
std::atomic<bool> repeat = {false};
|
||||||
ICFLevel icfLevel = ICFLevel::All;
|
ICFLevel icfLevel = ICFLevel::All;
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true if section S is subject of ICF.
|
// Returns true if section S is subject of ICF.
|
||||||
|
@ -247,12 +246,12 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {
|
||||||
// Merge identical COMDAT sections.
|
// Merge identical COMDAT sections.
|
||||||
// Two sections are considered the same if their section headers,
|
// Two sections are considered the same if their section headers,
|
||||||
// contents and relocations are all the same.
|
// contents and relocations are all the same.
|
||||||
void ICF::run() {
|
void ICF::run(ArrayRef<Chunk *> vec) {
|
||||||
ScopedTimer t(ctx.icfTimer);
|
ScopedTimer t(icfTimer);
|
||||||
|
|
||||||
// Collect only mergeable sections and group by hash value.
|
// Collect only mergeable sections and group by hash value.
|
||||||
uint32_t nextId = 1;
|
uint32_t nextId = 1;
|
||||||
for (Chunk *c : ctx.symtab.getChunks()) {
|
for (Chunk *c : vec) {
|
||||||
if (auto *sc = dyn_cast<SectionChunk>(c)) {
|
if (auto *sc = dyn_cast<SectionChunk>(c)) {
|
||||||
if (isEligible(sc))
|
if (isEligible(sc))
|
||||||
chunks.push_back(sc);
|
chunks.push_back(sc);
|
||||||
|
@ -263,7 +262,7 @@ void ICF::run() {
|
||||||
|
|
||||||
// Make sure that ICF doesn't merge sections that are being handled by string
|
// Make sure that ICF doesn't merge sections that are being handled by string
|
||||||
// tail merging.
|
// tail merging.
|
||||||
for (MergeChunk *mc : ctx.mergeChunkInstances)
|
for (MergeChunk *mc : MergeChunk::instances)
|
||||||
if (mc)
|
if (mc)
|
||||||
for (SectionChunk *sc : mc->sections)
|
for (SectionChunk *sc : mc->sections)
|
||||||
sc->eqClass[0] = nextId++;
|
sc->eqClass[0] = nextId++;
|
||||||
|
@ -318,8 +317,8 @@ void ICF::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry point to ICF.
|
// Entry point to ICF.
|
||||||
void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) {
|
void doICF(ArrayRef<Chunk *> chunks, ICFLevel icfLevel) {
|
||||||
ICF(ctx, icfLevel).run();
|
ICF(icfLevel).run(chunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
|
@ -17,9 +17,8 @@ namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
class Chunk;
|
class Chunk;
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
void doICF(COFFLinkerContext &ctx, ICFLevel);
|
void doICF(ArrayRef<Chunk *> chunks, ICFLevel);
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "InputFiles.h"
|
#include "InputFiles.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DebugTypes.h"
|
#include "DebugTypes.h"
|
||||||
|
@ -70,6 +69,11 @@ std::string lld::toString(const coff::InputFile *file) {
|
||||||
.str();
|
.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ObjFile *> ObjFile::instances;
|
||||||
|
std::map<std::string, PDBInputFile *> PDBInputFile::instances;
|
||||||
|
std::vector<ImportFile *> ImportFile::instances;
|
||||||
|
std::vector<BitcodeFile *> BitcodeFile::instances;
|
||||||
|
|
||||||
/// Checks that Source is compatible with being a weak alias to Target.
|
/// Checks that Source is compatible with being a weak alias to Target.
|
||||||
/// If Source is Undefined and has no weak alias set, makes it a weak
|
/// If Source is Undefined and has no weak alias set, makes it a weak
|
||||||
/// alias to Target.
|
/// alias to Target.
|
||||||
|
@ -94,8 +98,7 @@ static bool ignoredSymbolName(StringRef name) {
|
||||||
return name == "@feat.00" || name == "@comp.id";
|
return name == "@feat.00" || name == "@comp.id";
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
|
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
||||||
: InputFile(ctx, ArchiveKind, m) {}
|
|
||||||
|
|
||||||
void ArchiveFile::parse() {
|
void ArchiveFile::parse() {
|
||||||
// Parse a MemoryBufferRef as an archive file.
|
// Parse a MemoryBufferRef as an archive file.
|
||||||
|
@ -103,7 +106,7 @@ void ArchiveFile::parse() {
|
||||||
|
|
||||||
// Read the symbol table to construct Lazy objects.
|
// Read the symbol table to construct Lazy objects.
|
||||||
for (const Archive::Symbol &sym : file->symbols())
|
for (const Archive::Symbol &sym : file->symbols())
|
||||||
ctx.symtab.addLazyArchive(this, sym);
|
symtab->addLazyArchive(this, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a buffer pointing to a member file containing a given symbol.
|
// Returns a buffer pointing to a member file containing a given symbol.
|
||||||
|
@ -141,11 +144,11 @@ void LazyObjFile::fetch() {
|
||||||
|
|
||||||
InputFile *file;
|
InputFile *file;
|
||||||
if (isBitcode(mb))
|
if (isBitcode(mb))
|
||||||
file = make<BitcodeFile>(ctx, mb, "", 0, std::move(symbols));
|
file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
|
||||||
else
|
else
|
||||||
file = make<ObjFile>(ctx, mb, std::move(symbols));
|
file = make<ObjFile>(mb, std::move(symbols));
|
||||||
mb = {};
|
mb = {};
|
||||||
ctx.symtab.addFile(file);
|
symtab->addFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyObjFile::parse() {
|
void LazyObjFile::parse() {
|
||||||
|
@ -155,7 +158,7 @@ void LazyObjFile::parse() {
|
||||||
CHECK(lto::InputFile::create(this->mb), this);
|
CHECK(lto::InputFile::create(this->mb), this);
|
||||||
for (const lto::InputFile::Symbol &sym : obj->symbols()) {
|
for (const lto::InputFile::Symbol &sym : obj->symbols()) {
|
||||||
if (!sym.isUndefined())
|
if (!sym.isUndefined())
|
||||||
ctx.symtab.addLazyObject(this, sym.getName());
|
symtab->addLazyObject(this, sym.getName());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +175,7 @@ void LazyObjFile::parse() {
|
||||||
StringRef name = check(coffObj->getSymbolName(coffSym));
|
StringRef name = check(coffObj->getSymbolName(coffSym));
|
||||||
if (coffSym.isAbsolute() && ignoredSymbolName(name))
|
if (coffSym.isAbsolute() && ignoredSymbolName(name))
|
||||||
continue;
|
continue;
|
||||||
ctx.symtab.addLazyObject(this, name);
|
symtab->addLazyObject(this, name);
|
||||||
i += coffSym.getNumberOfAuxSymbols();
|
i += coffSym.getNumberOfAuxSymbols();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,7 +293,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
||||||
// COFF sections that look like string literal sections (i.e. no
|
// COFF sections that look like string literal sections (i.e. no
|
||||||
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
|
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
|
||||||
// for string literals) are subject to string tail merging.
|
// for string literals) are subject to string tail merging.
|
||||||
MergeChunk::addSection(ctx, c);
|
MergeChunk::addSection(c);
|
||||||
else if (name == ".rsrc" || name.startswith(".rsrc$"))
|
else if (name == ".rsrc" || name.startswith(".rsrc$"))
|
||||||
resourceChunks.push_back(c);
|
resourceChunks.push_back(c);
|
||||||
else
|
else
|
||||||
|
@ -384,8 +387,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
|
||||||
if (sym.isExternal()) {
|
if (sym.isExternal()) {
|
||||||
StringRef name = check(coffObj->getSymbolName(sym));
|
StringRef name = check(coffObj->getSymbolName(sym));
|
||||||
if (sc)
|
if (sc)
|
||||||
return ctx.symtab.addRegular(this, name, sym.getGeneric(), sc,
|
return symtab->addRegular(this, name, sym.getGeneric(), sc,
|
||||||
sym.getValue());
|
sym.getValue());
|
||||||
// For MinGW symbols named .weak.* that point to a discarded section,
|
// For MinGW symbols named .weak.* that point to a discarded section,
|
||||||
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
||||||
// everything should be fine. If something actually refers to the symbol
|
// everything should be fine. If something actually refers to the symbol
|
||||||
|
@ -393,7 +396,7 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
|
||||||
// references at the end.
|
// references at the end.
|
||||||
if (config->mingw && name.startswith(".weak."))
|
if (config->mingw && name.startswith(".weak."))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return ctx.symtab.addUndefined(name, this, false);
|
return symtab->addUndefined(name, this, false);
|
||||||
}
|
}
|
||||||
if (sc)
|
if (sc)
|
||||||
return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
||||||
|
@ -461,7 +464,7 @@ void ObjFile::initializeSymbols() {
|
||||||
for (auto &kv : weakAliases) {
|
for (auto &kv : weakAliases) {
|
||||||
Symbol *sym = kv.first;
|
Symbol *sym = kv.first;
|
||||||
uint32_t idx = kv.second;
|
uint32_t idx = kv.second;
|
||||||
checkAndSetWeakAlias(&ctx.symtab, this, sym, symbols[idx]);
|
checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the memory used by sparseChunks now that symbol loading is finished.
|
// Free the memory used by sparseChunks now that symbol loading is finished.
|
||||||
|
@ -470,7 +473,7 @@ void ObjFile::initializeSymbols() {
|
||||||
|
|
||||||
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
|
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
|
||||||
StringRef name = check(coffObj->getSymbolName(sym));
|
StringRef name = check(coffObj->getSymbolName(sym));
|
||||||
return ctx.symtab.addUndefined(name, this, sym.isWeakExternal());
|
return symtab->addUndefined(name, this, sym.isWeakExternal());
|
||||||
}
|
}
|
||||||
|
|
||||||
static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj,
|
static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj,
|
||||||
|
@ -540,13 +543,13 @@ void ObjFile::handleComdatSelection(
|
||||||
Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
|
Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
|
||||||
" and " + Twine((int)selection) + " in " + toString(this))
|
" and " + Twine((int)selection) + " in " + toString(this))
|
||||||
.str());
|
.str());
|
||||||
ctx.symtab.reportDuplicate(leader, this);
|
symtab->reportDuplicate(leader, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (selection) {
|
switch (selection) {
|
||||||
case IMAGE_COMDAT_SELECT_NODUPLICATES:
|
case IMAGE_COMDAT_SELECT_NODUPLICATES:
|
||||||
ctx.symtab.reportDuplicate(leader, this);
|
symtab->reportDuplicate(leader, this);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IMAGE_COMDAT_SELECT_ANY:
|
case IMAGE_COMDAT_SELECT_ANY:
|
||||||
|
@ -556,14 +559,14 @@ void ObjFile::handleComdatSelection(
|
||||||
case IMAGE_COMDAT_SELECT_SAME_SIZE:
|
case IMAGE_COMDAT_SELECT_SAME_SIZE:
|
||||||
if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
|
if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
|
||||||
if (!config->mingw) {
|
if (!config->mingw) {
|
||||||
ctx.symtab.reportDuplicate(leader, this);
|
symtab->reportDuplicate(leader, this);
|
||||||
} else {
|
} else {
|
||||||
const coff_aux_section_definition *leaderDef = nullptr;
|
const coff_aux_section_definition *leaderDef = nullptr;
|
||||||
if (leaderChunk->file)
|
if (leaderChunk->file)
|
||||||
leaderDef = findSectionDef(leaderChunk->file->getCOFFObj(),
|
leaderDef = findSectionDef(leaderChunk->file->getCOFFObj(),
|
||||||
leaderChunk->getSectionNumber());
|
leaderChunk->getSectionNumber());
|
||||||
if (!leaderDef || leaderDef->Length != def->Length)
|
if (!leaderDef || leaderDef->Length != def->Length)
|
||||||
ctx.symtab.reportDuplicate(leader, this);
|
symtab->reportDuplicate(leader, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -574,7 +577,7 @@ void ObjFile::handleComdatSelection(
|
||||||
// if the two comdat sections have e.g. different alignment.
|
// if the two comdat sections have e.g. different alignment.
|
||||||
// Match that.
|
// Match that.
|
||||||
if (leaderChunk->getContents() != newChunk.getContents())
|
if (leaderChunk->getContents() != newChunk.getContents())
|
||||||
ctx.symtab.reportDuplicate(leader, this, &newChunk, sym.getValue());
|
symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,8 +620,8 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||||
if (sym.isCommon()) {
|
if (sym.isCommon()) {
|
||||||
auto *c = make<CommonChunk>(sym);
|
auto *c = make<CommonChunk>(sym);
|
||||||
chunks.push_back(c);
|
chunks.push_back(c);
|
||||||
return ctx.symtab.addCommon(this, getName(), sym.getValue(),
|
return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(),
|
||||||
sym.getGeneric(), c);
|
c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sym.isAbsolute()) {
|
if (sym.isAbsolute()) {
|
||||||
|
@ -631,7 +634,7 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (sym.isExternal())
|
if (sym.isExternal())
|
||||||
return ctx.symtab.addAbsolute(name, sym);
|
return symtab->addAbsolute(name, sym);
|
||||||
return make<DefinedAbsolute>(name, sym);
|
return make<DefinedAbsolute>(name, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,7 +667,7 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||||
|
|
||||||
if (sym.isExternal()) {
|
if (sym.isExternal()) {
|
||||||
std::tie(leader, prevailing) =
|
std::tie(leader, prevailing) =
|
||||||
ctx.symtab.addComdat(this, getName(), sym.getGeneric());
|
symtab->addComdat(this, getName(), sym.getGeneric());
|
||||||
} else {
|
} else {
|
||||||
leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
||||||
/*IsExternal*/ false, sym.getGeneric());
|
/*IsExternal*/ false, sym.getGeneric());
|
||||||
|
@ -786,11 +789,12 @@ void ObjFile::initializeDependencies() {
|
||||||
else
|
else
|
||||||
data = getDebugSection(".debug$T");
|
data = getDebugSection(".debug$T");
|
||||||
|
|
||||||
|
// Don't make a TpiSource for objects with no debug info. If the object has
|
||||||
// symbols but no types, make a plain, empty TpiSource anyway, because it
|
// symbols but no types, make a plain, empty TpiSource anyway, because it
|
||||||
// simplifies adding the symbols later.
|
// simplifies adding the symbols later.
|
||||||
if (data.empty()) {
|
if (data.empty()) {
|
||||||
if (!debugChunks.empty())
|
if (!debugChunks.empty())
|
||||||
debugTypesObj = makeTpiSource(ctx, this);
|
debugTypesObj = makeTpiSource(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,7 +812,7 @@ void ObjFile::initializeDependencies() {
|
||||||
|
|
||||||
// This object file is a PCH file that others will depend on.
|
// This object file is a PCH file that others will depend on.
|
||||||
if (isPCH) {
|
if (isPCH) {
|
||||||
debugTypesObj = makePrecompSource(ctx, this);
|
debugTypesObj = makePrecompSource(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,8 +820,8 @@ void ObjFile::initializeDependencies() {
|
||||||
if (firstType->kind() == LF_TYPESERVER2) {
|
if (firstType->kind() == LF_TYPESERVER2) {
|
||||||
TypeServer2Record ts = cantFail(
|
TypeServer2Record ts = cantFail(
|
||||||
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
|
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
|
||||||
debugTypesObj = makeUseTypeServerSource(ctx, this, ts);
|
debugTypesObj = makeUseTypeServerSource(this, ts);
|
||||||
enqueuePdbFile(ts.getName(), this);
|
PDBInputFile::enqueue(ts.getName(), this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,14 +830,14 @@ void ObjFile::initializeDependencies() {
|
||||||
if (firstType->kind() == LF_PRECOMP) {
|
if (firstType->kind() == LF_PRECOMP) {
|
||||||
PrecompRecord precomp = cantFail(
|
PrecompRecord precomp = cantFail(
|
||||||
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
|
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
|
||||||
debugTypesObj = makeUsePrecompSource(ctx, this, precomp);
|
debugTypesObj = makeUsePrecompSource(this, precomp);
|
||||||
// Drop the LF_PRECOMP record from the input stream.
|
// Drop the LF_PRECOMP record from the input stream.
|
||||||
debugTypes = debugTypes.drop_front(firstType->RecordData.size());
|
debugTypes = debugTypes.drop_front(firstType->RecordData.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a plain old object file.
|
// This is a plain old object file.
|
||||||
debugTypesObj = makeTpiSource(ctx, this);
|
debugTypesObj = makeTpiSource(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a PDB path assuming the PDB is in the same folder as the OBJ
|
// Make a PDB path assuming the PDB is in the same folder as the OBJ
|
||||||
|
@ -851,7 +855,7 @@ static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) {
|
||||||
|
|
||||||
// The casing of the PDB path stamped in the OBJ can differ from the actual path
|
// The casing of the PDB path stamped in the OBJ can differ from the actual path
|
||||||
// on disk. With this, we ensure to always use lowercase as a key for the
|
// on disk. With this, we ensure to always use lowercase as a key for the
|
||||||
// pdbInputFileInstances map, at least on Windows.
|
// PDBInputFile::instances map, at least on Windows.
|
||||||
static std::string normalizePdbPath(StringRef path) {
|
static std::string normalizePdbPath(StringRef path) {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
return path.lower();
|
return path.lower();
|
||||||
|
@ -875,25 +879,33 @@ static Optional<std::string> findPdbPath(StringRef pdbPath,
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
PDBInputFile::PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m)
|
PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {}
|
||||||
: InputFile(ctx, PDBKind, m) {}
|
|
||||||
|
|
||||||
PDBInputFile::~PDBInputFile() = default;
|
PDBInputFile::~PDBInputFile() = default;
|
||||||
|
|
||||||
PDBInputFile *PDBInputFile::findFromRecordPath(const COFFLinkerContext &ctx,
|
PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path,
|
||||||
StringRef path,
|
|
||||||
ObjFile *fromFile) {
|
ObjFile *fromFile) {
|
||||||
auto p = findPdbPath(path.str(), fromFile);
|
auto p = findPdbPath(path.str(), fromFile);
|
||||||
if (!p)
|
if (!p)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
auto it = ctx.pdbInputFileInstances.find(*p);
|
auto it = PDBInputFile::instances.find(*p);
|
||||||
if (it != ctx.pdbInputFileInstances.end())
|
if (it != PDBInputFile::instances.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) {
|
||||||
|
auto p = findPdbPath(path.str(), fromFile);
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
auto it = PDBInputFile::instances.emplace(*p, nullptr);
|
||||||
|
if (!it.second)
|
||||||
|
return; // already scheduled for load
|
||||||
|
driver->enqueuePDB(*p);
|
||||||
|
}
|
||||||
|
|
||||||
void PDBInputFile::parse() {
|
void PDBInputFile::parse() {
|
||||||
ctx.pdbInputFileInstances[mb.getBufferIdentifier().str()] = this;
|
PDBInputFile::instances[mb.getBufferIdentifier().str()] = this;
|
||||||
|
|
||||||
std::unique_ptr<pdb::IPDBSession> thisSession;
|
std::unique_ptr<pdb::IPDBSession> thisSession;
|
||||||
loadErr.emplace(pdb::NativeSession::createFromPdb(
|
loadErr.emplace(pdb::NativeSession::createFromPdb(
|
||||||
|
@ -911,7 +923,7 @@ void PDBInputFile::parse() {
|
||||||
loadErr.emplace(expectedInfo.takeError());
|
loadErr.emplace(expectedInfo.takeError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugTypesObj = makeTypeServerSource(ctx, this);
|
debugTypesObj = makeTypeServerSource(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used only for DWARF debug info, which is not common (except in MinGW
|
// Used only for DWARF debug info, which is not common (except in MinGW
|
||||||
|
@ -945,16 +957,6 @@ Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
|
||||||
return dwarf->getDILineInfo(offset, sectionIndex);
|
return dwarf->getDILineInfo(offset, sectionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) {
|
|
||||||
auto p = findPdbPath(path.str(), fromFile);
|
|
||||||
if (!p)
|
|
||||||
return;
|
|
||||||
auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr);
|
|
||||||
if (!it.second)
|
|
||||||
return; // already scheduled for load
|
|
||||||
driver->enqueuePDB(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImportFile::parse() {
|
void ImportFile::parse() {
|
||||||
const char *buf = mb.getBufferStart();
|
const char *buf = mb.getBufferStart();
|
||||||
const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
|
const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
|
||||||
|
@ -988,31 +990,31 @@ void ImportFile::parse() {
|
||||||
this->hdr = hdr;
|
this->hdr = hdr;
|
||||||
externalName = extName;
|
externalName = extName;
|
||||||
|
|
||||||
impSym = ctx.symtab.addImportData(impName, this);
|
impSym = symtab->addImportData(impName, this);
|
||||||
// If this was a duplicate, we logged an error but may continue;
|
// If this was a duplicate, we logged an error but may continue;
|
||||||
// in this case, impSym is nullptr.
|
// in this case, impSym is nullptr.
|
||||||
if (!impSym)
|
if (!impSym)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hdr->getType() == llvm::COFF::IMPORT_CONST)
|
if (hdr->getType() == llvm::COFF::IMPORT_CONST)
|
||||||
static_cast<void>(ctx.symtab.addImportData(name, this));
|
static_cast<void>(symtab->addImportData(name, this));
|
||||||
|
|
||||||
// If type is function, we need to create a thunk which jump to an
|
// If type is function, we need to create a thunk which jump to an
|
||||||
// address pointed by the __imp_ symbol. (This allows you to call
|
// address pointed by the __imp_ symbol. (This allows you to call
|
||||||
// DLL functions just like regular non-DLL functions.)
|
// DLL functions just like regular non-DLL functions.)
|
||||||
if (hdr->getType() == llvm::COFF::IMPORT_CODE)
|
if (hdr->getType() == llvm::COFF::IMPORT_CODE)
|
||||||
thunkSym = ctx.symtab.addImportThunk(
|
thunkSym = symtab->addImportThunk(
|
||||||
name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
|
name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
|
||||||
}
|
}
|
||||||
|
|
||||||
BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
|
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
StringRef archiveName, uint64_t offsetInArchive)
|
uint64_t offsetInArchive)
|
||||||
: BitcodeFile(ctx, mb, archiveName, offsetInArchive, {}) {}
|
: BitcodeFile(mb, archiveName, offsetInArchive, {}) {}
|
||||||
|
|
||||||
BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
|
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
StringRef archiveName, uint64_t offsetInArchive,
|
uint64_t offsetInArchive,
|
||||||
std::vector<Symbol *> &&symbols)
|
std::vector<Symbol *> &&symbols)
|
||||||
: InputFile(ctx, BitcodeKind, mb), symbols(std::move(symbols)) {
|
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
|
||||||
std::string path = mb.getBufferIdentifier().str();
|
std::string path = mb.getBufferIdentifier().str();
|
||||||
if (config->thinLTOIndexOnly)
|
if (config->thinLTOIndexOnly)
|
||||||
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
||||||
|
@ -1067,7 +1069,7 @@ void BitcodeFile::parse() {
|
||||||
for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
|
for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
|
||||||
// FIXME: Check nodeduplicate
|
// FIXME: Check nodeduplicate
|
||||||
comdat[i] =
|
comdat[i] =
|
||||||
ctx.symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first));
|
symtab->addComdat(this, saver.save(obj->getComdatTable()[i].first));
|
||||||
for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
|
for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
|
||||||
StringRef symName = saver.save(objSym.getName());
|
StringRef symName = saver.save(objSym.getName());
|
||||||
int comdatIndex = objSym.getComdatIndex();
|
int comdatIndex = objSym.getComdatIndex();
|
||||||
|
@ -1078,27 +1080,27 @@ void BitcodeFile::parse() {
|
||||||
else
|
else
|
||||||
fakeSC = <oDataSectionChunk.chunk;
|
fakeSC = <oDataSectionChunk.chunk;
|
||||||
if (objSym.isUndefined()) {
|
if (objSym.isUndefined()) {
|
||||||
sym = ctx.symtab.addUndefined(symName, this, false);
|
sym = symtab->addUndefined(symName, this, false);
|
||||||
} else if (objSym.isCommon()) {
|
} else if (objSym.isCommon()) {
|
||||||
sym = ctx.symtab.addCommon(this, symName, objSym.getCommonSize());
|
sym = symtab->addCommon(this, symName, objSym.getCommonSize());
|
||||||
} else if (objSym.isWeak() && objSym.isIndirect()) {
|
} else if (objSym.isWeak() && objSym.isIndirect()) {
|
||||||
// Weak external.
|
// Weak external.
|
||||||
sym = ctx.symtab.addUndefined(symName, this, true);
|
sym = symtab->addUndefined(symName, this, true);
|
||||||
std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
|
std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
|
||||||
Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback));
|
Symbol *alias = symtab->addUndefined(saver.save(fallback));
|
||||||
checkAndSetWeakAlias(&ctx.symtab, this, sym, alias);
|
checkAndSetWeakAlias(symtab, this, sym, alias);
|
||||||
} else if (comdatIndex != -1) {
|
} else if (comdatIndex != -1) {
|
||||||
if (symName == obj->getComdatTable()[comdatIndex].first) {
|
if (symName == obj->getComdatTable()[comdatIndex].first) {
|
||||||
sym = comdat[comdatIndex].first;
|
sym = comdat[comdatIndex].first;
|
||||||
if (cast<DefinedRegular>(sym)->data == nullptr)
|
if (cast<DefinedRegular>(sym)->data == nullptr)
|
||||||
cast<DefinedRegular>(sym)->data = &fakeSC->repl;
|
cast<DefinedRegular>(sym)->data = &fakeSC->repl;
|
||||||
} else if (comdat[comdatIndex].second) {
|
} else if (comdat[comdatIndex].second) {
|
||||||
sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC);
|
sym = symtab->addRegular(this, symName, nullptr, fakeSC);
|
||||||
} else {
|
} else {
|
||||||
sym = ctx.symtab.addUndefined(symName, this, false);
|
sym = symtab->addUndefined(symName, this, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC);
|
sym = symtab->addRegular(this, symName, nullptr, fakeSC);
|
||||||
}
|
}
|
||||||
symbols.push_back(sym);
|
symbols.push_back(sym);
|
||||||
if (objSym.isUsed())
|
if (objSym.isUsed())
|
||||||
|
@ -1183,9 +1185,9 @@ void DLLFile::parse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
StringRef impName = saver.save("__imp_" + symbolName);
|
StringRef impName = saver.save("__imp_" + symbolName);
|
||||||
ctx.symtab.addLazyDLLSymbol(this, s, impName);
|
symtab->addLazyDLLSymbol(this, s, impName);
|
||||||
if (code)
|
if (code)
|
||||||
ctx.symtab.addLazyDLLSymbol(this, s, symbolName);
|
symtab->addLazyDLLSymbol(this, s, symbolName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1217,6 +1219,6 @@ void DLLFile::makeImport(DLLFile::Symbol *s) {
|
||||||
p += s->symbolName.size() + 1;
|
p += s->symbolName.size() + 1;
|
||||||
memcpy(p, s->dllName.data(), s->dllName.size());
|
memcpy(p, s->dllName.data(), s->dllName.size());
|
||||||
MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName);
|
MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName);
|
||||||
ImportFile *impFile = make<ImportFile>(ctx, mbref);
|
ImportFile *impFile = make<ImportFile>(mbref);
|
||||||
ctx.symtab.addFile(impFile);
|
symtab->addFile(impFile);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ namespace lld {
|
||||||
class DWARFCache;
|
class DWARFCache;
|
||||||
|
|
||||||
namespace coff {
|
namespace coff {
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
|
std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
|
||||||
|
|
||||||
|
@ -92,11 +91,8 @@ public:
|
||||||
// Returns .drectve section contents if exist.
|
// Returns .drectve section contents if exist.
|
||||||
StringRef getDirectives() { return directives; }
|
StringRef getDirectives() { return directives; }
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
InputFile(COFFLinkerContext &c, Kind k, MemoryBufferRef m)
|
InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {}
|
||||||
: mb(m), ctx(c), fileKind(k) {}
|
|
||||||
|
|
||||||
StringRef directives;
|
StringRef directives;
|
||||||
|
|
||||||
|
@ -107,7 +103,7 @@ private:
|
||||||
// .lib or .a file.
|
// .lib or .a file.
|
||||||
class ArchiveFile : public InputFile {
|
class ArchiveFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m);
|
explicit ArchiveFile(MemoryBufferRef m);
|
||||||
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
|
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
|
||||||
void parse() override;
|
void parse() override;
|
||||||
|
|
||||||
|
@ -124,8 +120,7 @@ private:
|
||||||
// .obj or .o file between -start-lib and -end-lib.
|
// .obj or .o file between -start-lib and -end-lib.
|
||||||
class LazyObjFile : public InputFile {
|
class LazyObjFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit LazyObjFile(COFFLinkerContext &ctx, MemoryBufferRef m)
|
explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {}
|
||||||
: InputFile(ctx, LazyObjectKind, m) {}
|
|
||||||
static bool classof(const InputFile *f) {
|
static bool classof(const InputFile *f) {
|
||||||
return f->kind() == LazyObjectKind;
|
return f->kind() == LazyObjectKind;
|
||||||
}
|
}
|
||||||
|
@ -141,11 +136,9 @@ private:
|
||||||
// .obj or .o file. This may be a member of an archive file.
|
// .obj or .o file. This may be a member of an archive file.
|
||||||
class ObjFile : public InputFile {
|
class ObjFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m)
|
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
|
||||||
: InputFile(ctx, ObjectKind, m) {}
|
explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
|
||||||
explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m,
|
: InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
|
||||||
std::vector<Symbol *> &&symbols)
|
|
||||||
: InputFile(ctx, ObjectKind, m), symbols(std::move(symbols)) {}
|
|
||||||
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
||||||
void parse() override;
|
void parse() override;
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
|
@ -182,6 +175,8 @@ public:
|
||||||
|
|
||||||
bool isResourceObjFile() const { return !resourceChunks.empty(); }
|
bool isResourceObjFile() const { return !resourceChunks.empty(); }
|
||||||
|
|
||||||
|
static std::vector<ObjFile *> instances;
|
||||||
|
|
||||||
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
||||||
// indicate if an object was compiled with certain security features enabled
|
// indicate if an object was compiled with certain security features enabled
|
||||||
// like stack guard, safeseh, /guard:cf, or other things.
|
// like stack guard, safeseh, /guard:cf, or other things.
|
||||||
|
@ -233,8 +228,6 @@ private:
|
||||||
return getSection(sym.getSectionNumber());
|
return getSection(sym.getSectionNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueuePdbFile(StringRef path, ObjFile *fromFile);
|
|
||||||
|
|
||||||
void initializeChunks();
|
void initializeChunks();
|
||||||
void initializeSymbols();
|
void initializeSymbols();
|
||||||
void initializeFlags();
|
void initializeFlags();
|
||||||
|
@ -325,13 +318,16 @@ private:
|
||||||
// stream.
|
// stream.
|
||||||
class PDBInputFile : public InputFile {
|
class PDBInputFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m);
|
explicit PDBInputFile(MemoryBufferRef m);
|
||||||
~PDBInputFile();
|
~PDBInputFile();
|
||||||
static bool classof(const InputFile *f) { return f->kind() == PDBKind; }
|
static bool classof(const InputFile *f) { return f->kind() == PDBKind; }
|
||||||
void parse() override;
|
void parse() override;
|
||||||
|
|
||||||
static PDBInputFile *findFromRecordPath(const COFFLinkerContext &ctx,
|
static void enqueue(StringRef path, ObjFile *fromFile);
|
||||||
StringRef path, ObjFile *fromFile);
|
|
||||||
|
static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile);
|
||||||
|
|
||||||
|
static std::map<std::string, PDBInputFile *> instances;
|
||||||
|
|
||||||
// Record possible errors while opening the PDB file
|
// Record possible errors while opening the PDB file
|
||||||
llvm::Optional<Error> loadErr;
|
llvm::Optional<Error> loadErr;
|
||||||
|
@ -348,11 +344,12 @@ public:
|
||||||
// for details about the format.
|
// for details about the format.
|
||||||
class ImportFile : public InputFile {
|
class ImportFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
|
explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {}
|
||||||
: InputFile(ctx, ImportKind, m) {}
|
|
||||||
|
|
||||||
static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
|
static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
|
||||||
|
|
||||||
|
static std::vector<ImportFile *> instances;
|
||||||
|
|
||||||
Symbol *impSym = nullptr;
|
Symbol *impSym = nullptr;
|
||||||
Symbol *thunkSym = nullptr;
|
Symbol *thunkSym = nullptr;
|
||||||
std::string dllName;
|
std::string dllName;
|
||||||
|
@ -380,15 +377,16 @@ public:
|
||||||
// Used for LTO.
|
// Used for LTO.
|
||||||
class BitcodeFile : public InputFile {
|
class BitcodeFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb, StringRef archiveName,
|
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
uint64_t offsetInArchive);
|
uint64_t offsetInArchive);
|
||||||
explicit BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef m,
|
explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName,
|
||||||
StringRef archiveName, uint64_t offsetInArchive,
|
uint64_t offsetInArchive,
|
||||||
std::vector<Symbol *> &&symbols);
|
std::vector<Symbol *> &&symbols);
|
||||||
~BitcodeFile();
|
~BitcodeFile();
|
||||||
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
||||||
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
|
static std::vector<BitcodeFile *> instances;
|
||||||
std::unique_ptr<llvm::lto::InputFile> obj;
|
std::unique_ptr<llvm::lto::InputFile> obj;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -400,8 +398,7 @@ private:
|
||||||
// .dll file. MinGW only.
|
// .dll file. MinGW only.
|
||||||
class DLLFile : public InputFile {
|
class DLLFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit DLLFile(COFFLinkerContext &ctx, MemoryBufferRef m)
|
explicit DLLFile(MemoryBufferRef m) : InputFile(DLLKind, m) {}
|
||||||
: InputFile(ctx, DLLKind, m) {}
|
|
||||||
static bool classof(const InputFile *f) { return f->kind() == DLLKind; }
|
static bool classof(const InputFile *f) { return f->kind() == DLLKind; }
|
||||||
void parse() override;
|
void parse() override;
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "LLDMapFile.h"
|
#include "LLDMapFile.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include "Writer.h"
|
#include "Writer.h"
|
||||||
|
@ -45,9 +44,9 @@ static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list of all symbols that we want to print out.
|
// Returns a list of all symbols that we want to print out.
|
||||||
static std::vector<DefinedRegular *> getSymbols(const COFFLinkerContext &ctx) {
|
static std::vector<DefinedRegular *> getSymbols() {
|
||||||
std::vector<DefinedRegular *> v;
|
std::vector<DefinedRegular *> v;
|
||||||
for (ObjFile *file : ctx.objFileInstances)
|
for (ObjFile *file : ObjFile::instances)
|
||||||
for (Symbol *b : file->getSymbols())
|
for (Symbol *b : file->getSymbols())
|
||||||
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
||||||
if (sym && !sym->getCOFFSymbol().isSectionDefinition())
|
if (sym && !sym->getCOFFSymbol().isSectionDefinition())
|
||||||
|
@ -87,7 +86,7 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
|
void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||||
if (config->lldmapFile.empty())
|
if (config->lldmapFile.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
|
||||||
fatal("cannot open " + config->lldmapFile + ": " + ec.message());
|
fatal("cannot open " + config->lldmapFile + ": " + ec.message());
|
||||||
|
|
||||||
// Collect symbol info that we want to print out.
|
// Collect symbol info that we want to print out.
|
||||||
std::vector<DefinedRegular *> syms = getSymbols(ctx);
|
std::vector<DefinedRegular *> syms = getSymbols();
|
||||||
SymbolMapTy sectionSyms = getSectionSyms(syms);
|
SymbolMapTy sectionSyms = getSectionSyms(syms);
|
||||||
DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
|
DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
|
||||||
|
|
||||||
|
@ -105,7 +104,7 @@ void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
|
||||||
os << "Address Size Align Out In Symbol\n";
|
os << "Address Size Align Out In Symbol\n";
|
||||||
|
|
||||||
// Print out file contents.
|
// Print out file contents.
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
|
writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
|
||||||
os << sec->name << '\n';
|
os << sec->name << '\n';
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
#ifndef LLD_COFF_LLDMAPFILE_H
|
#ifndef LLD_COFF_LLDMAPFILE_H
|
||||||
#define LLD_COFF_LLDMAPFILE_H
|
#define LLD_COFF_LLDMAPFILE_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
class COFFLinkerContext;
|
class OutputSection;
|
||||||
void writeLLDMapFile(const COFFLinkerContext &ctx);
|
void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
|
||||||
|
|
||||||
// Merge all the bitcode files we have seen, codegen the result
|
// Merge all the bitcode files we have seen, codegen the result
|
||||||
// and return the resulting objects.
|
// and return the resulting objects.
|
||||||
std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
|
std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||||
unsigned maxTasks = ltoObj->getMaxTasks();
|
unsigned maxTasks = ltoObj->getMaxTasks();
|
||||||
buf.resize(maxTasks);
|
buf.resize(maxTasks);
|
||||||
files.resize(maxTasks);
|
files.resize(maxTasks);
|
||||||
|
@ -224,7 +224,7 @@ std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
|
||||||
|
|
||||||
if (config->saveTemps)
|
if (config->saveTemps)
|
||||||
saveBuffer(buf[i], ltoObjName);
|
saveBuffer(buf[i], ltoObjName);
|
||||||
ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
|
ret.push_back(make<ObjFile>(MemoryBufferRef(objBuf, ltoObjName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -38,7 +38,6 @@ namespace coff {
|
||||||
|
|
||||||
class BitcodeFile;
|
class BitcodeFile;
|
||||||
class InputFile;
|
class InputFile;
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
class BitcodeCompiler {
|
class BitcodeCompiler {
|
||||||
public:
|
public:
|
||||||
|
@ -46,7 +45,7 @@ public:
|
||||||
~BitcodeCompiler();
|
~BitcodeCompiler();
|
||||||
|
|
||||||
void add(BitcodeFile &f);
|
void add(BitcodeFile &f);
|
||||||
std::vector<InputFile *> compile(COFFLinkerContext &ctx);
|
std::vector<InputFile *> compile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<llvm::lto::LTO> ltoObj;
|
std::unique_ptr<llvm::lto::LTO> ltoObj;
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "MapFile.h"
|
#include "MapFile.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include "Writer.h"
|
#include "Writer.h"
|
||||||
|
@ -43,6 +42,11 @@ using namespace llvm::object;
|
||||||
using namespace lld;
|
using namespace lld;
|
||||||
using namespace lld::coff;
|
using namespace lld::coff;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
// Print out the first two columns of a line.
|
// Print out the first two columns of a line.
|
||||||
static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
|
static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
|
||||||
os << format(" %04x:%08llx", sec, addr);
|
os << format(" %04x:%08llx", sec, addr);
|
||||||
|
@ -95,10 +99,9 @@ static void sortUniqueSymbols(std::vector<Defined *> &syms) {
|
||||||
|
|
||||||
// Returns the lists of all symbols that we want to print out.
|
// Returns the lists of all symbols that we want to print out.
|
||||||
static void getSymbols(std::vector<Defined *> &syms,
|
static void getSymbols(std::vector<Defined *> &syms,
|
||||||
std::vector<Defined *> &staticSyms,
|
std::vector<Defined *> &staticSyms) {
|
||||||
const COFFLinkerContext &ctx) {
|
|
||||||
|
|
||||||
for (ObjFile *file : ctx.objFileInstances)
|
for (ObjFile *file : ObjFile::instances)
|
||||||
for (Symbol *b : file->getSymbols()) {
|
for (Symbol *b : file->getSymbols()) {
|
||||||
if (!b || !b->isLive())
|
if (!b || !b->isLive())
|
||||||
continue;
|
continue;
|
||||||
|
@ -116,7 +119,7 @@ static void getSymbols(std::vector<Defined *> &syms,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ImportFile *file : ctx.importFileInstances) {
|
for (ImportFile *file : ImportFile::instances) {
|
||||||
if (!file->live)
|
if (!file->live)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@ static void getSymbols(std::vector<Defined *> &syms,
|
||||||
|
|
||||||
// Construct a map from symbols to their stringified representations.
|
// Construct a map from symbols to their stringified representations.
|
||||||
static DenseMap<Defined *, std::string>
|
static DenseMap<Defined *, std::string>
|
||||||
getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
|
getSymbolStrings(ArrayRef<Defined *> syms) {
|
||||||
std::vector<std::string> str(syms.size());
|
std::vector<std::string> str(syms.size());
|
||||||
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
|
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
|
||||||
raw_string_ostream os(str[i]);
|
raw_string_ostream os(str[i]);
|
||||||
|
@ -158,7 +161,7 @@ getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
|
||||||
fileDescr = "<common>";
|
fileDescr = "<common>";
|
||||||
} else if (Chunk *chunk = sym->getChunk()) {
|
} else if (Chunk *chunk = sym->getChunk()) {
|
||||||
address = sym->getRVA();
|
address = sym->getRVA();
|
||||||
if (OutputSection *sec = ctx.getOutputSection(chunk))
|
if (OutputSection *sec = chunk->getOutputSection())
|
||||||
address -= sec->header.VirtualAddress;
|
address -= sec->header.VirtualAddress;
|
||||||
|
|
||||||
sectionIdx = chunk->getOutputSectionIdx();
|
sectionIdx = chunk->getOutputSectionIdx();
|
||||||
|
@ -198,7 +201,7 @@ getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||||
if (config->mapFile.empty())
|
if (config->mapFile.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -207,22 +210,21 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
||||||
if (ec)
|
if (ec)
|
||||||
fatal("cannot open " + config->mapFile + ": " + ec.message());
|
fatal("cannot open " + config->mapFile + ": " + ec.message());
|
||||||
|
|
||||||
ScopedTimer t1(ctx.totalMapTimer);
|
ScopedTimer t1(totalMapTimer);
|
||||||
|
|
||||||
// Collect symbol info that we want to print out.
|
// Collect symbol info that we want to print out.
|
||||||
ScopedTimer t2(ctx.symbolGatherTimer);
|
ScopedTimer t2(symbolGatherTimer);
|
||||||
std::vector<Defined *> syms;
|
std::vector<Defined *> syms;
|
||||||
std::vector<Defined *> staticSyms;
|
std::vector<Defined *> staticSyms;
|
||||||
getSymbols(syms, staticSyms, ctx);
|
getSymbols(syms, staticSyms);
|
||||||
t2.stop();
|
t2.stop();
|
||||||
|
|
||||||
ScopedTimer t3(ctx.symbolStringsTimer);
|
ScopedTimer t3(symbolStringsTimer);
|
||||||
DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
|
DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
|
||||||
DenseMap<Defined *, std::string> staticSymStr =
|
DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
|
||||||
getSymbolStrings(ctx, staticSyms);
|
|
||||||
t3.stop();
|
t3.stop();
|
||||||
|
|
||||||
ScopedTimer t4(ctx.writeTimer);
|
ScopedTimer t4(writeTimer);
|
||||||
SmallString<128> AppName = sys::path::filename(config->outputFile);
|
SmallString<128> AppName = sys::path::filename(config->outputFile);
|
||||||
sys::path::replace_extension(AppName, "");
|
sys::path::replace_extension(AppName, "");
|
||||||
|
|
||||||
|
@ -246,7 +248,7 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
||||||
// Print out section table.
|
// Print out section table.
|
||||||
os << " Start Length Name Class\n";
|
os << " Start Length Name Class\n";
|
||||||
|
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
// Merge display of chunks with same sectionName
|
// Merge display of chunks with same sectionName
|
||||||
std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
|
std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
|
||||||
for (Chunk *c : sec->chunks) {
|
for (Chunk *c : sec->chunks) {
|
||||||
|
@ -301,7 +303,7 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
||||||
Chunk *chunk = entry->getChunk();
|
Chunk *chunk = entry->getChunk();
|
||||||
entrySecIndex = chunk->getOutputSectionIdx();
|
entrySecIndex = chunk->getOutputSectionIdx();
|
||||||
entryAddress =
|
entryAddress =
|
||||||
entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
|
entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os << " entry point at ";
|
os << " entry point at ";
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
#ifndef LLD_COFF_MAPFILE_H
|
#ifndef LLD_COFF_MAPFILE_H
|
||||||
#define LLD_COFF_MAPFILE_H
|
#define LLD_COFF_MAPFILE_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
class COFFLinkerContext;
|
class OutputSection;
|
||||||
void writeMapFile(COFFLinkerContext &ctx);
|
void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include "lld/Common/Timer.h"
|
#include "lld/Common/Timer.h"
|
||||||
|
@ -16,11 +15,13 @@
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
|
static Timer gctimer("GC", Timer::root());
|
||||||
|
|
||||||
// Set live bit on for each reachable chunk. Unmarked (unreachable)
|
// Set live bit on for each reachable chunk. Unmarked (unreachable)
|
||||||
// COMDAT chunks will be ignored by Writer, so they will be excluded
|
// COMDAT chunks will be ignored by Writer, so they will be excluded
|
||||||
// from the final output.
|
// from the final output.
|
||||||
void markLive(COFFLinkerContext &ctx) {
|
void markLive(ArrayRef<Chunk *> chunks) {
|
||||||
ScopedTimer t(ctx.gcTimer);
|
ScopedTimer t(gctimer);
|
||||||
|
|
||||||
// We build up a worklist of sections which have been marked as live. We only
|
// We build up a worklist of sections which have been marked as live. We only
|
||||||
// push into the worklist when we discover an unmarked section, and we mark
|
// push into the worklist when we discover an unmarked section, and we mark
|
||||||
|
@ -30,7 +31,7 @@ void markLive(COFFLinkerContext &ctx) {
|
||||||
// COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not
|
// COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not
|
||||||
// traverse DWARF sections. They are live, but they should not keep other
|
// traverse DWARF sections. They are live, but they should not keep other
|
||||||
// sections alive.
|
// sections alive.
|
||||||
for (Chunk *c : ctx.symtab.getChunks())
|
for (Chunk *c : chunks)
|
||||||
if (auto *sc = dyn_cast<SectionChunk>(c))
|
if (auto *sc = dyn_cast<SectionChunk>(c))
|
||||||
if (sc->live && !sc->isDWARF())
|
if (sc->live && !sc->isDWARF())
|
||||||
worklist.push_back(sc);
|
worklist.push_back(sc);
|
||||||
|
@ -69,5 +70,6 @@ void markLive(COFFLinkerContext &ctx) {
|
||||||
enqueue(&c);
|
enqueue(&c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,14 @@
|
||||||
#define LLD_COFF_MARKLIVE_H
|
#define LLD_COFF_MARKLIVE_H
|
||||||
|
|
||||||
#include "lld/Common/LLVM.h"
|
#include "lld/Common/LLVM.h"
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
class COFFLinkerContext;
|
class Chunk;
|
||||||
|
|
||||||
void markLive(COFFLinkerContext &ctx);
|
void markLive(ArrayRef<Chunk *> chunks);
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "MinGW.h"
|
#include "MinGW.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Driver.h"
|
#include "Driver.h"
|
||||||
#include "InputFiles.h"
|
#include "InputFiles.h"
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
|
@ -123,8 +122,7 @@ void AutoExporter::addWholeArchive(StringRef path) {
|
||||||
excludeLibs.erase(libName);
|
excludeLibs.erase(libName);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
|
bool AutoExporter::shouldExport(Defined *sym) const {
|
||||||
Defined *sym) const {
|
|
||||||
if (!sym || !sym->getChunk())
|
if (!sym || !sym->getChunk())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -143,7 +141,7 @@ bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If a corresponding __imp_ symbol exists and is defined, don't export it.
|
// If a corresponding __imp_ symbol exists and is defined, don't export it.
|
||||||
if (ctx.symtab.find(("__imp_" + sym->getName()).str()))
|
if (symtab->find(("__imp_" + sym->getName()).str()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check that file is non-null before dereferencing it, symbols not
|
// Check that file is non-null before dereferencing it, symbols not
|
||||||
|
@ -194,7 +192,7 @@ static StringRef mangle(Twine sym) {
|
||||||
// like they are not being used at all, so we explicitly set some flags so
|
// like they are not being used at all, so we explicitly set some flags so
|
||||||
// that LTO won't eliminate them.
|
// that LTO won't eliminate them.
|
||||||
std::vector<WrappedSymbol>
|
std::vector<WrappedSymbol>
|
||||||
lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
|
lld::coff::addWrappedSymbols(opt::InputArgList &args) {
|
||||||
std::vector<WrappedSymbol> v;
|
std::vector<WrappedSymbol> v;
|
||||||
DenseSet<StringRef> seen;
|
DenseSet<StringRef> seen;
|
||||||
|
|
||||||
|
@ -203,18 +201,18 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
|
||||||
if (!seen.insert(name).second)
|
if (!seen.insert(name).second)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Symbol *sym = ctx.symtab.findUnderscore(name);
|
Symbol *sym = symtab->findUnderscore(name);
|
||||||
if (!sym)
|
if (!sym)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Symbol *real = ctx.symtab.addUndefined(mangle("__real_" + name));
|
Symbol *real = symtab->addUndefined(mangle("__real_" + name));
|
||||||
Symbol *wrap = ctx.symtab.addUndefined(mangle("__wrap_" + name));
|
Symbol *wrap = symtab->addUndefined(mangle("__wrap_" + name));
|
||||||
v.push_back({sym, real, wrap});
|
v.push_back({sym, real, wrap});
|
||||||
|
|
||||||
// These symbols may seem undefined initially, but don't bail out
|
// These symbols may seem undefined initially, but don't bail out
|
||||||
// at symtab.reportUnresolvable() due to them, but let wrapSymbols
|
// at symtab->reportUnresolvable() due to them, but let wrapSymbols
|
||||||
// below sort things out before checking finally with
|
// below sort things out before checking finally with
|
||||||
// symtab.resolveRemainingUndefines().
|
// symtab->resolveRemainingUndefines().
|
||||||
sym->deferUndefined = true;
|
sym->deferUndefined = true;
|
||||||
real->deferUndefined = true;
|
real->deferUndefined = true;
|
||||||
// We want to tell LTO not to inline symbols to be overwritten
|
// We want to tell LTO not to inline symbols to be overwritten
|
||||||
|
@ -235,14 +233,13 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
|
||||||
// When this function is executed, only InputFiles and symbol table
|
// When this function is executed, only InputFiles and symbol table
|
||||||
// contain pointers to symbol objects. We visit them to replace pointers,
|
// contain pointers to symbol objects. We visit them to replace pointers,
|
||||||
// so that wrapped symbols are swapped as instructed by the command line.
|
// so that wrapped symbols are swapped as instructed by the command line.
|
||||||
void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
|
void lld::coff::wrapSymbols(ArrayRef<WrappedSymbol> wrapped) {
|
||||||
ArrayRef<WrappedSymbol> wrapped) {
|
|
||||||
DenseMap<Symbol *, Symbol *> map;
|
DenseMap<Symbol *, Symbol *> map;
|
||||||
for (const WrappedSymbol &w : wrapped) {
|
for (const WrappedSymbol &w : wrapped) {
|
||||||
map[w.sym] = w.wrap;
|
map[w.sym] = w.wrap;
|
||||||
map[w.real] = w.sym;
|
map[w.real] = w.sym;
|
||||||
if (Defined *d = dyn_cast<Defined>(w.wrap)) {
|
if (Defined *d = dyn_cast<Defined>(w.wrap)) {
|
||||||
Symbol *imp = ctx.symtab.find(("__imp_" + w.sym->getName()).str());
|
Symbol *imp = symtab->find(("__imp_" + w.sym->getName()).str());
|
||||||
// Create a new defined local import for the wrap symbol. If
|
// Create a new defined local import for the wrap symbol. If
|
||||||
// no imp prefixed symbol existed, there's no need for it.
|
// no imp prefixed symbol existed, there's no need for it.
|
||||||
// (We can't easily distinguish whether any object file actually
|
// (We can't easily distinguish whether any object file actually
|
||||||
|
@ -250,14 +247,14 @@ void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
|
||||||
if (imp) {
|
if (imp) {
|
||||||
DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
|
DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
|
||||||
saver.save("__imp_" + w.wrap->getName()), d);
|
saver.save("__imp_" + w.wrap->getName()), d);
|
||||||
ctx.symtab.localImportChunks.push_back(wrapimp->getChunk());
|
symtab->localImportChunks.push_back(wrapimp->getChunk());
|
||||||
map[imp] = wrapimp;
|
map[imp] = wrapimp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pointers in input files.
|
// Update pointers in input files.
|
||||||
parallelForEach(ctx.objFileInstances, [&](ObjFile *file) {
|
parallelForEach(ObjFile::instances, [&](ObjFile *file) {
|
||||||
MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
|
MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
|
||||||
for (size_t i = 0, e = syms.size(); i != e; ++i)
|
for (size_t i = 0, e = syms.size(); i != e; ++i)
|
||||||
if (Symbol *s = map.lookup(syms[i]))
|
if (Symbol *s = map.lookup(syms[i]))
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
// Logic for deciding what symbols to export, when exporting all
|
// Logic for deciding what symbols to export, when exporting all
|
||||||
// symbols for MinGW.
|
// symbols for MinGW.
|
||||||
|
@ -35,7 +34,7 @@ public:
|
||||||
llvm::StringSet<> excludeLibs;
|
llvm::StringSet<> excludeLibs;
|
||||||
llvm::StringSet<> excludeObjects;
|
llvm::StringSet<> excludeObjects;
|
||||||
|
|
||||||
bool shouldExport(const COFFLinkerContext &ctx, Defined *sym) const;
|
bool shouldExport(Defined *sym) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
void writeDefFile(StringRef name);
|
void writeDefFile(StringRef name);
|
||||||
|
@ -54,10 +53,9 @@ struct WrappedSymbol {
|
||||||
Symbol *wrap;
|
Symbol *wrap;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<WrappedSymbol> addWrappedSymbols(COFFLinkerContext &ctx,
|
std::vector<WrappedSymbol> addWrappedSymbols(llvm::opt::InputArgList &args);
|
||||||
llvm::opt::InputArgList &args);
|
|
||||||
|
|
||||||
void wrapSymbols(COFFLinkerContext &ctx, ArrayRef<WrappedSymbol> wrapped);
|
void wrapSymbols(ArrayRef<WrappedSymbol> wrapped);
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
106
lld/COFF/PDB.cpp
106
lld/COFF/PDB.cpp
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "PDB.h"
|
#include "PDB.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Chunks.h"
|
#include "Chunks.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DebugTypes.h"
|
#include "DebugTypes.h"
|
||||||
|
@ -67,6 +66,16 @@ using llvm::pdb::StringTableFixup;
|
||||||
|
|
||||||
static ExitOnError exitOnErr;
|
static ExitOnError exitOnErr;
|
||||||
|
|
||||||
|
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
|
||||||
|
static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
|
||||||
|
Timer lld::coff::loadGHashTimer("Global Type Hashing", addObjectsTimer);
|
||||||
|
Timer lld::coff::mergeGHashTimer("GHash Type Merging", addObjectsTimer);
|
||||||
|
static Timer typeMergingTimer("Type Merging", addObjectsTimer);
|
||||||
|
static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
|
||||||
|
static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer);
|
||||||
|
static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
|
||||||
|
static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class DebugSHandler;
|
class DebugSHandler;
|
||||||
|
|
||||||
|
@ -74,8 +83,8 @@ class PDBLinker {
|
||||||
friend DebugSHandler;
|
friend DebugSHandler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PDBLinker(COFFLinkerContext &ctx)
|
PDBLinker(SymbolTable *symtab)
|
||||||
: builder(bAlloc), tMerger(ctx, bAlloc), ctx(ctx) {
|
: symtab(symtab), builder(bAlloc), tMerger(bAlloc) {
|
||||||
// This isn't strictly necessary, but link.exe usually puts an empty string
|
// This isn't strictly necessary, but link.exe usually puts an empty string
|
||||||
// as the first "valid" string in the string table, so we do the same in
|
// as the first "valid" string in the string table, so we do the same in
|
||||||
// order to maintain as much byte-for-byte compatibility as possible.
|
// order to maintain as much byte-for-byte compatibility as possible.
|
||||||
|
@ -98,7 +107,7 @@ public:
|
||||||
void addPublicsToPDB();
|
void addPublicsToPDB();
|
||||||
|
|
||||||
/// Link info for each import file in the symbol table into the PDB.
|
/// Link info for each import file in the symbol table into the PDB.
|
||||||
void addImportFilesToPDB();
|
void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
|
||||||
|
|
||||||
void createModuleDBI(ObjFile *file);
|
void createModuleDBI(ObjFile *file);
|
||||||
|
|
||||||
|
@ -135,7 +144,8 @@ public:
|
||||||
std::vector<uint8_t> &storage);
|
std::vector<uint8_t> &storage);
|
||||||
|
|
||||||
/// Add the section map and section contributions to the PDB.
|
/// Add the section map and section contributions to the PDB.
|
||||||
void addSections(ArrayRef<uint8_t> sectionTable);
|
void addSections(ArrayRef<OutputSection *> outputSections,
|
||||||
|
ArrayRef<uint8_t> sectionTable);
|
||||||
|
|
||||||
/// Write the PDB to disk and store the Guid generated for it in *Guid.
|
/// Write the PDB to disk and store the Guid generated for it in *Guid.
|
||||||
void commit(codeview::GUID *guid);
|
void commit(codeview::GUID *guid);
|
||||||
|
@ -144,13 +154,12 @@ public:
|
||||||
void printStats();
|
void printStats();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SymbolTable *symtab;
|
||||||
|
|
||||||
pdb::PDBFileBuilder builder;
|
pdb::PDBFileBuilder builder;
|
||||||
|
|
||||||
TypeMerger tMerger;
|
TypeMerger tMerger;
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
|
|
||||||
/// PDBs use a single global string table for filenames in the file checksum
|
/// PDBs use a single global string table for filenames in the file checksum
|
||||||
/// table.
|
/// table.
|
||||||
DebugStringTableSubsection pdbStrTab;
|
DebugStringTableSubsection pdbStrTab;
|
||||||
|
@ -290,12 +299,11 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addGHashTypeInfo(COFFLinkerContext &ctx,
|
static void addGHashTypeInfo(pdb::PDBFileBuilder &builder) {
|
||||||
pdb::PDBFileBuilder &builder) {
|
|
||||||
// Start the TPI or IPI stream header.
|
// Start the TPI or IPI stream header.
|
||||||
builder.getTpiBuilder().setVersionHeader(pdb::PdbTpiV80);
|
builder.getTpiBuilder().setVersionHeader(pdb::PdbTpiV80);
|
||||||
builder.getIpiBuilder().setVersionHeader(pdb::PdbTpiV80);
|
builder.getIpiBuilder().setVersionHeader(pdb::PdbTpiV80);
|
||||||
for_each(ctx.tpiSourceList, [&](TpiSource *source) {
|
for_each(TpiSource::instances, [&](TpiSource *source) {
|
||||||
builder.getTpiBuilder().addTypeRecords(source->mergedTpi.recs,
|
builder.getTpiBuilder().addTypeRecords(source->mergedTpi.recs,
|
||||||
source->mergedTpi.recSizes,
|
source->mergedTpi.recSizes,
|
||||||
source->mergedTpi.recHashes);
|
source->mergedTpi.recHashes);
|
||||||
|
@ -711,9 +719,8 @@ Error PDBLinker::commitSymbolsForObject(void *ctx, void *obj,
|
||||||
static_cast<ObjFile *>(obj), writer);
|
static_cast<ObjFile *>(obj), writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static pdb::SectionContrib createSectionContrib(COFFLinkerContext &ctx,
|
static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
|
||||||
const Chunk *c, uint32_t modi) {
|
OutputSection *os = c ? c->getOutputSection() : nullptr;
|
||||||
OutputSection *os = c ? ctx.getOutputSection(c) : nullptr;
|
|
||||||
pdb::SectionContrib sc;
|
pdb::SectionContrib sc;
|
||||||
memset(&sc, 0, sizeof(sc));
|
memset(&sc, 0, sizeof(sc));
|
||||||
sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex;
|
sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex;
|
||||||
|
@ -1016,7 +1023,7 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
|
||||||
if (!source->file)
|
if (!source->file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ScopedTimer t(ctx.symbolMergingTimer);
|
ScopedTimer t(symbolMergingTimer);
|
||||||
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
|
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
|
||||||
DebugSHandler dsh(*this, *source->file, source);
|
DebugSHandler dsh(*this, *source->file, source);
|
||||||
// Now do all live .debug$S and .debug$F sections.
|
// Now do all live .debug$S and .debug$F sections.
|
||||||
|
@ -1075,7 +1082,7 @@ void PDBLinker::createModuleDBI(ObjFile *file) {
|
||||||
auto *secChunk = dyn_cast<SectionChunk>(c);
|
auto *secChunk = dyn_cast<SectionChunk>(c);
|
||||||
if (!secChunk || !secChunk->live)
|
if (!secChunk || !secChunk->live)
|
||||||
continue;
|
continue;
|
||||||
pdb::SectionContrib sc = createSectionContrib(ctx, secChunk, modi);
|
pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
|
||||||
file->moduleDBI->setFirstSectionContrib(sc);
|
file->moduleDBI->setFirstSectionContrib(sc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1088,7 +1095,7 @@ void PDBLinker::addDebug(TpiSource *source) {
|
||||||
// indices to PDB type and item indices. If we are using ghashes, types have
|
// indices to PDB type and item indices. If we are using ghashes, types have
|
||||||
// already been merged.
|
// already been merged.
|
||||||
if (!config->debugGHashes) {
|
if (!config->debugGHashes) {
|
||||||
ScopedTimer t(ctx.typeMergingTimer);
|
ScopedTimer t(typeMergingTimer);
|
||||||
if (Error e = source->mergeDebugT(&tMerger)) {
|
if (Error e = source->mergeDebugT(&tMerger)) {
|
||||||
// If type merging failed, ignore the symbols.
|
// If type merging failed, ignore the symbols.
|
||||||
warnUnusable(source->file, std::move(e));
|
warnUnusable(source->file, std::move(e));
|
||||||
|
@ -1106,7 +1113,7 @@ void PDBLinker::addDebug(TpiSource *source) {
|
||||||
addDebugSymbols(source);
|
addDebugSymbols(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) {
|
static pdb::BulkPublic createPublic(Defined *def) {
|
||||||
pdb::BulkPublic pub;
|
pdb::BulkPublic pub;
|
||||||
pub.Name = def->getName().data();
|
pub.Name = def->getName().data();
|
||||||
pub.NameLen = def->getName().size();
|
pub.NameLen = def->getName().size();
|
||||||
|
@ -1120,7 +1127,7 @@ static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) {
|
||||||
}
|
}
|
||||||
pub.setFlags(flags);
|
pub.setFlags(flags);
|
||||||
|
|
||||||
OutputSection *os = ctx.getOutputSection(def->getChunk());
|
OutputSection *os = def->getChunk()->getOutputSection();
|
||||||
assert(os && "all publics should be in final image");
|
assert(os && "all publics should be in final image");
|
||||||
pub.Offset = def->getRVA() - os->getRVA();
|
pub.Offset = def->getRVA() - os->getRVA();
|
||||||
pub.Segment = os->sectionIndex;
|
pub.Segment = os->sectionIndex;
|
||||||
|
@ -1130,31 +1137,32 @@ static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) {
|
||||||
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
|
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
|
||||||
// TpiData.
|
// TpiData.
|
||||||
void PDBLinker::addObjectsToPDB() {
|
void PDBLinker::addObjectsToPDB() {
|
||||||
ScopedTimer t1(ctx.addObjectsTimer);
|
ScopedTimer t1(addObjectsTimer);
|
||||||
|
|
||||||
// Create module descriptors
|
// Create module descriptors
|
||||||
for_each(ctx.objFileInstances, [&](ObjFile *obj) { createModuleDBI(obj); });
|
for_each(ObjFile::instances, [&](ObjFile *obj) { createModuleDBI(obj); });
|
||||||
|
|
||||||
// Reorder dependency type sources to come first.
|
// Reorder dependency type sources to come first.
|
||||||
tMerger.sortDependencies();
|
TpiSource::sortDependencies();
|
||||||
|
|
||||||
// Merge type information from input files using global type hashing.
|
// Merge type information from input files using global type hashing.
|
||||||
if (config->debugGHashes)
|
if (config->debugGHashes)
|
||||||
tMerger.mergeTypesWithGHash();
|
tMerger.mergeTypesWithGHash();
|
||||||
|
|
||||||
// Merge dependencies and then regular objects.
|
// Merge dependencies and then regular objects.
|
||||||
for_each(tMerger.dependencySources,
|
for_each(TpiSource::dependencySources,
|
||||||
|
[&](TpiSource *source) { addDebug(source); });
|
||||||
|
for_each(TpiSource::objectSources,
|
||||||
[&](TpiSource *source) { addDebug(source); });
|
[&](TpiSource *source) { addDebug(source); });
|
||||||
for_each(tMerger.objectSources, [&](TpiSource *source) { addDebug(source); });
|
|
||||||
|
|
||||||
builder.getStringTableBuilder().setStrings(pdbStrTab);
|
builder.getStringTableBuilder().setStrings(pdbStrTab);
|
||||||
t1.stop();
|
t1.stop();
|
||||||
|
|
||||||
// Construct TPI and IPI stream contents.
|
// Construct TPI and IPI stream contents.
|
||||||
ScopedTimer t2(ctx.tpiStreamLayoutTimer);
|
ScopedTimer t2(tpiStreamLayoutTimer);
|
||||||
// Collect all the merged types.
|
// Collect all the merged types.
|
||||||
if (config->debugGHashes) {
|
if (config->debugGHashes) {
|
||||||
addGHashTypeInfo(ctx, builder);
|
addGHashTypeInfo(builder);
|
||||||
} else {
|
} else {
|
||||||
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
|
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
|
||||||
addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
|
addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
|
||||||
|
@ -1162,7 +1170,7 @@ void PDBLinker::addObjectsToPDB() {
|
||||||
t2.stop();
|
t2.stop();
|
||||||
|
|
||||||
if (config->showSummary) {
|
if (config->showSummary) {
|
||||||
for_each(ctx.tpiSourceList, [&](TpiSource *source) {
|
for_each(TpiSource::instances, [&](TpiSource *source) {
|
||||||
nbTypeRecords += source->nbTypeRecords;
|
nbTypeRecords += source->nbTypeRecords;
|
||||||
nbTypeRecordsBytes += source->nbTypeRecordsBytes;
|
nbTypeRecordsBytes += source->nbTypeRecordsBytes;
|
||||||
});
|
});
|
||||||
|
@ -1170,11 +1178,11 @@ void PDBLinker::addObjectsToPDB() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDBLinker::addPublicsToPDB() {
|
void PDBLinker::addPublicsToPDB() {
|
||||||
ScopedTimer t3(ctx.publicsLayoutTimer);
|
ScopedTimer t3(publicsLayoutTimer);
|
||||||
// Compute the public symbols.
|
// Compute the public symbols.
|
||||||
auto &gsiBuilder = builder.getGsiBuilder();
|
auto &gsiBuilder = builder.getGsiBuilder();
|
||||||
std::vector<pdb::BulkPublic> publics;
|
std::vector<pdb::BulkPublic> publics;
|
||||||
ctx.symtab.forEachSymbol([&publics, this](Symbol *s) {
|
symtab->forEachSymbol([&publics](Symbol *s) {
|
||||||
// Only emit external, defined, live symbols that have a chunk. Static,
|
// Only emit external, defined, live symbols that have a chunk. Static,
|
||||||
// non-external symbols do not appear in the symbol table.
|
// non-external symbols do not appear in the symbol table.
|
||||||
auto *def = dyn_cast<Defined>(s);
|
auto *def = dyn_cast<Defined>(s);
|
||||||
|
@ -1195,7 +1203,7 @@ void PDBLinker::addPublicsToPDB() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
publics.push_back(createPublic(ctx, def));
|
publics.push_back(createPublic(def));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1219,10 +1227,10 @@ void PDBLinker::printStats() {
|
||||||
stream << format_decimal(v, 15) << " " << s << '\n';
|
stream << format_decimal(v, 15) << " " << s << '\n';
|
||||||
};
|
};
|
||||||
|
|
||||||
print(ctx.objFileInstances.size(),
|
print(ObjFile::instances.size(),
|
||||||
"Input OBJ files (expanded from all cmd-line inputs)");
|
"Input OBJ files (expanded from all cmd-line inputs)");
|
||||||
print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies");
|
print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies");
|
||||||
print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies");
|
print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies");
|
||||||
print(nbTypeRecords, "Input type records");
|
print(nbTypeRecords, "Input type records");
|
||||||
print(nbTypeRecordsBytes, "Input type records bytes");
|
print(nbTypeRecordsBytes, "Input type records bytes");
|
||||||
print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records");
|
print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records");
|
||||||
|
@ -1475,13 +1483,13 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all import files as modules to the PDB.
|
// Add all import files as modules to the PDB.
|
||||||
void PDBLinker::addImportFilesToPDB() {
|
void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
|
||||||
if (ctx.importFileInstances.empty())
|
if (ImportFile::instances.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
|
std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
|
||||||
|
|
||||||
for (ImportFile *file : ctx.importFileInstances) {
|
for (ImportFile *file : ImportFile::instances) {
|
||||||
if (!file->live)
|
if (!file->live)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1505,7 +1513,7 @@ void PDBLinker::addImportFilesToPDB() {
|
||||||
exitOnErr(dbiBuilder.addModuleInfo(file->dllName));
|
exitOnErr(dbiBuilder.addModuleInfo(file->dllName));
|
||||||
firstMod.setObjFileName(libPath);
|
firstMod.setObjFileName(libPath);
|
||||||
pdb::SectionContrib sc =
|
pdb::SectionContrib sc =
|
||||||
createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex);
|
createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
|
||||||
firstMod.setFirstSectionContrib(sc);
|
firstMod.setFirstSectionContrib(sc);
|
||||||
|
|
||||||
// The second module is where the import stream goes.
|
// The second module is where the import stream goes.
|
||||||
|
@ -1515,7 +1523,7 @@ void PDBLinker::addImportFilesToPDB() {
|
||||||
|
|
||||||
DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
|
DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
|
||||||
Chunk *thunkChunk = thunk->getChunk();
|
Chunk *thunkChunk = thunk->getChunk();
|
||||||
OutputSection *thunkOS = ctx.getOutputSection(thunkChunk);
|
OutputSection *thunkOS = thunkChunk->getOutputSection();
|
||||||
|
|
||||||
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
|
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
|
||||||
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
|
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
|
||||||
|
@ -1557,27 +1565,28 @@ void PDBLinker::addImportFilesToPDB() {
|
||||||
mod->addSymbol(newSym);
|
mod->addSymbol(newSym);
|
||||||
|
|
||||||
pdb::SectionContrib sc =
|
pdb::SectionContrib sc =
|
||||||
createSectionContrib(ctx, thunk->getChunk(), mod->getModuleIndex());
|
createSectionContrib(thunk->getChunk(), mod->getModuleIndex());
|
||||||
mod->setFirstSectionContrib(sc);
|
mod->setFirstSectionContrib(sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a PDB file.
|
// Creates a PDB file.
|
||||||
void lld::coff::createPDB(COFFLinkerContext &ctx,
|
void lld::coff::createPDB(SymbolTable *symtab,
|
||||||
|
ArrayRef<OutputSection *> outputSections,
|
||||||
ArrayRef<uint8_t> sectionTable,
|
ArrayRef<uint8_t> sectionTable,
|
||||||
llvm::codeview::DebugInfo *buildId) {
|
llvm::codeview::DebugInfo *buildId) {
|
||||||
ScopedTimer t1(ctx.totalPdbLinkTimer);
|
ScopedTimer t1(totalPdbLinkTimer);
|
||||||
PDBLinker pdb(ctx);
|
PDBLinker pdb(symtab);
|
||||||
|
|
||||||
pdb.initialize(buildId);
|
pdb.initialize(buildId);
|
||||||
pdb.addObjectsToPDB();
|
pdb.addObjectsToPDB();
|
||||||
pdb.addImportFilesToPDB();
|
pdb.addImportFilesToPDB(outputSections);
|
||||||
pdb.addSections(sectionTable);
|
pdb.addSections(outputSections, sectionTable);
|
||||||
pdb.addNatvisFiles();
|
pdb.addNatvisFiles();
|
||||||
pdb.addNamedStreams();
|
pdb.addNamedStreams();
|
||||||
pdb.addPublicsToPDB();
|
pdb.addPublicsToPDB();
|
||||||
|
|
||||||
ScopedTimer t2(ctx.diskCommitTimer);
|
ScopedTimer t2(diskCommitTimer);
|
||||||
codeview::GUID guid;
|
codeview::GUID guid;
|
||||||
pdb.commit(&guid);
|
pdb.commit(&guid);
|
||||||
memcpy(&buildId->PDB70.Signature, &guid, 16);
|
memcpy(&buildId->PDB70.Signature, &guid, 16);
|
||||||
|
@ -1617,7 +1626,8 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
|
||||||
dbiBuilder.setBuildNumber(14, 11);
|
dbiBuilder.setBuildNumber(14, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
|
void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
|
||||||
|
ArrayRef<uint8_t> sectionTable) {
|
||||||
// It's not entirely clear what this is, but the * Linker * module uses it.
|
// It's not entirely clear what this is, but the * Linker * module uses it.
|
||||||
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
|
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
|
||||||
nativePath = config->pdbPath;
|
nativePath = config->pdbPath;
|
||||||
|
@ -1628,11 +1638,11 @@ void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
|
||||||
addCommonLinkerModuleSymbols(nativePath, linkerModule);
|
addCommonLinkerModuleSymbols(nativePath, linkerModule);
|
||||||
|
|
||||||
// Add section contributions. They must be ordered by ascending RVA.
|
// Add section contributions. They must be ordered by ascending RVA.
|
||||||
for (OutputSection *os : ctx.outputSections) {
|
for (OutputSection *os : outputSections) {
|
||||||
addLinkerModuleSectionSymbol(linkerModule, *os);
|
addLinkerModuleSectionSymbol(linkerModule, *os);
|
||||||
for (Chunk *c : os->chunks) {
|
for (Chunk *c : os->chunks) {
|
||||||
pdb::SectionContrib sc =
|
pdb::SectionContrib sc =
|
||||||
createSectionContrib(ctx, c, linkerModule.getModuleIndex());
|
createSectionContrib(c, linkerModule.getModuleIndex());
|
||||||
builder.getDbiBuilder().addSectionContrib(sc);
|
builder.getDbiBuilder().addSectionContrib(sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1641,7 +1651,7 @@ void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
|
||||||
// to provide trampolines thunks for incremental function patching. Set this
|
// to provide trampolines thunks for incremental function patching. Set this
|
||||||
// as "unused" because LLD doesn't support /INCREMENTAL link.
|
// as "unused" because LLD doesn't support /INCREMENTAL link.
|
||||||
pdb::SectionContrib sc =
|
pdb::SectionContrib sc =
|
||||||
createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex);
|
createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
|
||||||
linkerModule.setFirstSectionContrib(sc);
|
linkerModule.setFirstSectionContrib(sc);
|
||||||
|
|
||||||
// Add Section Map stream.
|
// Add Section Map stream.
|
||||||
|
|
|
@ -23,15 +23,21 @@ namespace lld {
|
||||||
class Timer;
|
class Timer;
|
||||||
|
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
class OutputSection;
|
||||||
class SectionChunk;
|
class SectionChunk;
|
||||||
class COFFLinkerContext;
|
class SymbolTable;
|
||||||
|
|
||||||
void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef<uint8_t> sectionTable,
|
void createPDB(SymbolTable *symtab,
|
||||||
|
llvm::ArrayRef<OutputSection *> outputSections,
|
||||||
|
llvm::ArrayRef<uint8_t> sectionTable,
|
||||||
llvm::codeview::DebugInfo *buildId);
|
llvm::codeview::DebugInfo *buildId);
|
||||||
|
|
||||||
llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
|
llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
|
||||||
getFileLineCodeView(const SectionChunk *c, uint32_t addr);
|
getFileLineCodeView(const SectionChunk *c, uint32_t addr);
|
||||||
|
|
||||||
|
extern Timer loadGHashTimer;
|
||||||
|
extern Timer mergeGHashTimer;
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Driver.h"
|
#include "Driver.h"
|
||||||
#include "LTO.h"
|
#include "LTO.h"
|
||||||
|
@ -35,6 +34,10 @@ StringRef ltrim1(StringRef s, const char *chars) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Timer ltoTimer("LTO", Timer::root());
|
||||||
|
|
||||||
|
SymbolTable *symtab;
|
||||||
|
|
||||||
void SymbolTable::addFile(InputFile *file) {
|
void SymbolTable::addFile(InputFile *file) {
|
||||||
log("Reading " + toString(file));
|
log("Reading " + toString(file));
|
||||||
file->parse();
|
file->parse();
|
||||||
|
@ -49,11 +52,11 @@ void SymbolTable::addFile(InputFile *file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto *f = dyn_cast<ObjFile>(file)) {
|
if (auto *f = dyn_cast<ObjFile>(file)) {
|
||||||
ctx.objFileInstances.push_back(f);
|
ObjFile::instances.push_back(f);
|
||||||
} else if (auto *f = dyn_cast<BitcodeFile>(file)) {
|
} else if (auto *f = dyn_cast<BitcodeFile>(file)) {
|
||||||
ctx.bitcodeFileInstances.push_back(f);
|
BitcodeFile::instances.push_back(f);
|
||||||
} else if (auto *f = dyn_cast<ImportFile>(file)) {
|
} else if (auto *f = dyn_cast<ImportFile>(file)) {
|
||||||
ctx.importFileInstances.push_back(f);
|
ImportFile::instances.push_back(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
driver->parseDirectives(file);
|
driver->parseDirectives(file);
|
||||||
|
@ -369,9 +372,12 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||||
/// defined symbol imported" diagnostic for symbols in localImports.
|
/// defined symbol imported" diagnostic for symbols in localImports.
|
||||||
/// objFiles and bitcodeFiles (if not nullptr) are used to report where
|
/// objFiles and bitcodeFiles (if not nullptr) are used to report where
|
||||||
/// undefined symbols are referenced.
|
/// undefined symbols are referenced.
|
||||||
static void reportProblemSymbols(
|
static void
|
||||||
const COFFLinkerContext &ctx, const SmallPtrSetImpl<Symbol *> &undefs,
|
reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs,
|
||||||
const DenseMap<Symbol *, Symbol *> *localImports, bool needBitcodeFiles) {
|
const DenseMap<Symbol *, Symbol *> *localImports,
|
||||||
|
const std::vector<ObjFile *> objFiles,
|
||||||
|
const std::vector<BitcodeFile *> *bitcodeFiles) {
|
||||||
|
|
||||||
// Return early if there is nothing to report (which should be
|
// Return early if there is nothing to report (which should be
|
||||||
// the common case).
|
// the common case).
|
||||||
if (undefs.empty() && (!localImports || localImports->empty()))
|
if (undefs.empty() && (!localImports || localImports->empty()))
|
||||||
|
@ -412,11 +418,11 @@ static void reportProblemSymbols(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (ObjFile *file : ctx.objFileInstances)
|
for (ObjFile *file : objFiles)
|
||||||
processFile(file, file->getSymbols());
|
processFile(file, file->getSymbols());
|
||||||
|
|
||||||
if (needBitcodeFiles)
|
if (bitcodeFiles)
|
||||||
for (BitcodeFile *file : ctx.bitcodeFileInstances)
|
for (BitcodeFile *file : *bitcodeFiles)
|
||||||
processFile(file, file->getSymbols());
|
processFile(file, file->getSymbols());
|
||||||
|
|
||||||
for (const UndefinedDiag &undefDiag : undefDiags)
|
for (const UndefinedDiag &undefDiag : undefDiags)
|
||||||
|
@ -445,8 +451,9 @@ void SymbolTable::reportUnresolvable() {
|
||||||
undefs.insert(sym);
|
undefs.insert(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
reportProblemSymbols(ctx, undefs,
|
reportProblemSymbols(undefs,
|
||||||
/* localImports */ nullptr, true);
|
/* localImports */ nullptr, ObjFile::instances,
|
||||||
|
&BitcodeFile::instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::resolveRemainingUndefines() {
|
void SymbolTable::resolveRemainingUndefines() {
|
||||||
|
@ -508,8 +515,8 @@ void SymbolTable::resolveRemainingUndefines() {
|
||||||
}
|
}
|
||||||
|
|
||||||
reportProblemSymbols(
|
reportProblemSymbols(
|
||||||
ctx, undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
|
undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
|
||||||
false);
|
ObjFile::instances, /* bitcode files no longer needed */ nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
|
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
|
||||||
|
@ -790,20 +797,20 @@ void SymbolTable::addLibcall(StringRef name) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Chunk *> SymbolTable::getChunks() const {
|
std::vector<Chunk *> SymbolTable::getChunks() {
|
||||||
std::vector<Chunk *> res;
|
std::vector<Chunk *> res;
|
||||||
for (ObjFile *file : ctx.objFileInstances) {
|
for (ObjFile *file : ObjFile::instances) {
|
||||||
ArrayRef<Chunk *> v = file->getChunks();
|
ArrayRef<Chunk *> v = file->getChunks();
|
||||||
res.insert(res.end(), v.begin(), v.end());
|
res.insert(res.end(), v.begin(), v.end());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *SymbolTable::find(StringRef name) const {
|
Symbol *SymbolTable::find(StringRef name) {
|
||||||
return symMap.lookup(CachedHashStringRef(name));
|
return symMap.lookup(CachedHashStringRef(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *SymbolTable::findUnderscore(StringRef name) const {
|
Symbol *SymbolTable::findUnderscore(StringRef name) {
|
||||||
if (config->machine == I386)
|
if (config->machine == I386)
|
||||||
return find(("_" + name).str());
|
return find(("_" + name).str());
|
||||||
return find(name);
|
return find(name);
|
||||||
|
@ -866,17 +873,17 @@ Symbol *SymbolTable::addUndefined(StringRef name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::addCombinedLTOObjects() {
|
void SymbolTable::addCombinedLTOObjects() {
|
||||||
if (ctx.bitcodeFileInstances.empty())
|
if (BitcodeFile::instances.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ScopedTimer t(ctx.ltoTimer);
|
ScopedTimer t(ltoTimer);
|
||||||
lto.reset(new BitcodeCompiler());
|
lto.reset(new BitcodeCompiler);
|
||||||
for (BitcodeFile *f : ctx.bitcodeFileInstances)
|
for (BitcodeFile *f : BitcodeFile::instances)
|
||||||
lto->add(*f);
|
lto->add(*f);
|
||||||
for (InputFile *newObj : lto->compile(ctx)) {
|
for (InputFile *newObj : lto->compile()) {
|
||||||
ObjFile *obj = cast<ObjFile>(newObj);
|
ObjFile *obj = cast<ObjFile>(newObj);
|
||||||
obj->parse();
|
obj->parse();
|
||||||
ctx.objFileInstances.push_back(obj);
|
ObjFile::instances.push_back(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ namespace coff {
|
||||||
|
|
||||||
class Chunk;
|
class Chunk;
|
||||||
class CommonChunk;
|
class CommonChunk;
|
||||||
class COFFLinkerContext;
|
|
||||||
class Defined;
|
class Defined;
|
||||||
class DefinedAbsolute;
|
class DefinedAbsolute;
|
||||||
class DefinedRegular;
|
class DefinedRegular;
|
||||||
|
@ -48,8 +47,6 @@ class Symbol;
|
||||||
// There is one add* function per symbol type.
|
// There is one add* function per symbol type.
|
||||||
class SymbolTable {
|
class SymbolTable {
|
||||||
public:
|
public:
|
||||||
SymbolTable(COFFLinkerContext &ctx) : ctx(ctx) {}
|
|
||||||
|
|
||||||
void addFile(InputFile *file);
|
void addFile(InputFile *file);
|
||||||
|
|
||||||
// Emit errors for symbols that cannot be resolved.
|
// Emit errors for symbols that cannot be resolved.
|
||||||
|
@ -66,11 +63,11 @@ public:
|
||||||
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
||||||
|
|
||||||
// Returns a list of chunks of selected symbols.
|
// Returns a list of chunks of selected symbols.
|
||||||
std::vector<Chunk *> getChunks() const;
|
std::vector<Chunk *> getChunks();
|
||||||
|
|
||||||
// Returns a symbol for a given name. Returns a nullptr if not found.
|
// Returns a symbol for a given name. Returns a nullptr if not found.
|
||||||
Symbol *find(StringRef name) const;
|
Symbol *find(StringRef name);
|
||||||
Symbol *findUnderscore(StringRef name) const;
|
Symbol *findUnderscore(StringRef name);
|
||||||
|
|
||||||
// Occasionally we have to resolve an undefined symbol to its
|
// Occasionally we have to resolve an undefined symbol to its
|
||||||
// mangled symbol. This function tries to find a mangled name
|
// mangled symbol. This function tries to find a mangled name
|
||||||
|
@ -134,10 +131,10 @@ private:
|
||||||
|
|
||||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
|
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
|
||||||
std::unique_ptr<BitcodeCompiler> lto;
|
std::unique_ptr<BitcodeCompiler> lto;
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern SymbolTable *symtab;
|
||||||
|
|
||||||
std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
|
std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
|
||||||
|
|
||||||
StringRef ltrim1(StringRef s, const char *chars);
|
StringRef ltrim1(StringRef s, const char *chars);
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
#define LLD_COFF_TYPEMERGER_H
|
#define LLD_COFF_TYPEMERGER_H
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DebugTypes.h"
|
|
||||||
#include "lld/Common/Timer.h"
|
|
||||||
#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
|
#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeHashing.h"
|
#include "llvm/DebugInfo/CodeView/TypeHashing.h"
|
||||||
#include "llvm/Support/Allocator.h"
|
#include "llvm/Support/Allocator.h"
|
||||||
|
@ -27,7 +25,7 @@ struct GHashState;
|
||||||
|
|
||||||
class TypeMerger {
|
class TypeMerger {
|
||||||
public:
|
public:
|
||||||
TypeMerger(COFFLinkerContext &ctx, llvm::BumpPtrAllocator &alloc);
|
TypeMerger(llvm::BumpPtrAllocator &alloc);
|
||||||
|
|
||||||
~TypeMerger();
|
~TypeMerger();
|
||||||
|
|
||||||
|
@ -61,20 +59,6 @@ public:
|
||||||
// keyed by type index.
|
// keyed by type index.
|
||||||
SmallVector<uint32_t, 0> tpiCounts;
|
SmallVector<uint32_t, 0> tpiCounts;
|
||||||
SmallVector<uint32_t, 0> ipiCounts;
|
SmallVector<uint32_t, 0> ipiCounts;
|
||||||
|
|
||||||
/// Dependency type sources, such as type servers or PCH object files. These
|
|
||||||
/// must be processed before objects that rely on them. Set by
|
|
||||||
/// sortDependencies.
|
|
||||||
ArrayRef<TpiSource *> dependencySources;
|
|
||||||
|
|
||||||
/// Object file sources. These must be processed after dependencySources.
|
|
||||||
ArrayRef<TpiSource *> objectSources;
|
|
||||||
|
|
||||||
/// Sorts the dependencies and reassigns TpiSource indices.
|
|
||||||
void sortDependencies();
|
|
||||||
|
|
||||||
private:
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "Writer.h"
|
#include "Writer.h"
|
||||||
#include "COFFLinkerContext.h"
|
|
||||||
#include "CallGraphSort.h"
|
#include "CallGraphSort.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DLL.h"
|
#include "DLL.h"
|
||||||
|
@ -81,14 +80,23 @@ static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8");
|
||||||
|
|
||||||
static const int numberOfDataDirectory = 16;
|
static const int numberOfDataDirectory = 16;
|
||||||
|
|
||||||
|
// Global vector of all output sections. After output sections are finalized,
|
||||||
|
// this can be indexed by Chunk::getOutputSection.
|
||||||
|
static std::vector<OutputSection *> outputSections;
|
||||||
|
|
||||||
|
OutputSection *Chunk::getOutputSection() const {
|
||||||
|
return osidx == 0 ? nullptr : outputSections[osidx - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputSection::clear() { outputSections.clear(); }
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class DebugDirectoryChunk : public NonSectionChunk {
|
class DebugDirectoryChunk : public NonSectionChunk {
|
||||||
public:
|
public:
|
||||||
DebugDirectoryChunk(COFFLinkerContext &c,
|
DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
|
||||||
const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
|
|
||||||
bool writeRepro)
|
bool writeRepro)
|
||||||
: records(r), writeRepro(writeRepro), ctx(c) {}
|
: records(r), writeRepro(writeRepro) {}
|
||||||
|
|
||||||
size_t getSize() const override {
|
size_t getSize() const override {
|
||||||
return (records.size() + int(writeRepro)) * sizeof(debug_directory);
|
return (records.size() + int(writeRepro)) * sizeof(debug_directory);
|
||||||
|
@ -99,7 +107,7 @@ public:
|
||||||
|
|
||||||
for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
|
for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
|
||||||
Chunk *c = record.second;
|
Chunk *c = record.second;
|
||||||
OutputSection *os = ctx.getOutputSection(c);
|
OutputSection *os = c->getOutputSection();
|
||||||
uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
|
uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
|
||||||
fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
|
fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
|
||||||
++d;
|
++d;
|
||||||
|
@ -138,8 +146,6 @@ private:
|
||||||
mutable std::vector<support::ulittle32_t *> timeDateStamps;
|
mutable std::vector<support::ulittle32_t *> timeDateStamps;
|
||||||
const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
|
const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
|
||||||
bool writeRepro;
|
bool writeRepro;
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CVDebugRecordChunk : public NonSectionChunk {
|
class CVDebugRecordChunk : public NonSectionChunk {
|
||||||
|
@ -195,7 +201,7 @@ public:
|
||||||
// The writer writes a SymbolTable result to a file.
|
// The writer writes a SymbolTable result to a file.
|
||||||
class Writer {
|
class Writer {
|
||||||
public:
|
public:
|
||||||
Writer(COFFLinkerContext &c) : buffer(errorHandler().outputBuffer), ctx(c) {}
|
Writer() : buffer(errorHandler().outputBuffer) {}
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -298,12 +304,13 @@ private:
|
||||||
// files, so we need to keep track of them separately.
|
// files, so we need to keep track of them separately.
|
||||||
Chunk *firstPdata = nullptr;
|
Chunk *firstPdata = nullptr;
|
||||||
Chunk *lastPdata;
|
Chunk *lastPdata;
|
||||||
|
|
||||||
COFFLinkerContext &ctx;
|
|
||||||
};
|
};
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
void lld::coff::writeResult(COFFLinkerContext &ctx) { Writer(ctx).run(); }
|
static Timer codeLayoutTimer("Code Layout", Timer::root());
|
||||||
|
static Timer diskCommitTimer("Commit Output File", Timer::root());
|
||||||
|
|
||||||
|
void lld::coff::writeResult() { Writer().run(); }
|
||||||
|
|
||||||
void OutputSection::addChunk(Chunk *c) {
|
void OutputSection::addChunk(Chunk *c) {
|
||||||
chunks.push_back(c);
|
chunks.push_back(c);
|
||||||
|
@ -542,7 +549,7 @@ void Writer::finalizeAddresses() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t origNumChunks = 0;
|
size_t origNumChunks = 0;
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
sec->origChunks = sec->chunks;
|
sec->origChunks = sec->chunks;
|
||||||
origNumChunks += sec->chunks.size();
|
origNumChunks += sec->chunks.size();
|
||||||
}
|
}
|
||||||
|
@ -554,7 +561,7 @@ void Writer::finalizeAddresses() {
|
||||||
// adding them turned out ok.
|
// adding them turned out ok.
|
||||||
bool rangesOk = true;
|
bool rangesOk = true;
|
||||||
size_t numChunks = 0;
|
size_t numChunks = 0;
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
if (!verifyRanges(sec->chunks)) {
|
if (!verifyRanges(sec->chunks)) {
|
||||||
rangesOk = false;
|
rangesOk = false;
|
||||||
break;
|
break;
|
||||||
|
@ -575,7 +582,7 @@ void Writer::finalizeAddresses() {
|
||||||
// If the previous pass didn't work out, reset everything back to the
|
// If the previous pass didn't work out, reset everything back to the
|
||||||
// original conditions before retrying with a wider margin. This should
|
// original conditions before retrying with a wider margin. This should
|
||||||
// ideally never happen under real circumstances.
|
// ideally never happen under real circumstances.
|
||||||
for (OutputSection *sec : ctx.outputSections)
|
for (OutputSection *sec : outputSections)
|
||||||
sec->chunks = sec->origChunks;
|
sec->chunks = sec->origChunks;
|
||||||
margin *= 2;
|
margin *= 2;
|
||||||
}
|
}
|
||||||
|
@ -583,7 +590,7 @@ void Writer::finalizeAddresses() {
|
||||||
// Try adding thunks everywhere where it is needed, with a margin
|
// Try adding thunks everywhere where it is needed, with a margin
|
||||||
// to avoid things going out of range due to the added thunks.
|
// to avoid things going out of range due to the added thunks.
|
||||||
bool addressesChanged = false;
|
bool addressesChanged = false;
|
||||||
for (OutputSection *sec : ctx.outputSections)
|
for (OutputSection *sec : outputSections)
|
||||||
addressesChanged |= createThunks(sec, margin);
|
addressesChanged |= createThunks(sec, margin);
|
||||||
// If the verification above thought we needed thunks, we should have
|
// If the verification above thought we needed thunks, we should have
|
||||||
// added some.
|
// added some.
|
||||||
|
@ -600,7 +607,7 @@ void Writer::finalizeAddresses() {
|
||||||
|
|
||||||
// The main function of the writer.
|
// The main function of the writer.
|
||||||
void Writer::run() {
|
void Writer::run() {
|
||||||
ScopedTimer t1(ctx.codeLayoutTimer);
|
ScopedTimer t1(codeLayoutTimer);
|
||||||
|
|
||||||
createImportTables();
|
createImportTables();
|
||||||
createSections();
|
createSections();
|
||||||
|
@ -638,17 +645,17 @@ void Writer::run() {
|
||||||
|
|
||||||
if (!config->pdbPath.empty() && config->debug) {
|
if (!config->pdbPath.empty() && config->debug) {
|
||||||
assert(buildId);
|
assert(buildId);
|
||||||
createPDB(ctx, sectionTable, buildId->buildId);
|
createPDB(symtab, outputSections, sectionTable, buildId->buildId);
|
||||||
}
|
}
|
||||||
writeBuildId();
|
writeBuildId();
|
||||||
|
|
||||||
writeLLDMapFile(ctx);
|
writeLLDMapFile(outputSections);
|
||||||
writeMapFile(ctx);
|
writeMapFile(outputSections);
|
||||||
|
|
||||||
if (errorCount())
|
if (errorCount())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ScopedTimer t2(ctx.outputCommitTimer);
|
ScopedTimer t2(diskCommitTimer);
|
||||||
if (auto e = buffer->commit())
|
if (auto e = buffer->commit())
|
||||||
fatal("failed to write the output file: " + toString(std::move(e)));
|
fatal("failed to write the output file: " + toString(std::move(e)));
|
||||||
}
|
}
|
||||||
|
@ -809,8 +816,7 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
|
||||||
|
|
||||||
void Writer::sortSections() {
|
void Writer::sortSections() {
|
||||||
if (!config->callGraphProfile.empty()) {
|
if (!config->callGraphProfile.empty()) {
|
||||||
DenseMap<const SectionChunk *, int> order =
|
DenseMap<const SectionChunk *, int> order = computeCallGraphProfileOrder();
|
||||||
computeCallGraphProfileOrder(ctx);
|
|
||||||
for (auto it : order) {
|
for (auto it : order) {
|
||||||
if (DefinedRegular *sym = it.first->sym)
|
if (DefinedRegular *sym = it.first->sym)
|
||||||
config->order[sym->getName()] = it.second;
|
config->order[sym->getName()] = it.second;
|
||||||
|
@ -837,7 +843,7 @@ void Writer::createSections() {
|
||||||
OutputSection *&sec = sections[{name, outChars}];
|
OutputSection *&sec = sections[{name, outChars}];
|
||||||
if (!sec) {
|
if (!sec) {
|
||||||
sec = make<OutputSection>(name, outChars);
|
sec = make<OutputSection>(name, outChars);
|
||||||
ctx.outputSections.push_back(sec);
|
outputSections.push_back(sec);
|
||||||
}
|
}
|
||||||
return sec;
|
return sec;
|
||||||
};
|
};
|
||||||
|
@ -858,7 +864,7 @@ void Writer::createSections() {
|
||||||
dtorsSec = createSection(".dtors", data | r | w);
|
dtorsSec = createSection(".dtors", data | r | w);
|
||||||
|
|
||||||
// Then bin chunks by name and output characteristics.
|
// Then bin chunks by name and output characteristics.
|
||||||
for (Chunk *c : ctx.symtab.getChunks()) {
|
for (Chunk *c : symtab->getChunks()) {
|
||||||
auto *sc = dyn_cast<SectionChunk>(c);
|
auto *sc = dyn_cast<SectionChunk>(c);
|
||||||
if (sc && !sc->live) {
|
if (sc && !sc->live) {
|
||||||
if (config->verbose)
|
if (config->verbose)
|
||||||
|
@ -935,14 +941,14 @@ void Writer::createSections() {
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
llvm::stable_sort(ctx.outputSections,
|
llvm::stable_sort(outputSections,
|
||||||
[&](const OutputSection *s, const OutputSection *t) {
|
[&](const OutputSection *s, const OutputSection *t) {
|
||||||
return sectionOrder(s) < sectionOrder(t);
|
return sectionOrder(s) < sectionOrder(t);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Writer::createMiscChunks() {
|
void Writer::createMiscChunks() {
|
||||||
for (MergeChunk *p : ctx.mergeChunkInstances) {
|
for (MergeChunk *p : MergeChunk::instances) {
|
||||||
if (p) {
|
if (p) {
|
||||||
p->finalizeContents();
|
p->finalizeContents();
|
||||||
rdataSec->addChunk(p);
|
rdataSec->addChunk(p);
|
||||||
|
@ -950,16 +956,15 @@ void Writer::createMiscChunks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create thunks for locally-dllimported symbols.
|
// Create thunks for locally-dllimported symbols.
|
||||||
if (!ctx.symtab.localImportChunks.empty()) {
|
if (!symtab->localImportChunks.empty()) {
|
||||||
for (Chunk *c : ctx.symtab.localImportChunks)
|
for (Chunk *c : symtab->localImportChunks)
|
||||||
rdataSec->addChunk(c);
|
rdataSec->addChunk(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Debug Information Chunks
|
// Create Debug Information Chunks
|
||||||
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
|
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
|
||||||
if (config->debug || config->repro || config->cetCompat) {
|
if (config->debug || config->repro || config->cetCompat) {
|
||||||
debugDirectory =
|
debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
|
||||||
make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
|
|
||||||
debugDirectory->setAlignment(4);
|
debugDirectory->setAlignment(4);
|
||||||
debugInfoSec->addChunk(debugDirectory);
|
debugInfoSec->addChunk(debugDirectory);
|
||||||
}
|
}
|
||||||
|
@ -1008,7 +1013,7 @@ void Writer::createImportTables() {
|
||||||
// Initialize DLLOrder so that import entries are ordered in
|
// Initialize DLLOrder so that import entries are ordered in
|
||||||
// the same order as in the command line. (That affects DLL
|
// the same order as in the command line. (That affects DLL
|
||||||
// initialization order, and this ordering is MSVC-compatible.)
|
// initialization order, and this ordering is MSVC-compatible.)
|
||||||
for (ImportFile *file : ctx.importFileInstances) {
|
for (ImportFile *file : ImportFile::instances) {
|
||||||
if (!file->live)
|
if (!file->live)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1031,10 +1036,10 @@ void Writer::createImportTables() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Writer::appendImportThunks() {
|
void Writer::appendImportThunks() {
|
||||||
if (ctx.importFileInstances.empty())
|
if (ImportFile::instances.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (ImportFile *file : ctx.importFileInstances) {
|
for (ImportFile *file : ImportFile::instances) {
|
||||||
if (!file->live)
|
if (!file->live)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1050,7 +1055,7 @@ void Writer::appendImportThunks() {
|
||||||
|
|
||||||
if (!delayIdata.empty()) {
|
if (!delayIdata.empty()) {
|
||||||
Defined *helper = cast<Defined>(config->delayLoadHelper);
|
Defined *helper = cast<Defined>(config->delayLoadHelper);
|
||||||
delayIdata.create(ctx, helper);
|
delayIdata.create(helper);
|
||||||
for (Chunk *c : delayIdata.getChunks())
|
for (Chunk *c : delayIdata.getChunks())
|
||||||
didatSec->addChunk(c);
|
didatSec->addChunk(c);
|
||||||
for (Chunk *c : delayIdata.getDataChunks())
|
for (Chunk *c : delayIdata.getDataChunks())
|
||||||
|
@ -1090,25 +1095,25 @@ void Writer::removeUnusedSections() {
|
||||||
// later. Only remove sections that have no Chunks at all.
|
// later. Only remove sections that have no Chunks at all.
|
||||||
return s->chunks.empty();
|
return s->chunks.empty();
|
||||||
};
|
};
|
||||||
ctx.outputSections.erase(std::remove_if(ctx.outputSections.begin(),
|
outputSections.erase(
|
||||||
ctx.outputSections.end(), isUnused),
|
std::remove_if(outputSections.begin(), outputSections.end(), isUnused),
|
||||||
ctx.outputSections.end());
|
outputSections.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Windows loader doesn't seem to like empty sections,
|
// The Windows loader doesn't seem to like empty sections,
|
||||||
// so we remove them if any.
|
// so we remove them if any.
|
||||||
void Writer::removeEmptySections() {
|
void Writer::removeEmptySections() {
|
||||||
auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; };
|
auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; };
|
||||||
ctx.outputSections.erase(std::remove_if(ctx.outputSections.begin(),
|
outputSections.erase(
|
||||||
ctx.outputSections.end(), isEmpty),
|
std::remove_if(outputSections.begin(), outputSections.end(), isEmpty),
|
||||||
ctx.outputSections.end());
|
outputSections.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Writer::assignOutputSectionIndices() {
|
void Writer::assignOutputSectionIndices() {
|
||||||
// Assign final output section indices, and assign each chunk to its output
|
// Assign final output section indices, and assign each chunk to its output
|
||||||
// section.
|
// section.
|
||||||
uint32_t idx = 1;
|
uint32_t idx = 1;
|
||||||
for (OutputSection *os : ctx.outputSections) {
|
for (OutputSection *os : outputSections) {
|
||||||
os->sectionIndex = idx;
|
os->sectionIndex = idx;
|
||||||
for (Chunk *c : os->chunks)
|
for (Chunk *c : os->chunks)
|
||||||
c->setOutputSectionIdx(idx);
|
c->setOutputSectionIdx(idx);
|
||||||
|
@ -1117,7 +1122,7 @@ void Writer::assignOutputSectionIndices() {
|
||||||
|
|
||||||
// Merge chunks are containers of chunks, so assign those an output section
|
// Merge chunks are containers of chunks, so assign those an output section
|
||||||
// too.
|
// too.
|
||||||
for (MergeChunk *mc : ctx.mergeChunkInstances)
|
for (MergeChunk *mc : MergeChunk::instances)
|
||||||
if (mc)
|
if (mc)
|
||||||
for (SectionChunk *sc : mc->sections)
|
for (SectionChunk *sc : mc->sections)
|
||||||
if (sc && sc->live)
|
if (sc && sc->live)
|
||||||
|
@ -1148,7 +1153,7 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
|
||||||
Chunk *c = def->getChunk();
|
Chunk *c = def->getChunk();
|
||||||
if (!c)
|
if (!c)
|
||||||
return None;
|
return None;
|
||||||
OutputSection *os = ctx.getOutputSection(c);
|
OutputSection *os = c->getOutputSection();
|
||||||
if (!os)
|
if (!os)
|
||||||
return None;
|
return None;
|
||||||
|
|
||||||
|
@ -1195,7 +1200,7 @@ void Writer::createSymbolAndStringTable() {
|
||||||
// solution where discardable sections have long names preserved and
|
// solution where discardable sections have long names preserved and
|
||||||
// non-discardable sections have their names truncated, to ensure that any
|
// non-discardable sections have their names truncated, to ensure that any
|
||||||
// section which is mapped at runtime also has its name mapped at runtime.
|
// section which is mapped at runtime also has its name mapped at runtime.
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
if (sec->name.size() <= COFF::NameSize)
|
if (sec->name.size() <= COFF::NameSize)
|
||||||
continue;
|
continue;
|
||||||
if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
|
if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
|
||||||
|
@ -1209,7 +1214,7 @@ void Writer::createSymbolAndStringTable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->debugDwarf || config->debugSymtab) {
|
if (config->debugDwarf || config->debugSymtab) {
|
||||||
for (ObjFile *file : ctx.objFileInstances) {
|
for (ObjFile *file : ObjFile::instances) {
|
||||||
for (Symbol *b : file->getSymbols()) {
|
for (Symbol *b : file->getSymbols()) {
|
||||||
auto *d = dyn_cast_or_null<Defined>(b);
|
auto *d = dyn_cast_or_null<Defined>(b);
|
||||||
if (!d || d->writtenToSymtab)
|
if (!d || d->writtenToSymtab)
|
||||||
|
@ -1269,7 +1274,7 @@ void Writer::mergeSections() {
|
||||||
void Writer::assignAddresses() {
|
void Writer::assignAddresses() {
|
||||||
sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
|
sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
|
||||||
sizeof(data_directory) * numberOfDataDirectory +
|
sizeof(data_directory) * numberOfDataDirectory +
|
||||||
sizeof(coff_section) * ctx.outputSections.size();
|
sizeof(coff_section) * outputSections.size();
|
||||||
sizeOfHeaders +=
|
sizeOfHeaders +=
|
||||||
config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
|
config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
|
||||||
sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);
|
sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);
|
||||||
|
@ -1278,7 +1283,7 @@ void Writer::assignAddresses() {
|
||||||
// The first page is kept unmapped.
|
// The first page is kept unmapped.
|
||||||
uint64_t rva = alignTo(sizeOfHeaders, config->align);
|
uint64_t rva = alignTo(sizeOfHeaders, config->align);
|
||||||
|
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
if (sec == relocSec)
|
if (sec == relocSec)
|
||||||
addBaserels();
|
addBaserels();
|
||||||
uint64_t rawSize = 0, virtualSize = 0;
|
uint64_t rawSize = 0, virtualSize = 0;
|
||||||
|
@ -1313,7 +1318,7 @@ void Writer::assignAddresses() {
|
||||||
sizeOfImage = alignTo(rva, config->align);
|
sizeOfImage = alignTo(rva, config->align);
|
||||||
|
|
||||||
// Assign addresses to sections in MergeChunks.
|
// Assign addresses to sections in MergeChunks.
|
||||||
for (MergeChunk *mc : ctx.mergeChunkInstances)
|
for (MergeChunk *mc : MergeChunk::instances)
|
||||||
if (mc)
|
if (mc)
|
||||||
mc->assignSubsectionRVAs();
|
mc->assignSubsectionRVAs();
|
||||||
}
|
}
|
||||||
|
@ -1348,7 +1353,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||||
auto *coff = reinterpret_cast<coff_file_header *>(buf);
|
auto *coff = reinterpret_cast<coff_file_header *>(buf);
|
||||||
buf += sizeof(*coff);
|
buf += sizeof(*coff);
|
||||||
coff->Machine = config->machine;
|
coff->Machine = config->machine;
|
||||||
coff->NumberOfSections = ctx.outputSections.size();
|
coff->NumberOfSections = outputSections.size();
|
||||||
coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
|
coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
|
||||||
if (config->largeAddressAware)
|
if (config->largeAddressAware)
|
||||||
coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
|
coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
|
||||||
|
@ -1461,7 +1466,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||||
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
|
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
|
||||||
dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize();
|
dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize();
|
||||||
}
|
}
|
||||||
if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) {
|
if (Symbol *sym = symtab->findUnderscore("_tls_used")) {
|
||||||
if (Defined *b = dyn_cast<Defined>(sym)) {
|
if (Defined *b = dyn_cast<Defined>(sym)) {
|
||||||
dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
|
dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
|
||||||
dir[TLS_TABLE].Size = config->is64()
|
dir[TLS_TABLE].Size = config->is64()
|
||||||
|
@ -1473,7 +1478,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||||
dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();
|
dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();
|
||||||
dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();
|
dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();
|
||||||
}
|
}
|
||||||
if (Symbol *sym = ctx.symtab.findUnderscore("_load_config_used")) {
|
if (Symbol *sym = symtab->findUnderscore("_load_config_used")) {
|
||||||
if (auto *b = dyn_cast<DefinedRegular>(sym)) {
|
if (auto *b = dyn_cast<DefinedRegular>(sym)) {
|
||||||
SectionChunk *sc = b->getChunk();
|
SectionChunk *sc = b->getChunk();
|
||||||
assert(b->getRVA() >= sc->getRVA());
|
assert(b->getRVA() >= sc->getRVA());
|
||||||
|
@ -1497,12 +1502,12 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write section table
|
// Write section table
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
sec->writeHeaderTo(buf);
|
sec->writeHeaderTo(buf);
|
||||||
buf += sizeof(coff_section);
|
buf += sizeof(coff_section);
|
||||||
}
|
}
|
||||||
sectionTable = ArrayRef<uint8_t>(
|
sectionTable = ArrayRef<uint8_t>(
|
||||||
buf - ctx.outputSections.size() * sizeof(coff_section), buf);
|
buf - outputSections.size() * sizeof(coff_section), buf);
|
||||||
|
|
||||||
if (outputSymtab.empty() && strtab.empty())
|
if (outputSymtab.empty() && strtab.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -1530,7 +1535,7 @@ void Writer::openFile(StringRef path) {
|
||||||
|
|
||||||
void Writer::createSEHTable() {
|
void Writer::createSEHTable() {
|
||||||
SymbolRVASet handlers;
|
SymbolRVASet handlers;
|
||||||
for (ObjFile *file : ctx.objFileInstances) {
|
for (ObjFile *file : ObjFile::instances) {
|
||||||
if (!file->hasSafeSEH())
|
if (!file->hasSafeSEH())
|
||||||
error("/safeseh: " + file->getName() + " is not compatible with SEH");
|
error("/safeseh: " + file->getName() + " is not compatible with SEH");
|
||||||
markSymbolsForRVATable(file, file->getSXDataChunks(), handlers);
|
markSymbolsForRVATable(file, file->getSXDataChunks(), handlers);
|
||||||
|
@ -1539,7 +1544,7 @@ void Writer::createSEHTable() {
|
||||||
// Set the "no SEH" characteristic if there really were no handlers, or if
|
// Set the "no SEH" characteristic if there really were no handlers, or if
|
||||||
// there is no load config object to point to the table of handlers.
|
// there is no load config object to point to the table of handlers.
|
||||||
setNoSEHCharacteristic =
|
setNoSEHCharacteristic =
|
||||||
handlers.empty() || !ctx.symtab.findUnderscore("_load_config_used");
|
handlers.empty() || !symtab->findUnderscore("_load_config_used");
|
||||||
|
|
||||||
maybeAddRVATable(std::move(handlers), "__safe_se_handler_table",
|
maybeAddRVATable(std::move(handlers), "__safe_se_handler_table",
|
||||||
"__safe_se_handler_count");
|
"__safe_se_handler_count");
|
||||||
|
@ -1637,7 +1642,7 @@ void Writer::createGuardCFTables() {
|
||||||
std::vector<Symbol *> giatsSymbols;
|
std::vector<Symbol *> giatsSymbols;
|
||||||
SymbolRVASet longJmpTargets;
|
SymbolRVASet longJmpTargets;
|
||||||
SymbolRVASet ehContTargets;
|
SymbolRVASet ehContTargets;
|
||||||
for (ObjFile *file : ctx.objFileInstances) {
|
for (ObjFile *file : ObjFile::instances) {
|
||||||
// If the object was compiled with /guard:cf, the address taken symbols
|
// If the object was compiled with /guard:cf, the address taken symbols
|
||||||
// are in .gfids$y sections, the longjmp targets are in .gljmp$y sections,
|
// are in .gfids$y sections, the longjmp targets are in .gljmp$y sections,
|
||||||
// and ehcont targets are in .gehcont$y sections. If the object was not
|
// and ehcont targets are in .gehcont$y sections. If the object was not
|
||||||
|
@ -1703,7 +1708,7 @@ void Writer::createGuardCFTables() {
|
||||||
guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable);
|
guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable);
|
||||||
if (config->guardCF & GuardCFLevel::EHCont)
|
if (config->guardCF & GuardCFLevel::EHCont)
|
||||||
guardFlags |= uint32_t(coff_guard_flags::HasEHContTable);
|
guardFlags |= uint32_t(coff_guard_flags::HasEHContTable);
|
||||||
Symbol *flagSym = ctx.symtab.findUnderscore("__guard_flags");
|
Symbol *flagSym = symtab->findUnderscore("__guard_flags");
|
||||||
cast<DefinedAbsolute>(flagSym)->setVA(guardFlags);
|
cast<DefinedAbsolute>(flagSym)->setVA(guardFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1775,8 +1780,8 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
|
||||||
tableChunk = make<RVATableChunk>(std::move(tableSymbols));
|
tableChunk = make<RVATableChunk>(std::move(tableSymbols));
|
||||||
rdataSec->addChunk(tableChunk);
|
rdataSec->addChunk(tableChunk);
|
||||||
|
|
||||||
Symbol *t = ctx.symtab.findUnderscore(tableSym);
|
Symbol *t = symtab->findUnderscore(tableSym);
|
||||||
Symbol *c = ctx.symtab.findUnderscore(countSym);
|
Symbol *c = symtab->findUnderscore(countSym);
|
||||||
replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk);
|
replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk);
|
||||||
cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
|
cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
|
||||||
}
|
}
|
||||||
|
@ -1788,7 +1793,7 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
|
||||||
void Writer::createRuntimePseudoRelocs() {
|
void Writer::createRuntimePseudoRelocs() {
|
||||||
std::vector<RuntimePseudoReloc> rels;
|
std::vector<RuntimePseudoReloc> rels;
|
||||||
|
|
||||||
for (Chunk *c : ctx.symtab.getChunks()) {
|
for (Chunk *c : symtab->getChunks()) {
|
||||||
auto *sc = dyn_cast<SectionChunk>(c);
|
auto *sc = dyn_cast<SectionChunk>(c);
|
||||||
if (!sc || !sc->live)
|
if (!sc || !sc->live)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1811,9 +1816,8 @@ void Writer::createRuntimePseudoRelocs() {
|
||||||
EmptyChunk *endOfList = make<EmptyChunk>();
|
EmptyChunk *endOfList = make<EmptyChunk>();
|
||||||
rdataSec->addChunk(endOfList);
|
rdataSec->addChunk(endOfList);
|
||||||
|
|
||||||
Symbol *headSym = ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
|
Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
|
||||||
Symbol *endSym =
|
Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
|
||||||
ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
|
|
||||||
replaceSymbol<DefinedSynthetic>(headSym, headSym->getName(), table);
|
replaceSymbol<DefinedSynthetic>(headSym, headSym->getName(), table);
|
||||||
replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList);
|
replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList);
|
||||||
}
|
}
|
||||||
|
@ -1833,8 +1837,8 @@ void Writer::insertCtorDtorSymbols() {
|
||||||
dtorsSec->insertChunkAtStart(dtorListHead);
|
dtorsSec->insertChunkAtStart(dtorListHead);
|
||||||
dtorsSec->addChunk(dtorListEnd);
|
dtorsSec->addChunk(dtorListEnd);
|
||||||
|
|
||||||
Symbol *ctorListSym = ctx.symtab.findUnderscore("__CTOR_LIST__");
|
Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__");
|
||||||
Symbol *dtorListSym = ctx.symtab.findUnderscore("__DTOR_LIST__");
|
Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__");
|
||||||
replaceSymbol<DefinedSynthetic>(ctorListSym, ctorListSym->getName(),
|
replaceSymbol<DefinedSynthetic>(ctorListSym, ctorListSym->getName(),
|
||||||
ctorListHead);
|
ctorListHead);
|
||||||
replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(),
|
replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(),
|
||||||
|
@ -1847,7 +1851,7 @@ void Writer::setSectionPermissions() {
|
||||||
for (auto &p : config->section) {
|
for (auto &p : config->section) {
|
||||||
StringRef name = p.first;
|
StringRef name = p.first;
|
||||||
uint32_t perm = p.second;
|
uint32_t perm = p.second;
|
||||||
for (OutputSection *sec : ctx.outputSections)
|
for (OutputSection *sec : outputSections)
|
||||||
if (sec->name == name)
|
if (sec->name == name)
|
||||||
sec->setPermissions(perm);
|
sec->setPermissions(perm);
|
||||||
}
|
}
|
||||||
|
@ -1857,10 +1861,10 @@ void Writer::setSectionPermissions() {
|
||||||
void Writer::writeSections() {
|
void Writer::writeSections() {
|
||||||
// Record the number of sections to apply section index relocations
|
// Record the number of sections to apply section index relocations
|
||||||
// against absolute symbols. See applySecIdx in Chunks.cpp..
|
// against absolute symbols. See applySecIdx in Chunks.cpp..
|
||||||
DefinedAbsolute::numOutputSections = ctx.outputSections.size();
|
DefinedAbsolute::numOutputSections = outputSections.size();
|
||||||
|
|
||||||
uint8_t *buf = buffer->getBufferStart();
|
uint8_t *buf = buffer->getBufferStart();
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
uint8_t *secBuf = buf + sec->getFileOff();
|
uint8_t *secBuf = buf + sec->getFileOff();
|
||||||
// Fill gaps between functions in .text with INT3 instructions
|
// Fill gaps between functions in .text with INT3 instructions
|
||||||
// instead of leaving as NUL bytes (which can be interpreted as
|
// instead of leaving as NUL bytes (which can be interpreted as
|
||||||
|
@ -1930,7 +1934,7 @@ void Writer::sortExceptionTable() {
|
||||||
return;
|
return;
|
||||||
// We assume .pdata contains function table entries only.
|
// We assume .pdata contains function table entries only.
|
||||||
auto bufAddr = [&](Chunk *c) {
|
auto bufAddr = [&](Chunk *c) {
|
||||||
OutputSection *os = ctx.getOutputSection(c);
|
OutputSection *os = c->getOutputSection();
|
||||||
return buffer->getBufferStart() + os->getFileOff() + c->getRVA() -
|
return buffer->getBufferStart() + os->getFileOff() + c->getRVA() -
|
||||||
os->getRVA();
|
os->getRVA();
|
||||||
};
|
};
|
||||||
|
@ -1998,7 +2002,7 @@ void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputSection *Writer::findSection(StringRef name) {
|
OutputSection *Writer::findSection(StringRef name) {
|
||||||
for (OutputSection *sec : ctx.outputSections)
|
for (OutputSection *sec : outputSections)
|
||||||
if (sec->name == name)
|
if (sec->name == name)
|
||||||
return sec;
|
return sec;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2006,7 +2010,7 @@ OutputSection *Writer::findSection(StringRef name) {
|
||||||
|
|
||||||
uint32_t Writer::getSizeOfInitializedData() {
|
uint32_t Writer::getSizeOfInitializedData() {
|
||||||
uint32_t res = 0;
|
uint32_t res = 0;
|
||||||
for (OutputSection *s : ctx.outputSections)
|
for (OutputSection *s : outputSections)
|
||||||
if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
|
if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
|
||||||
res += s->getRawSize();
|
res += s->getRawSize();
|
||||||
return res;
|
return res;
|
||||||
|
@ -2018,7 +2022,7 @@ void Writer::addBaserels() {
|
||||||
return;
|
return;
|
||||||
relocSec->chunks.clear();
|
relocSec->chunks.clear();
|
||||||
std::vector<Baserel> v;
|
std::vector<Baserel> v;
|
||||||
for (OutputSection *sec : ctx.outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
|
if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
|
||||||
continue;
|
continue;
|
||||||
// Collect all locations for base relocations.
|
// Collect all locations for base relocations.
|
||||||
|
@ -2067,11 +2071,11 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
|
||||||
|
|
||||||
void Writer::fixTlsAlignment() {
|
void Writer::fixTlsAlignment() {
|
||||||
Defined *tlsSym =
|
Defined *tlsSym =
|
||||||
dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore("_tls_used"));
|
dyn_cast_or_null<Defined>(symtab->findUnderscore("_tls_used"));
|
||||||
if (!tlsSym)
|
if (!tlsSym)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OutputSection *sec = ctx.getOutputSection(tlsSym->getChunk());
|
OutputSection *sec = tlsSym->getChunk()->getOutputSection();
|
||||||
assert(sec && tlsSym->getRVA() >= sec->getRVA() &&
|
assert(sec && tlsSym->getRVA() >= sec->getRVA() &&
|
||||||
"no output section for _tls_used");
|
"no output section for _tls_used");
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,8 @@
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace coff {
|
namespace coff {
|
||||||
static const int pageSize = 4096;
|
static const int pageSize = 4096;
|
||||||
class COFFLinkerContext;
|
|
||||||
|
|
||||||
void writeResult(COFFLinkerContext &ctx);
|
void writeResult();
|
||||||
|
|
||||||
class PartialSection {
|
class PartialSection {
|
||||||
public:
|
public:
|
||||||
|
@ -51,6 +50,9 @@ public:
|
||||||
void writeHeaderTo(uint8_t *buf);
|
void writeHeaderTo(uint8_t *buf);
|
||||||
void addContributingPartialSection(PartialSection *sec);
|
void addContributingPartialSection(PartialSection *sec);
|
||||||
|
|
||||||
|
// Clear the output sections static container.
|
||||||
|
static void clear();
|
||||||
|
|
||||||
// Returns the size of this section in an executable memory image.
|
// Returns the size of this section in an executable memory image.
|
||||||
// This may be smaller than the raw size (the raw size is multiple
|
// This may be smaller than the raw size (the raw size is multiple
|
||||||
// of disk sector size, so there may be padding at end), or may be
|
// of disk sector size, so there may be padding at end), or may be
|
||||||
|
|
|
@ -31,8 +31,13 @@ Timer::Timer(llvm::StringRef name, Timer &parent) : name(std::string(name)) {
|
||||||
parent.children.push_back(this);
|
parent.children.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer &Timer::root() {
|
||||||
|
static Timer rootTimer("Total Link Time");
|
||||||
|
return rootTimer;
|
||||||
|
}
|
||||||
|
|
||||||
void Timer::print() {
|
void Timer::print() {
|
||||||
double totalDuration = static_cast<double>(millis());
|
double totalDuration = static_cast<double>(root().millis());
|
||||||
|
|
||||||
// We want to print the grand total under all the intermediate phases, so we
|
// We want to print the grand total under all the intermediate phases, so we
|
||||||
// print all children first, then print the total under that.
|
// print all children first, then print the total under that.
|
||||||
|
@ -42,7 +47,7 @@ void Timer::print() {
|
||||||
|
|
||||||
message(std::string(50, '-'));
|
message(std::string(50, '-'));
|
||||||
|
|
||||||
print(0, millis(), false);
|
root().print(0, root().millis(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
double Timer::millis() const {
|
double Timer::millis() const {
|
||||||
|
|
|
@ -38,8 +38,7 @@ class Timer {
|
||||||
public:
|
public:
|
||||||
Timer(llvm::StringRef name, Timer &parent);
|
Timer(llvm::StringRef name, Timer &parent);
|
||||||
|
|
||||||
// Creates the root timer.
|
static Timer &root();
|
||||||
explicit Timer(llvm::StringRef name);
|
|
||||||
|
|
||||||
void addToTotal(std::chrono::nanoseconds time) { total += time.count(); }
|
void addToTotal(std::chrono::nanoseconds time) { total += time.count(); }
|
||||||
void print();
|
void print();
|
||||||
|
@ -47,6 +46,7 @@ public:
|
||||||
double millis() const;
|
double millis() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
explicit Timer(llvm::StringRef name);
|
||||||
void print(int depth, double totalDuration, bool recurse = true) const;
|
void print(int depth, double totalDuration, bool recurse = true) const;
|
||||||
|
|
||||||
std::atomic<std::chrono::nanoseconds::rep> total;
|
std::atomic<std::chrono::nanoseconds::rep> total;
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
; REQUIRES: x86
|
|
||||||
; RUN: llvm-as %s -o %t.obj
|
|
||||||
|
|
||||||
; Test different configurations of lld to get all possible timer outputs.
|
|
||||||
; RUN: lld-link %t.obj -time -entry:main -debug:noghash 2>&1 | \
|
|
||||||
; RUN: FileCheck %s --check-prefix=CHECK1
|
|
||||||
; RUN: lld-link %t.obj -time -entry:main -debug 2>&1 | \
|
|
||||||
; RUN: FileCheck %s --check-prefix=CHECK2
|
|
||||||
; RUN: lld-link %t.obj -time -entry:main -map 2>&1 | \
|
|
||||||
; RUN: FileCheck %s --check-prefix=CHECK3
|
|
||||||
|
|
||||||
; CHECK1: Input File Reading:
|
|
||||||
; CHECK1: LTO:
|
|
||||||
; CHECK1: Code Layout:
|
|
||||||
; CHECK1: Commit Output File:
|
|
||||||
; CHECK1: PDB Emission (Cumulative):
|
|
||||||
; CHECK1: Add Objects:
|
|
||||||
; CHECK1: Type Merging:
|
|
||||||
; CHECK1: Symbol Merging:
|
|
||||||
; CHECK1: Publics Stream Layout:
|
|
||||||
; CHECK1: TPI Stream Layout:
|
|
||||||
; CHECK1: Commit to Disk:
|
|
||||||
|
|
||||||
; CHECK2: Input File Reading:
|
|
||||||
; CHECK2: LTO:
|
|
||||||
; CHECK2: Code Layout:
|
|
||||||
; CHECK2: Commit Output File:
|
|
||||||
; CHECK2: PDB Emission (Cumulative):
|
|
||||||
; CHECK2: Add Objects:
|
|
||||||
; CHECK2: Global Type Hashing:
|
|
||||||
; CHECK2: GHash Type Merging:
|
|
||||||
; CHECK2: Symbol Merging:
|
|
||||||
; CHECK2: Publics Stream Layout:
|
|
||||||
; CHECK2: TPI Stream Layout:
|
|
||||||
; CHECK2: Commit to Disk:
|
|
||||||
|
|
||||||
; CHECK3: Input File Reading:
|
|
||||||
; CHECK3: LTO:
|
|
||||||
; CHECK3: GC:
|
|
||||||
; CHECK3: ICF:
|
|
||||||
; CHECK3: Code Layout:
|
|
||||||
; CHECK3: Commit Output File:
|
|
||||||
; CHECK3: MAP Emission (Cumulative):
|
|
||||||
; CHECK3: Gather Symbols:
|
|
||||||
; CHECK3: Build Symbol Strings:
|
|
||||||
; CHECK3: Write to File:
|
|
||||||
|
|
||||||
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
|
||||||
target triple = "x86_64-pc-windows-msvc19.11.0"
|
|
||||||
|
|
||||||
define dso_local i32 @main() {
|
|
||||||
entry:
|
|
||||||
ret i32 0
|
|
||||||
}
|
|
||||||
|
|
||||||
!llvm.dbg.cu = !{!0}
|
|
||||||
!llvm.module.flags = !{!2, !3}
|
|
||||||
|
|
||||||
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
|
|
||||||
!1 = !DIFile(filename: "t.cpp", directory: "", checksumkind: CSK_MD5, checksum: "495fd79f78a98304e065540d576057d9")
|
|
||||||
!2 = !{i32 2, !"CodeView", i32 1}
|
|
||||||
!3 = !{i32 2, !"Debug Info Version", i32 3}
|
|
|
@ -29,7 +29,6 @@ static_library("COFF") {
|
||||||
sources = [
|
sources = [
|
||||||
"CallGraphSort.cpp",
|
"CallGraphSort.cpp",
|
||||||
"Chunks.cpp",
|
"Chunks.cpp",
|
||||||
"COFFLinkerContext.cpp",
|
|
||||||
"DLL.cpp",
|
"DLL.cpp",
|
||||||
"DebugTypes.cpp",
|
"DebugTypes.cpp",
|
||||||
"Driver.cpp",
|
"Driver.cpp",
|
||||||
|
|
Loading…
Reference in New Issue