aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lld
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lld')
-rw-r--r--contrib/llvm-project/lld/.arcconfig4
-rw-r--r--contrib/llvm-project/lld/.clang-tidy19
-rw-r--r--contrib/llvm-project/lld/COFF/Chunks.cpp2
-rw-r--r--contrib/llvm-project/lld/COFF/Chunks.h3
-rw-r--r--contrib/llvm-project/lld/COFF/Config.h10
-rw-r--r--contrib/llvm-project/lld/COFF/DebugTypes.cpp538
-rw-r--r--contrib/llvm-project/lld/COFF/DebugTypes.h56
-rw-r--r--contrib/llvm-project/lld/COFF/Driver.cpp125
-rw-r--r--contrib/llvm-project/lld/COFF/Driver.h22
-rw-r--r--contrib/llvm-project/lld/COFF/DriverUtils.cpp67
-rw-r--r--contrib/llvm-project/lld/COFF/ICF.cpp21
-rw-r--r--contrib/llvm-project/lld/COFF/InputFiles.cpp184
-rw-r--r--contrib/llvm-project/lld/COFF/InputFiles.h46
-rw-r--r--contrib/llvm-project/lld/COFF/LLDMapFile.cpp123
-rw-r--r--contrib/llvm-project/lld/COFF/LLDMapFile.h21
-rw-r--r--contrib/llvm-project/lld/COFF/LTO.cpp61
-rw-r--r--contrib/llvm-project/lld/COFF/LTO.h2
-rw-r--r--contrib/llvm-project/lld/COFF/MapFile.cpp323
-rw-r--r--contrib/llvm-project/lld/COFF/MinGW.cpp19
-rw-r--r--contrib/llvm-project/lld/COFF/Options.td24
-rw-r--r--contrib/llvm-project/lld/COFF/PDB.cpp785
-rw-r--r--contrib/llvm-project/lld/COFF/SymbolTable.cpp78
-rw-r--r--contrib/llvm-project/lld/COFF/SymbolTable.h1
-rw-r--r--contrib/llvm-project/lld/COFF/Symbols.cpp32
-rw-r--r--contrib/llvm-project/lld/COFF/Symbols.h16
-rw-r--r--contrib/llvm-project/lld/COFF/TypeMerger.h13
-rw-r--r--contrib/llvm-project/lld/COFF/Writer.cpp83
-rw-r--r--contrib/llvm-project/lld/Common/ErrorHandler.cpp41
-rw-r--r--contrib/llvm-project/lld/Common/Filesystem.cpp36
-rw-r--r--contrib/llvm-project/lld/Common/Reproduce.cpp6
-rw-r--r--contrib/llvm-project/lld/Common/Strings.cpp31
-rw-r--r--contrib/llvm-project/lld/Common/TargetOptionsCommandFlags.cpp25
-rw-r--r--contrib/llvm-project/lld/Common/Timer.cpp29
-rw-r--r--contrib/llvm-project/lld/Common/Version.cpp11
-rw-r--r--contrib/llvm-project/lld/ELF/AArch64ErrataFix.cpp11
-rw-r--r--contrib/llvm-project/lld/ELF/ARMErrataFix.cpp12
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/AArch64.cpp145
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/AMDGPU.cpp17
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/ARM.cpp318
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/AVR.cpp142
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/Hexagon.cpp66
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/MSP430.cpp28
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/Mips.cpp84
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp26
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/PPC.cpp67
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/PPC64.cpp265
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/RISCV.cpp45
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/SPARCV9.cpp83
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/X86.cpp55
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/X86_64.cpp386
-rw-r--r--contrib/llvm-project/lld/ELF/CallGraphSort.cpp12
-rw-r--r--contrib/llvm-project/lld/ELF/Config.h24
-rw-r--r--contrib/llvm-project/lld/ELF/DWARF.cpp24
-rw-r--r--contrib/llvm-project/lld/ELF/DWARF.h9
-rw-r--r--contrib/llvm-project/lld/ELF/Driver.cpp369
-rw-r--r--contrib/llvm-project/lld/ELF/DriverUtils.cpp29
-rw-r--r--contrib/llvm-project/lld/ELF/EhFrame.cpp11
-rw-r--r--contrib/llvm-project/lld/ELF/ICF.cpp30
-rw-r--r--contrib/llvm-project/lld/ELF/InputFiles.cpp326
-rw-r--r--contrib/llvm-project/lld/ELF/InputFiles.h30
-rw-r--r--contrib/llvm-project/lld/ELF/InputSection.cpp150
-rw-r--r--contrib/llvm-project/lld/ELF/InputSection.h38
-rw-r--r--contrib/llvm-project/lld/ELF/LTO.cpp103
-rw-r--r--contrib/llvm-project/lld/ELF/LinkerScript.cpp170
-rw-r--r--contrib/llvm-project/lld/ELF/LinkerScript.h46
-rw-r--r--contrib/llvm-project/lld/ELF/MapFile.cpp29
-rw-r--r--contrib/llvm-project/lld/ELF/MapFile.h1
-rw-r--r--contrib/llvm-project/lld/ELF/MarkLive.cpp24
-rw-r--r--contrib/llvm-project/lld/ELF/Options.td198
-rw-r--r--contrib/llvm-project/lld/ELF/OutputSections.cpp53
-rw-r--r--contrib/llvm-project/lld/ELF/OutputSections.h3
-rw-r--r--contrib/llvm-project/lld/ELF/Relocations.cpp158
-rw-r--r--contrib/llvm-project/lld/ELF/Relocations.h14
-rw-r--r--contrib/llvm-project/lld/ELF/ScriptLexer.cpp11
-rw-r--r--contrib/llvm-project/lld/ELF/ScriptLexer.h5
-rw-r--r--contrib/llvm-project/lld/ELF/ScriptParser.cpp241
-rw-r--r--contrib/llvm-project/lld/ELF/ScriptParser.h2
-rw-r--r--contrib/llvm-project/lld/ELF/SymbolTable.cpp52
-rw-r--r--contrib/llvm-project/lld/ELF/Symbols.cpp84
-rw-r--r--contrib/llvm-project/lld/ELF/Symbols.h27
-rw-r--r--contrib/llvm-project/lld/ELF/SyntheticSections.cpp268
-rw-r--r--contrib/llvm-project/lld/ELF/SyntheticSections.h4
-rw-r--r--contrib/llvm-project/lld/ELF/Target.cpp30
-rw-r--r--contrib/llvm-project/lld/ELF/Target.h74
-rw-r--r--contrib/llvm-project/lld/ELF/Thunks.cpp108
-rw-r--r--contrib/llvm-project/lld/ELF/Writer.cpp412
-rw-r--r--contrib/llvm-project/lld/MachO/Arch/X86_64.cpp286
-rw-r--r--contrib/llvm-project/lld/MachO/Config.h57
-rw-r--r--contrib/llvm-project/lld/MachO/Driver.cpp451
-rw-r--r--contrib/llvm-project/lld/MachO/Driver.h36
-rw-r--r--contrib/llvm-project/lld/MachO/ExportTrie.cpp283
-rw-r--r--contrib/llvm-project/lld/MachO/ExportTrie.h47
-rw-r--r--contrib/llvm-project/lld/MachO/InputFiles.cpp433
-rw-r--r--contrib/llvm-project/lld/MachO/InputFiles.h121
-rw-r--r--contrib/llvm-project/lld/MachO/InputSection.cpp48
-rw-r--r--contrib/llvm-project/lld/MachO/InputSection.h74
-rw-r--r--contrib/llvm-project/lld/MachO/MachOStructs.h36
-rw-r--r--contrib/llvm-project/lld/MachO/MergedOutputSection.cpp74
-rw-r--r--contrib/llvm-project/lld/MachO/MergedOutputSection.h56
-rw-r--r--contrib/llvm-project/lld/MachO/Options.td1297
-rw-r--r--contrib/llvm-project/lld/MachO/OutputSection.cpp18
-rw-r--r--contrib/llvm-project/lld/MachO/OutputSection.h74
-rw-r--r--contrib/llvm-project/lld/MachO/OutputSegment.cpp67
-rw-r--r--contrib/llvm-project/lld/MachO/OutputSegment.h62
-rw-r--r--contrib/llvm-project/lld/MachO/SymbolTable.cpp87
-rw-r--r--contrib/llvm-project/lld/MachO/SymbolTable.h50
-rw-r--r--contrib/llvm-project/lld/MachO/Symbols.cpp23
-rw-r--r--contrib/llvm-project/lld/MachO/Symbols.h138
-rw-r--r--contrib/llvm-project/lld/MachO/SyntheticSections.cpp409
-rw-r--r--contrib/llvm-project/lld/MachO/SyntheticSections.h290
-rw-r--r--contrib/llvm-project/lld/MachO/Target.cpp (renamed from contrib/llvm-project/lld/Common/Threads.cpp)9
-rw-r--r--contrib/llvm-project/lld/MachO/Target.h75
-rw-r--r--contrib/llvm-project/lld/MachO/Writer.cpp542
-rw-r--r--contrib/llvm-project/lld/MachO/Writer.h31
-rw-r--r--contrib/llvm-project/lld/docs/AtomLLD.rst2
-rw-r--r--contrib/llvm-project/lld/docs/ELF/linker_script.rst77
-rw-r--r--contrib/llvm-project/lld/docs/NewLLD.rst2
-rw-r--r--contrib/llvm-project/lld/docs/ReleaseNotes.rst116
-rw-r--r--contrib/llvm-project/lld/docs/WebAssembly.rst15
-rw-r--r--contrib/llvm-project/lld/docs/conf.py4
-rw-r--r--contrib/llvm-project/lld/docs/design.rst2
-rw-r--r--contrib/llvm-project/lld/docs/development.rst2
-rw-r--r--contrib/llvm-project/lld/docs/getting_started.rst4
-rw-r--r--contrib/llvm-project/lld/docs/index.rst11
-rw-r--r--contrib/llvm-project/lld/docs/ld.lld.160
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/DWARF.h2
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/Driver.h5
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/Memory.h13
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/Strings.h46
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/Threads.h92
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/Timer.h10
-rw-r--r--contrib/llvm-project/lld/include/lld/Common/Version.inc.in5
-rw-r--r--contrib/llvm-project/lld/include/lld/Core/File.h2
-rw-r--r--contrib/llvm-project/lld/include/lld/Core/Reference.h2
-rw-r--r--contrib/llvm-project/lld/include/lld/ReaderWriter/MachOLinkingContext.h6
-rw-r--r--contrib/llvm-project/lld/lib/Core/Error.cpp5
-rw-r--r--contrib/llvm-project/lld/lib/Core/Resolver.cpp2
-rw-r--r--contrib/llvm-project/lld/lib/Driver/DarwinLdDriver.cpp20
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp2
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/Atoms.h4
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp4
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/File.h72
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/GOTPass.cpp4
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/LayoutPass.cpp9
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp20
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp21
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp12
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp26
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/MachO/TLVPass.cpp4
-rw-r--r--contrib/llvm-project/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp6
-rw-r--r--contrib/llvm-project/lld/tools/lld/lld.cpp23
151 files changed, 10799 insertions, 2863 deletions
diff --git a/contrib/llvm-project/lld/.arcconfig b/contrib/llvm-project/lld/.arcconfig
deleted file mode 100644
index c8a8e079023f..000000000000
--- a/contrib/llvm-project/lld/.arcconfig
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "repository.callsign" : "LLD",
- "conduit_uri" : "https://reviews.llvm.org/"
-}
diff --git a/contrib/llvm-project/lld/.clang-tidy b/contrib/llvm-project/lld/.clang-tidy
new file mode 100644
index 000000000000..87ec2ff53af6
--- /dev/null
+++ b/contrib/llvm-project/lld/.clang-tidy
@@ -0,0 +1,19 @@
+# Almost identical to the top-level .clang-tidy, except that {Member,Parameter,Variable}Case use camelBack.
+Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming'
+CheckOptions:
+ - key: readability-identifier-naming.ClassCase
+ value: CamelCase
+ - key: readability-identifier-naming.EnumCase
+ value: CamelCase
+ - key: readability-identifier-naming.FunctionCase
+ value: camelBack
+ - key: readability-identifier-naming.MemberCase
+ value: camelBack
+ - key: readability-identifier-naming.ParameterCase
+ value: camelBack
+ - key: readability-identifier-naming.UnionCase
+ value: CamelCase
+ - key: readability-identifier-naming.VariableCase
+ value: camelBack
+ - key: readability-identifier-naming.IgnoreMainLikeFunctions
+ value: 1
diff --git a/contrib/llvm-project/lld/COFF/Chunks.cpp b/contrib/llvm-project/lld/COFF/Chunks.cpp
index 0e43d2b478b4..e04ceed505c2 100644
--- a/contrib/llvm-project/lld/COFF/Chunks.cpp
+++ b/contrib/llvm-project/lld/COFF/Chunks.cpp
@@ -333,7 +333,7 @@ static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
} else {
COFFSymbolRef coffSym =
check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex));
- file->getCOFFObj()->getSymbolName(coffSym, name);
+ name = check(file->getCOFFObj()->getSymbolName(coffSym));
}
std::vector<std::string> symbolLocations =
diff --git a/contrib/llvm-project/lld/COFF/Chunks.h b/contrib/llvm-project/lld/COFF/Chunks.h
index 2be2a72c4a1e..0528143383c5 100644
--- a/contrib/llvm-project/lld/COFF/Chunks.h
+++ b/contrib/llvm-project/lld/COFF/Chunks.h
@@ -269,7 +269,8 @@ public:
AssociatedIterator() = default;
AssociatedIterator(SectionChunk *head) : cur(head) {}
bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }
- const SectionChunk &operator*() const { return *cur; }
+ // FIXME: Wrong const-ness, but it makes filter ranges work.
+ SectionChunk &operator*() const { return *cur; }
SectionChunk &operator*() { return *cur; }
AssociatedIterator &operator++() {
cur = cur->assocChildren;
diff --git a/contrib/llvm-project/lld/COFF/Config.h b/contrib/llvm-project/lld/COFF/Config.h
index 2690ea5c4082..7c439176f3a4 100644
--- a/contrib/llvm-project/lld/COFF/Config.h
+++ b/contrib/llvm-project/lld/COFF/Config.h
@@ -110,6 +110,7 @@ struct Configuration {
bool showSummary = false;
unsigned debugTypes = static_cast<unsigned>(DebugType::None);
std::vector<std::string> natvisFiles;
+ llvm::StringMap<std::string> namedStreams;
llvm::SmallString<128> pdbAltPath;
llvm::SmallString<128> pdbPath;
llvm::SmallString<128> pdbSourcePath;
@@ -139,12 +140,13 @@ struct Configuration {
bool safeSEH = false;
Symbol *sehTable = nullptr;
Symbol *sehCount = nullptr;
+ bool noSEH = false;
// Used for /opt:lldlto=N
unsigned ltoo = 2;
// Used for /opt:lldltojobs=N
- unsigned thinLTOJobs = 0;
+ std::string thinLTOJobs;
// Used for /opt:lldltopartitions=N
unsigned ltoPartitions = 1;
@@ -182,6 +184,9 @@ struct Configuration {
llvm::StringMap<int> order;
// Used for /lldmap.
+ std::string lldmapFile;
+
+ // Used for /map.
std::string mapFile;
// Used for /thinlto-index-only:
@@ -211,6 +216,7 @@ struct Configuration {
uint32_t functionPadMin = 0;
bool dynamicBase = true;
bool allowBind = true;
+ bool cetCompat = false;
bool nxCompat = true;
bool allowIsolation = true;
bool terminalServerAware = true;
@@ -230,6 +236,8 @@ struct Configuration {
bool swaprunNet = false;
bool thinLTOEmitImportsFiles;
bool thinLTOIndexOnly;
+ bool autoImport = false;
+ bool pseudoRelocs = false;
};
extern Configuration *config;
diff --git a/contrib/llvm-project/lld/COFF/DebugTypes.cpp b/contrib/llvm-project/lld/COFF/DebugTypes.cpp
index 0960f16b01e6..4790b0166799 100644
--- a/contrib/llvm-project/lld/COFF/DebugTypes.cpp
+++ b/contrib/llvm-project/lld/COFF/DebugTypes.cpp
@@ -7,22 +7,26 @@
//===----------------------------------------------------------------------===//
#include "DebugTypes.h"
+#include "Chunks.h"
#include "Driver.h"
#include "InputFiles.h"
+#include "TypeMerger.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::codeview;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
namespace {
// The TypeServerSource class represents a PDB type server, a file referenced by
@@ -34,36 +38,40 @@ namespace {
// before any dependent OBJ.
class TypeServerSource : public TpiSource {
public:
- explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s)
- : TpiSource(PDB, nullptr), session(s), mb(m) {}
-
- // Queue a PDB type server for loading in the COFF Driver
- static void enqueue(const ObjFile *dependentFile,
- const TypeServer2Record &ts);
-
- // Create an instance
- static Expected<TypeServerSource *> getInstance(MemoryBufferRef m);
-
- // Fetch the PDB instance loaded for a corresponding dependent OBJ.
- static Expected<TypeServerSource *>
- findFromFile(const ObjFile *dependentFile);
-
- static std::map<std::string, std::pair<std::string, TypeServerSource *>>
- instances;
-
- // The interface to the PDB (if it was opened successfully)
- std::unique_ptr<llvm::pdb::NativeSession> session;
-
-private:
- MemoryBufferRef mb;
+ explicit TypeServerSource(PDBInputFile *f)
+ : TpiSource(PDB, nullptr), pdbInputFile(f) {
+ if (f->loadErr && *f->loadErr)
+ return;
+ pdb::PDBFile &file = f->session->getPDBFile();
+ auto expectedInfo = file.getPDBInfoStream();
+ if (!expectedInfo)
+ return;
+ auto it = mappings.emplace(expectedInfo->getGuid(), this);
+ assert(it.second);
+ (void)it;
+ tsIndexMap.isTypeServerMap = true;
+ }
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
+ bool isDependency() const override { return true; }
+
+ PDBInputFile *pdbInputFile = nullptr;
+
+ CVIndexMap tsIndexMap;
+
+ static std::map<codeview::GUID, TypeServerSource *> mappings;
};
// This class represents the debug type stream of an OBJ file that depends on a
// PDB type server (see TypeServerSource).
class UseTypeServerSource : public TpiSource {
public:
- UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts)
- : TpiSource(UsingPDB, f), typeServerDependency(*ts) {}
+ UseTypeServerSource(ObjFile *f, TypeServer2Record ts)
+ : TpiSource(UsingPDB, f), typeServerDependency(ts) {}
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
// Information about the PDB type server dependency, that needs to be loaded
// in before merging this OBJ.
@@ -76,15 +84,35 @@ public:
// such files, clang does not.
class PrecompSource : public TpiSource {
public:
- PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {}
+ PrecompSource(ObjFile *f) : TpiSource(PCH, f) {
+ if (!f->pchSignature || !*f->pchSignature)
+ fatal(toString(f) +
+ " claims to be a PCH object, but does not have a valid signature");
+ auto it = mappings.emplace(*f->pchSignature, this);
+ if (!it.second)
+ fatal("a PCH object with the same signature has already been provided (" +
+ toString(it.first->second->file) + " and " + toString(file) + ")");
+ precompIndexMap.isPrecompiledTypeMap = true;
+ }
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
+ bool isDependency() const override { return true; }
+
+ CVIndexMap precompIndexMap;
+
+ static std::map<uint32_t, PrecompSource *> mappings;
};
// This class represents the debug type stream of an OBJ file that depends on a
// Microsoft precompiled headers OBJ (see PrecompSource).
class UsePrecompSource : public TpiSource {
public:
- UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp)
- : TpiSource(UsingPCH, f), precompDependency(*precomp) {}
+ UsePrecompSource(ObjFile *f, PrecompRecord precomp)
+ : TpiSource(UsingPCH, f), precompDependency(precomp) {}
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
// Information about the Precomp OBJ dependency, that needs to be loaded in
// before merging this OBJ.
@@ -92,173 +120,363 @@ public:
};
} // namespace
-TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {}
+static std::vector<TpiSource *> gc;
-TpiSource *makeTpiSource(const ObjFile *f) {
- return make<TpiSource>(TpiSource::Regular, f);
+TpiSource::TpiSource(TpiKind k, ObjFile *f) : kind(k), file(f) {
+ gc.push_back(this);
}
-TpiSource *makeUseTypeServerSource(const ObjFile *f,
- const TypeServer2Record *ts) {
- TypeServerSource::enqueue(f, *ts);
- return make<UseTypeServerSource>(f, ts);
+// Vtable key method.
+TpiSource::~TpiSource() = default;
+
+TpiSource *lld::coff::makeTpiSource(ObjFile *file) {
+ return make<TpiSource>(TpiSource::Regular, file);
}
-TpiSource *makePrecompSource(const ObjFile *f) {
- return make<PrecompSource>(f);
+TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) {
+ return make<TypeServerSource>(pdbInputFile);
}
-TpiSource *makeUsePrecompSource(const ObjFile *f,
- const PrecompRecord *precomp) {
- return make<UsePrecompSource>(f, precomp);
+TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file,
+ TypeServer2Record ts) {
+ return make<UseTypeServerSource>(file, ts);
}
-template <>
-const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
- assert(source->kind == TpiSource::UsingPCH);
- return ((const UsePrecompSource *)source)->precompDependency;
+TpiSource *lld::coff::makePrecompSource(ObjFile *file) {
+ return make<PrecompSource>(file);
}
-template <>
-const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
- assert(source->kind == TpiSource::UsingPDB);
- return ((const UseTypeServerSource *)source)->typeServerDependency;
+TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file,
+ PrecompRecord precomp) {
+ return make<UsePrecompSource>(file, precomp);
}
-std::map<std::string, std::pair<std::string, TypeServerSource *>>
- TypeServerSource::instances;
+void TpiSource::forEachSource(llvm::function_ref<void(TpiSource *)> fn) {
+ for_each(gc, fn);
+}
-// Make a PDB path assuming the PDB is in the same folder as the OBJ
-static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) {
- StringRef localPath =
- !file->parentName.empty() ? file->parentName : file->getName();
- SmallString<128> path = sys::path::parent_path(localPath);
+std::map<codeview::GUID, TypeServerSource *> TypeServerSource::mappings;
+
+std::map<uint32_t, PrecompSource *> PrecompSource::mappings;
+
+// A COFF .debug$H section is currently a clang extension. This function checks
+// if a .debug$H section is in a format that we expect / understand, so that we
+// can ignore any sections which are coincidentally also named .debug$H but do
+// not contain a format we recognize.
+static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
+ if (debugH.size() < sizeof(object::debug_h_header))
+ return false;
+ auto *header =
+ reinterpret_cast<const object::debug_h_header *>(debugH.data());
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
+ header->Version == 0 &&
+ header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
+ (debugH.size() % 8 == 0);
+}
- // Currently, type server PDBs are only created by MSVC cl, which only runs
- // on Windows, so we can assume type server paths are Windows style.
- sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows));
- return path.str();
+static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
+ SectionChunk *sec =
+ SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
+ if (!sec)
+ return llvm::None;
+ ArrayRef<uint8_t> contents = sec->getContents();
+ if (!canUseDebugH(contents))
+ return None;
+ return contents;
}
-// 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
-// PDBInputFile::Instances map, at least on Windows.
-static std::string normalizePdbPath(StringRef path) {
-#if defined(_WIN32)
- return path.lower();
-#else // LINUX
- return path;
-#endif
+static ArrayRef<GloballyHashedType>
+getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
+ assert(canUseDebugH(debugH));
+
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ uint32_t count = debugH.size() / sizeof(GloballyHashedType);
+ return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
}
-// If existing, return the actual PDB path on disk.
-static Optional<std::string> findPdbPath(StringRef pdbPath,
- const ObjFile *dependentFile) {
- // Ensure the file exists before anything else. In some cases, if the path
- // points to a removable device, Driver::enqueuePath() would fail with an
- // error (EAGAIN, "resource unavailable try again") which we want to skip
- // silently.
- if (llvm::sys::fs::exists(pdbPath))
- return normalizePdbPath(pdbPath);
- std::string ret = getPdbBaseName(dependentFile, pdbPath);
- if (llvm::sys::fs::exists(ret))
- return normalizePdbPath(ret);
- return None;
+// Merge .debug$T for a generic object file.
+Expected<const CVIndexMap *> TpiSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) {
+ CVTypeArray types;
+ BinaryStreamReader reader(file->debugTypes, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+
+ if (config->debugGHashes) {
+ ArrayRef<GloballyHashedType> hashes;
+ std::vector<GloballyHashedType> ownedHashes;
+ if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
+ hashes = getHashesFromDebugH(*debugH);
+ else {
+ ownedHashes = GloballyHashedType::hashTypes(types);
+ hashes = ownedHashes;
+ }
+
+ if (auto err = mergeTypeAndIdRecords(m->globalIDTable, m->globalTypeTable,
+ indexMap->tpiMap, types, hashes,
+ file->pchSignature))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(err)));
+ } else {
+ if (auto err =
+ mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMap->tpiMap,
+ types, file->pchSignature))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(err)));
+ }
+
+ if (config->showSummary) {
+ // Count how many times we saw each type record in our input. This
+ // calculation requires a second pass over the type records to classify each
+ // record as a type or index. This is slow, but this code executes when
+ // collecting statistics.
+ m->tpiCounts.resize(m->getTypeTable().size());
+ m->ipiCounts.resize(m->getIDTable().size());
+ uint32_t srcIdx = 0;
+ for (CVType &ty : types) {
+ TypeIndex dstIdx = indexMap->tpiMap[srcIdx++];
+ // Type merging may fail, so a complex source type may become the simple
+ // NotTranslated type, which cannot be used as an array index.
+ if (dstIdx.isSimple())
+ continue;
+ SmallVectorImpl<uint32_t> &counts =
+ isIdRecord(ty.kind()) ? m->ipiCounts : m->tpiCounts;
+ ++counts[dstIdx.toArrayIndex()];
+ }
+ }
+
+ return indexMap;
}
-// Fetch the PDB instance that was already loaded by the COFF Driver.
-Expected<TypeServerSource *>
-TypeServerSource::findFromFile(const ObjFile *dependentFile) {
- const TypeServer2Record &ts =
- retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj);
+// Merge types from a type server PDB.
+Expected<const CVIndexMap *> TypeServerSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *) {
+ pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
+ Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
+ if (auto e = expectedTpi.takeError())
+ fatal("Type server does not have TPI stream: " + toString(std::move(e)));
+ pdb::TpiStream *maybeIpi = nullptr;
+ if (pdbFile.hasPDBIpiStream()) {
+ Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
+ if (auto e = expectedIpi.takeError())
+ fatal("Error getting type server IPI stream: " + toString(std::move(e)));
+ maybeIpi = &*expectedIpi;
+ }
+
+ if (config->debugGHashes) {
+ // PDBs do not actually store global hashes, so when merging a type server
+ // PDB we have to synthesize global hashes. To do this, we first synthesize
+ // global hashes for the TPI stream, since it is independent, then we
+ // synthesize hashes for the IPI stream, using the hashes for the TPI stream
+ // as inputs.
+ auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
+ Optional<uint32_t> endPrecomp;
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto err =
+ mergeTypeRecords(m->globalTypeTable, tsIndexMap.tpiMap,
+ expectedTpi->typeArray(), tpiHashes, endPrecomp))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
+
+ // Merge IPI.
+ if (maybeIpi) {
+ auto ipiHashes =
+ GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
+ if (auto err = mergeIdRecords(m->globalIDTable, tsIndexMap.tpiMap,
+ tsIndexMap.ipiMap, maybeIpi->typeArray(),
+ ipiHashes))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
+ } else {
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto err = mergeTypeRecords(m->typeTable, tsIndexMap.tpiMap,
+ expectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
+
+ // Merge IPI.
+ if (maybeIpi) {
+ if (auto err = mergeIdRecords(m->idTable, tsIndexMap.tpiMap,
+ tsIndexMap.ipiMap, maybeIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
+ }
+
+ if (config->showSummary) {
+ // Count how many times we saw each type record in our input. If a
+ // destination type index is present in the source to destination type index
+ // map, that means we saw it once in the input. Add it to our histogram.
+ m->tpiCounts.resize(m->getTypeTable().size());
+ m->ipiCounts.resize(m->getIDTable().size());
+ for (TypeIndex ti : tsIndexMap.tpiMap)
+ if (!ti.isSimple())
+ ++m->tpiCounts[ti.toArrayIndex()];
+ for (TypeIndex ti : tsIndexMap.ipiMap)
+ if (!ti.isSimple())
+ ++m->ipiCounts[ti.toArrayIndex()];
+ }
+
+ return &tsIndexMap;
+}
- Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
- if (!p)
- return createFileError(ts.Name, errorCodeToError(std::error_code(
- ENOENT, std::generic_category())));
+Expected<const CVIndexMap *>
+UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) {
+ const codeview::GUID &tsId = typeServerDependency.getGuid();
+ StringRef tsPath = typeServerDependency.getName();
+
+ TypeServerSource *tsSrc;
+ auto it = TypeServerSource::mappings.find(tsId);
+ if (it != TypeServerSource::mappings.end()) {
+ tsSrc = it->second;
+ } else {
+ // The file failed to load, lookup by name
+ PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file);
+ if (!pdb)
+ return createFileError(tsPath, errorCodeToError(std::error_code(
+ ENOENT, std::generic_category())));
+ // If an error occurred during loading, throw it now
+ if (pdb->loadErr && *pdb->loadErr)
+ return createFileError(tsPath, std::move(*pdb->loadErr));
+
+ tsSrc = (TypeServerSource *)pdb->debugTypesObj;
+ }
+
+ pdb::PDBFile &pdbSession = tsSrc->pdbInputFile->session->getPDBFile();
+ auto expectedInfo = pdbSession.getPDBInfoStream();
+ if (!expectedInfo)
+ return &tsSrc->tsIndexMap;
+
+ // Just because a file with a matching name was found and it was an actual
+ // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
+ // must match the GUID specified in the TypeServer2 record.
+ if (expectedInfo->getGuid() != typeServerDependency.getGuid())
+ return createFileError(
+ tsPath,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
- auto it = TypeServerSource::instances.find(*p);
- // The PDB file exists on disk, at this point we expect it to have been
- // inserted in the map by TypeServerSource::loadPDB()
- assert(it != TypeServerSource::instances.end());
+ return &tsSrc->tsIndexMap;
+}
+
+static bool equalsPath(StringRef path1, StringRef path2) {
+#if defined(_WIN32)
+ return path1.equals_lower(path2);
+#else
+ return path1.equals(path2);
+#endif
+}
- std::pair<std::string, TypeServerSource *> &pdb = it->second;
+// Find by name an OBJ provided on the command line
+static PrecompSource *findObjByName(StringRef fileNameOnly) {
+ SmallString<128> currentPath;
+ for (auto kv : PrecompSource::mappings) {
+ StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
+ sys::path::Style::windows);
+
+ // Compare based solely on the file name (link.exe behavior)
+ if (equalsPath(currentFileName, fileNameOnly))
+ return kv.second;
+ }
+ return nullptr;
+}
- if (!pdb.second)
+Expected<const CVIndexMap *> findPrecompMap(ObjFile *file, PrecompRecord &pr) {
+ // 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,
+ // the paths embedded in the OBJs are in the Windows format.
+ SmallString<128> prFileName =
+ sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
+
+ PrecompSource *precomp;
+ auto it = PrecompSource::mappings.find(pr.getSignature());
+ if (it != PrecompSource::mappings.end()) {
+ precomp = it->second;
+ } else {
+ // Lookup by name
+ precomp = findObjByName(prFileName);
+ }
+
+ if (!precomp)
return createFileError(
- *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str()));
+ prFileName,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile();
- pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
+ if (pr.getSignature() != file->pchSignature)
+ return createFileError(
+ toString(file),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- // Just because a file with a matching name was found doesn't mean it can be
- // used. The GUID must match between the PDB header and the OBJ
- // TypeServer2 record. The 'Age' is used by MSVC incremental compilation.
- if (info.getGuid() != ts.getGuid())
+ if (pr.getSignature() != *precomp->file->pchSignature)
return createFileError(
- ts.Name,
- make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
+ toString(precomp->file),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- return pdb.second;
+ return &precomp->precompIndexMap;
}
-// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
-// moved here.
-Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
- Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
- if (!ts)
- return ts.takeError();
- return ts.get()->session.get();
+/// Merges a precompiled headers TPI map into the current TPI map. The
+/// precompiled headers object will also be loaded and remapped in the
+/// process.
+static Expected<const CVIndexMap *>
+mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *indexMap,
+ PrecompRecord &precomp) {
+ auto e = findPrecompMap(file, precomp);
+ if (!e)
+ return e.takeError();
+
+ const CVIndexMap *precompIndexMap = *e;
+ assert(precompIndexMap->isPrecompiledTypeMap);
+
+ if (precompIndexMap->tpiMap.empty())
+ return precompIndexMap;
+
+ assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
+ assert(precomp.getTypesCount() <= precompIndexMap->tpiMap.size());
+ // Use the previously remapped index map from the precompiled headers.
+ indexMap->tpiMap.append(precompIndexMap->tpiMap.begin(),
+ precompIndexMap->tpiMap.begin() +
+ precomp.getTypesCount());
+ return indexMap;
}
-// Queue a PDB type server for loading in the COFF Driver
-void TypeServerSource::enqueue(const ObjFile *dependentFile,
- const TypeServer2Record &ts) {
- // Start by finding where the PDB is located (either the record path or next
- // to the OBJ file)
- Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
- if (!p)
- return;
- auto it = TypeServerSource::instances.emplace(
- *p, std::pair<std::string, TypeServerSource *>{});
- if (!it.second)
- return; // another OBJ already scheduled this PDB for load
-
- driver->enqueuePath(*p, false, false);
+Expected<const CVIndexMap *>
+UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) {
+ // This object was compiled with /Yu, so process the corresponding
+ // precompiled headers object (/Yc) first. Some type indices in the current
+ // object are referencing data in the precompiled headers object, so we need
+ // both to be loaded.
+ auto e = mergeInPrecompHeaderObj(file, indexMap, precompDependency);
+ if (!e)
+ return e.takeError();
+
+ // Drop LF_PRECOMP record from the input stream, as it has been replaced
+ // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
+ // call above. Note that we can't just call Types.drop_front(), as we
+ // explicitly want to rebase the stream.
+ CVTypeArray types;
+ BinaryStreamReader reader(file->debugTypes, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+ auto firstType = types.begin();
+ file->debugTypes = file->debugTypes.drop_front(firstType->RecordData.size());
+
+ return TpiSource::mergeDebugT(m, indexMap);
}
-// Create an instance of TypeServerSource or an error string if the PDB couldn't
-// be loaded. The error message will be displayed later, when the referring OBJ
-// will be merged in. NOTE - a PDB load failure is not a link error: some
-// debug info will simply be missing from the final PDB - that is the default
-// accepted behavior.
-void loadTypeServerSource(llvm::MemoryBufferRef m) {
- std::string path = normalizePdbPath(m.getBufferIdentifier());
-
- Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
- if (!ts)
- TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr};
- else
- TypeServerSource::instances[path] = {{}, *ts};
+Expected<const CVIndexMap *> PrecompSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *) {
+ // Note that we're not using the provided CVIndexMap. Instead, we use our
+ // local one. Precompiled headers objects need to save the index map for
+ // further reference by other objects which use the precompiled headers.
+ return TpiSource::mergeDebugT(m, &precompIndexMap);
}
-Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
- std::unique_ptr<llvm::pdb::IPDBSession> iSession;
- Error err = pdb::NativeSession::createFromPdb(
- MemoryBuffer::getMemBuffer(m, false), iSession);
- if (err)
- return std::move(err);
-
- std::unique_ptr<llvm::pdb::NativeSession> session(
- static_cast<pdb::NativeSession *>(iSession.release()));
-
- pdb::PDBFile &pdbFile = session->getPDBFile();
- Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream();
- // All PDB Files should have an Info stream.
- if (!info)
- return info.takeError();
- return make<TypeServerSource>(m, session.release());
+uint32_t TpiSource::countTypeServerPDBs() {
+ return TypeServerSource::mappings.size();
}
-} // namespace coff
-} // namespace lld
+uint32_t TpiSource::countPrecompObjs() {
+ return PrecompSource::mappings.size();
+}
+
+void TpiSource::clear() {
+ gc.clear();
+ TypeServerSource::mappings.clear();
+ PrecompSource::mappings.clear();
+}
diff --git a/contrib/llvm-project/lld/COFF/DebugTypes.h b/contrib/llvm-project/lld/COFF/DebugTypes.h
index e37c727232d0..24d79d83e4c6 100644
--- a/contrib/llvm-project/lld/COFF/DebugTypes.h
+++ b/contrib/llvm-project/lld/COFF/DebugTypes.h
@@ -26,35 +26,55 @@ namespace lld {
namespace coff {
class ObjFile;
+class PDBInputFile;
+struct CVIndexMap;
+class TypeMerger;
class TpiSource {
public:
enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB };
- TpiSource(TpiKind k, const ObjFile *f);
- virtual ~TpiSource() {}
+ TpiSource(TpiKind k, ObjFile *f);
+ virtual ~TpiSource();
- const TpiKind kind;
- const ObjFile *file;
-};
+ /// Produce a mapping from the type and item indices used in the object
+ /// file to those in the destination PDB.
+ ///
+ /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
+ /// and IPI from the type server PDB and return a map for it. Each unique type
+ /// server PDB is merged at most once, so this may return an existing index
+ /// mapping.
+ ///
+ /// If the object does not use a type server PDB (compiled with /Z7), we merge
+ /// all the type and item records from the .debug$S stream and fill in the
+ /// caller-provided ObjectIndexMap.
+ virtual llvm::Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap);
+ /// Is this a dependent file that needs to be processed first, before other
+ /// OBJs?
+ virtual bool isDependency() const { return false; }
+
+ static void forEachSource(llvm::function_ref<void(TpiSource *)> fn);
-TpiSource *makeTpiSource(const ObjFile *f);
-TpiSource *makeUseTypeServerSource(const ObjFile *f,
- const llvm::codeview::TypeServer2Record *ts);
-TpiSource *makePrecompSource(const ObjFile *f);
-TpiSource *makeUsePrecompSource(const ObjFile *f,
- const llvm::codeview::PrecompRecord *precomp);
+ static uint32_t countTypeServerPDBs();
+ static uint32_t countPrecompObjs();
-void loadTypeServerSource(llvm::MemoryBufferRef m);
+ /// Clear global data structures for TpiSources.
+ static void clear();
-// Temporary interface to get the dependency
-template <typename T> const T &retrieveDependencyInfo(const TpiSource *source);
+ const TpiKind kind;
+ ObjFile *file;
+};
-// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here
-llvm::Expected<llvm::pdb::NativeSession *>
-findTypeServerSource(const ObjFile *f);
+TpiSource *makeTpiSource(ObjFile *file);
+TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile);
+TpiSource *makeUseTypeServerSource(ObjFile *file,
+ llvm::codeview::TypeServer2Record ts);
+TpiSource *makePrecompSource(ObjFile *file);
+TpiSource *makeUsePrecompSource(ObjFile *file,
+ llvm::codeview::PrecompRecord ts);
} // namespace coff
} // namespace lld
-#endif \ No newline at end of file
+#endif
diff --git a/contrib/llvm-project/lld/COFF/Driver.cpp b/contrib/llvm-project/lld/COFF/Driver.cpp
index f770fff80bcb..9ceccef86779 100644
--- a/contrib/llvm-project/lld/COFF/Driver.cpp
+++ b/contrib/llvm-project/lld/COFF/Driver.cpp
@@ -21,7 +21,6 @@
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
@@ -39,6 +38,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
@@ -89,6 +89,8 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
ImportFile::instances.clear();
BitcodeFile::instances.clear();
memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
+ TpiSource::clear();
+
return !errorCount();
}
@@ -218,7 +220,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
symtab->addFile(make<ObjFile>(mbref));
break;
case file_magic::pdb:
- loadTypeServerSource(mbref);
+ symtab->addFile(make<PDBInputFile>(mbref));
break;
case file_magic::coff_cl_gl_object:
error(filename + ": is not a native COFF file. Recompile without /GL");
@@ -237,9 +239,9 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
}
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
- auto future =
- std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
- std::string pathStr = path;
+ auto future = std::make_shared<std::future<MBErrPair>>(
+ createFutureForFile(std::string(path)));
+ std::string pathStr = std::string(path);
enqueueTask([=]() {
auto mbOrErr = future->get();
if (mbOrErr.second) {
@@ -251,7 +253,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
// the option `/nodefaultlib` than a reference to a file in the root
// directory.
std::string nearest;
- if (COFFOptTable().findNearest(pathStr, nearest) > 1)
+ if (optTable.findNearest(pathStr, nearest) > 1)
error(msg);
else
error(msg + "; did you mean '" + nearest + "'");
@@ -343,11 +345,9 @@ void LinkerDriver::parseDirectives(InputFile *file) {
ArgParser parser;
// .drectve is always tokenized using Windows shell rules.
// /EXPORT: option can appear too many times, processing in fastpath.
- opt::InputArgList args;
- std::vector<StringRef> exports;
- std::tie(args, exports) = parser.parseDirectives(s);
+ ParsedDirectives directives = parser.parseDirectives(s);
- for (StringRef e : exports) {
+ for (StringRef e : directives.exports) {
// If a common header file contains dllexported function
// declarations, many object files may end up with having the
// same /EXPORT options. In order to save cost of parsing them,
@@ -366,7 +366,11 @@ void LinkerDriver::parseDirectives(InputFile *file) {
config->exports.push_back(exp);
}
- for (auto *arg : args) {
+ // Handle /include: in bulk.
+ for (StringRef inc : directives.includes)
+ addUndefined(inc);
+
+ for (auto *arg : directives.args) {
switch (arg->getOption().getID()) {
case OPT_aligncomm:
parseAligncomm(arg->getValue());
@@ -452,7 +456,7 @@ Optional<StringRef> LinkerDriver::findFile(StringRef filename) {
}
if (path.endswith_lower(".lib"))
- visitedLibs.insert(sys::path::filename(path));
+ visitedLibs.insert(std::string(sys::path::filename(path)));
return path;
}
@@ -624,6 +628,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
break;
case OPT_implib:
case OPT_pdb:
+ case OPT_pdbstripped:
case OPT_out:
os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n";
break;
@@ -640,7 +645,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
for (StringRef path : filePaths)
os << quote(relativeToRoot(path)) << "\n";
- return data.str();
+ return std::string(data.str());
}
enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
@@ -706,24 +711,25 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
return debugTypes;
}
-static std::string getMapFile(const opt::InputArgList &args) {
- auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+static std::string getMapFile(const opt::InputArgList &args,
+ opt::OptSpecifier os, opt::OptSpecifier osFile) {
+ auto *arg = args.getLastArg(os, osFile);
if (!arg)
return "";
- if (arg->getOption().getID() == OPT_lldmap_file)
+ if (arg->getOption().getID() == osFile.getID())
return arg->getValue();
- assert(arg->getOption().getID() == OPT_lldmap);
+ assert(arg->getOption().getID() == os.getID());
StringRef outFile = config->outputFile;
return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
static std::string getImplibPath() {
if (!config->implib.empty())
- return config->implib;
+ return std::string(config->implib);
SmallString<128> out = StringRef(config->outputFile);
sys::path::replace_extension(out, ".lib");
- return out.str();
+ return std::string(out.str());
}
// The import name is calculated as follows:
@@ -747,16 +753,16 @@ static std::string getImportName(bool asLib) {
(config->dll || asLib) ? ".dll" : ".exe");
}
- return out.str();
+ return std::string(out.str());
}
static void createImportLibrary(bool asLib) {
std::vector<COFFShortExport> exports;
for (Export &e1 : config->exports) {
COFFShortExport e2;
- e2.Name = e1.name;
- e2.SymbolName = e1.symbolName;
- e2.ExtName = e1.extName;
+ e2.Name = std::string(e1.name);
+ e2.SymbolName = std::string(e1.symbolName);
+ e2.ExtName = std::string(e1.extName);
e2.Ordinal = e1.ordinal;
e2.Noname = e1.noname;
e2.Data = e1.data;
@@ -817,8 +823,8 @@ static void parseModuleDefs(StringRef path) {
mb->getMemBufferRef(), config->machine, config->mingw));
if (config->outputFile.empty())
- config->outputFile = saver.save(m.OutputFile);
- config->importName = saver.save(m.ImportName);
+ config->outputFile = std::string(saver.save(m.OutputFile));
+ config->importName = std::string(saver.save(m.ImportName));
if (m.ImageBase)
config->imageBase = m.ImageBase;
if (m.StackReserve)
@@ -844,7 +850,8 @@ static void parseModuleDefs(StringRef path) {
// and set as "ExtName = Name". If Name has the form "OtherDll.Func",
// it shouldn't be a normal exported function but a forward to another
// DLL instead. This is supported by both MS and GNU linkers.
- if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) {
+ if (!e1.ExtName.empty() && e1.ExtName != e1.Name &&
+ StringRef(e1.Name).contains('.')) {
e2.name = saver.save(e1.ExtName);
e2.forwardTo = saver.save(e1.Name);
config->exports.push_back(e2);
@@ -903,7 +910,8 @@ static void parseOrderFile(StringRef arg) {
// All symbols that were not present in a given order file are
// considered to have the lowest priority 0 and are placed at
// end of an output section.
- for (std::string s : args::getLines(mb->getMemBufferRef())) {
+ for (StringRef arg : args::getLines(mb->getMemBufferRef())) {
+ std::string s(arg);
if (config->machine == I386 && !isDecorated(s))
s = "_" + s;
@@ -1093,13 +1101,15 @@ Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
SmallString<64> path = StringRef(arg->getValue());
sys::path::append(path, "repro.tar");
- return path.str().str();
+ return std::string(path);
}
return None;
}
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
+ ScopedTimer rootTimer(Timer::root());
+
// Needed for LTO.
InitializeAllTargetInfos();
InitializeAllTargets();
@@ -1141,14 +1151,23 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
return;
}
- lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true);
+ // /threads: takes a positive integer and provides the default value for
+ // /opt:lldltojobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v.str();
+ }
if (args.hasArg(OPT_show_timing))
config->showTiming = true;
config->showSummary = args.hasArg(OPT_summary);
- ScopedTimer t(Timer::root());
// Handle --version, which is an lld extension. This option is a bit odd
// because it doesn't start with "/", but we deliberately chose "--" to
// avoid conflict with /version and for compatibility with clang-cl.
@@ -1260,11 +1279,23 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
config->pdbAltPath = arg->getValue();
if (args.hasArg(OPT_natvis))
config->natvisFiles = args.getAllArgValues(OPT_natvis);
+ if (args.hasArg(OPT_pdbstream)) {
+ for (const StringRef value : args.getAllArgValues(OPT_pdbstream)) {
+ const std::pair<StringRef, StringRef> nameFile = value.split("=");
+ const StringRef name = nameFile.first;
+ const std::string file = nameFile.second.str();
+ config->namedStreams[name] = file;
+ }
+ }
if (auto *arg = args.getLastArg(OPT_pdb_source_path))
config->pdbSourcePath = arg->getValue();
}
+ // Handle /pdbstripped
+ if (args.hasArg(OPT_pdbstripped))
+ warn("ignoring /pdbstripped flag, it is not yet supported");
+
// Handle /noentry
if (args.hasArg(OPT_noentry)) {
if (args.hasArg(OPT_dll))
@@ -1410,9 +1441,9 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
error("/opt:lldlto: invalid optimization level: " + optLevel);
} else if (s.startswith("lldltojobs=")) {
StringRef jobs = s.substr(11);
- if (jobs.getAsInteger(10, config->thinLTOJobs) ||
- config->thinLTOJobs == 0)
+ if (!get_threadpool_strategy(jobs))
error("/opt:lldltojobs: invalid job count: " + jobs);
+ config->thinLTOJobs = jobs.str();
} else if (s.startswith("lldltopartitions=")) {
StringRef n = s.substr(17);
if (n.getAsInteger(10, config->ltoPartitions) ||
@@ -1543,6 +1574,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
!args.hasArg(OPT_profile));
config->integrityCheck =
args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
+ config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
for (auto *arg : args.filtered(OPT_swaprun))
parseSwaprun(arg->getValue());
@@ -1551,13 +1583,24 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
config->debugDwarf = debug == DebugKind::Dwarf;
config->debugGHashes = debug == DebugKind::GHash;
config->debugSymtab = debug == DebugKind::Symtab;
+ config->autoImport =
+ args.hasFlag(OPT_auto_import, OPT_auto_import_no, config->mingw);
+ config->pseudoRelocs = args.hasFlag(
+ OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw);
// Don't warn about long section names, such as .debug_info, for mingw or when
// -debug:dwarf is requested.
if (config->mingw || config->debugDwarf)
config->warnLongSectionNames = false;
- config->mapFile = getMapFile(args);
+ config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
+ config->mapFile = getMapFile(args, OPT_map, OPT_map_file);
+
+ if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
+ warn("/lldmap and /map have the same output file '" + config->mapFile +
+ "'.\n>>> ignoring /lldmap");
+ config->lldmapFile.clear();
+ }
if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
@@ -1657,9 +1700,10 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
config->wordsize = config->is64() ? 8 : 4;
// Handle /safeseh, x86 only, on by default, except for mingw.
- if (config->machine == I386 &&
- args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw))
- config->safeSEH = true;
+ if (config->machine == I386) {
+ config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw);
+ config->noSEH = args.hasArg(OPT_noseh);
+ }
// Handle /functionpadmin
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
@@ -1805,9 +1849,11 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Needed for MSVC 2017 15.5 CRT.
symtab->addAbsolute(mangle("__enclave_config"), 0);
- if (config->mingw) {
+ if (config->pseudoRelocs) {
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+ }
+ if (config->mingw) {
symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
}
@@ -1865,7 +1911,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
while (run());
}
- if (config->mingw) {
+ if (config->autoImport) {
+ // MinGW specific.
// Load any further object files that might be needed for doing automatic
// imports.
//
@@ -1997,7 +2044,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
writeResult();
// Stop early so we can print the results.
- Timer::root().stop();
+ rootTimer.stop();
if (config->showTiming)
Timer::root().print();
}
diff --git a/contrib/llvm-project/lld/COFF/Driver.h b/contrib/llvm-project/lld/COFF/Driver.h
index cc2f25a6f95e..3fee9b1fe50e 100644
--- a/contrib/llvm-project/lld/COFF/Driver.h
+++ b/contrib/llvm-project/lld/COFF/Driver.h
@@ -41,6 +41,21 @@ public:
COFFOptTable();
};
+// Constructing the option table is expensive. Use a global table to avoid doing
+// it more than once.
+extern COFFOptTable optTable;
+
+// The result of parsing the .drective section. The /export: and /include:
+// options are handled separately because they reference symbols, and the number
+// of symbols can be quite large. The LLVM Option library will perform at least
+// one memory allocation per argument, and that is prohibitively slow for
+// parsing directives.
+struct ParsedDirectives {
+ std::vector<StringRef> exports;
+ std::vector<StringRef> includes;
+ llvm::opt::InputArgList args;
+};
+
class ArgParser {
public:
// Parses command line options.
@@ -52,16 +67,13 @@ public:
// Tokenizes a given string and then parses as command line options in
// .drectve section. /EXPORT options are returned in second element
// to be processed in fastpath.
- std::pair<llvm::opt::InputArgList, std::vector<StringRef>>
- parseDirectives(StringRef s);
+ ParsedDirectives parseDirectives(StringRef s);
private:
// Concatenate LINK environment variable.
void addLINK(SmallVector<const char *, 256> &argv);
std::vector<const char *> tokenize(StringRef s);
-
- COFFOptTable table;
};
class LinkerDriver {
@@ -75,6 +87,8 @@ public:
void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
StringRef parentName);
+ void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
diff --git a/contrib/llvm-project/lld/COFF/DriverUtils.cpp b/contrib/llvm-project/lld/COFF/DriverUtils.cpp
index 301a5c5efa8a..6cb761abea4e 100644
--- a/contrib/llvm-project/lld/COFF/DriverUtils.cpp
+++ b/contrib/llvm-project/lld/COFF/DriverUtils.cpp
@@ -220,7 +220,8 @@ void parseAligncomm(StringRef s) {
error("/aligncomm: invalid argument: " + s);
return;
}
- config->alignComm[name] = std::max(config->alignComm[name], 1 << v);
+ config->alignComm[std::string(name)] =
+ std::max(config->alignComm[std::string(name)], 1 << v);
}
// Parses /functionpadmin option argument.
@@ -318,7 +319,7 @@ public:
SmallString<128> s;
if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))
fatal("cannot create a temporary file: " + ec.message());
- path = s.str();
+ path = std::string(s.str());
if (!contents.empty()) {
std::error_code ec;
@@ -403,7 +404,7 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
toString(std::move(e)));
}
- return merger.getMergedManifest().get()->getBuffer();
+ return std::string(merger.getMergedManifest().get()->getBuffer());
}
static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
@@ -431,9 +432,10 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
e.add("/out:" + StringRef(user.path));
e.run();
- return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
- .get()
- ->getBuffer();
+ return std::string(
+ CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
+ .get()
+ ->getBuffer());
}
static std::string createManifestXml() {
@@ -507,7 +509,7 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
}
void createSideBySideManifest() {
- std::string path = config->manifestFile;
+ std::string path = std::string(config->manifestFile);
if (path == "")
path = config->outputFile + ".manifest";
std::error_code ec;
@@ -765,6 +767,8 @@ static const llvm::opt::OptTable::Info infoTable[] = {
COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
+COFFOptTable optTable;
+
// Set color diagnostics according to --color-diagnostics={auto,always,never}
// or --no-color-diagnostics flags.
static void handleColorDiagnostics(opt::InputArgList &args) {
@@ -810,8 +814,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
// options so we parse here before and ignore all the options but
// --rsp-quoting and /lldignoreenv.
// (This means --rsp-quoting can't be added through %LINK%.)
- opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
-
+ opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>) and insert
// flags from %LINK% and %_LINK_%, and then parse the argument again.
@@ -820,8 +823,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
if (!args.hasArg(OPT_lldignoreenv))
addLINK(expandedArgv);
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
- args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
- missingCount);
+ args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(),
+ missingIndex, missingCount);
// Print the real command line if response files are expanded.
if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
@@ -845,7 +848,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
for (auto *arg : args.filtered(OPT_UNKNOWN)) {
std::string nearest;
- if (table.findNearest(arg->getAsString(args), nearest) > 1)
+ if (optTable.findNearest(arg->getAsString(args), nearest) > 1)
warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
else
warn("ignoring unknown argument '" + arg->getAsString(args) +
@@ -859,30 +862,38 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
}
// Tokenizes and parses a given string as command line in .drective section.
-// /EXPORT options are processed in fastpath.
-std::pair<opt::InputArgList, std::vector<StringRef>>
-ArgParser::parseDirectives(StringRef s) {
- std::vector<StringRef> exports;
+ParsedDirectives ArgParser::parseDirectives(StringRef s) {
+ ParsedDirectives result;
SmallVector<const char *, 16> rest;
- for (StringRef tok : tokenize(s)) {
+ // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for
+ // potentially every symbol in the object, so they must be handled quickly.
+ SmallVector<StringRef, 16> tokens;
+ cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens);
+ for (StringRef tok : tokens) {
if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:"))
- exports.push_back(tok.substr(strlen("/export:")));
- else
- rest.push_back(tok.data());
+ result.exports.push_back(tok.substr(strlen("/export:")));
+ else if (tok.startswith_lower("/include:") ||
+ tok.startswith_lower("-include:"))
+ result.includes.push_back(tok.substr(strlen("/include:")));
+ else {
+ // Save non-null-terminated strings to make proper C strings.
+ bool HasNul = tok.data()[tok.size()] == '\0';
+ rest.push_back(HasNul ? tok.data() : saver.save(tok).data());
+ }
}
// Make InputArgList from unparsed string vectors.
unsigned missingIndex;
unsigned missingCount;
- opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount);
+ result.args = optTable.ParseArgs(rest, missingIndex, missingCount);
if (missingCount)
- fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
- for (auto *arg : args.filtered(OPT_UNKNOWN))
- warn("ignoring unknown argument: " + arg->getAsString(args));
- return {std::move(args), std::move(exports)};
+ fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument");
+ for (auto *arg : result.args.filtered(OPT_UNKNOWN))
+ warn("ignoring unknown argument: " + arg->getAsString(result.args));
+ return result;
}
// link.exe has an interesting feature. If LINK or _LINK_ environment
@@ -907,9 +918,9 @@ std::vector<const char *> ArgParser::tokenize(StringRef s) {
}
void printHelp(const char *argv0) {
- COFFOptTable().PrintHelp(lld::outs(),
- (std::string(argv0) + " [options] file...").c_str(),
- "LLVM Linker", false);
+ optTable.PrintHelp(lld::outs(),
+ (std::string(argv0) + " [options] file...").c_str(),
+ "LLVM Linker", false);
}
} // namespace coff
diff --git a/contrib/llvm-project/lld/COFF/ICF.cpp b/contrib/llvm-project/lld/COFF/ICF.cpp
index c821569e3457..1b33634b63d6 100644
--- a/contrib/llvm-project/lld/COFF/ICF.cpp
+++ b/contrib/llvm-project/lld/COFF/ICF.cpp
@@ -21,7 +21,6 @@
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
@@ -127,15 +126,19 @@ void ICF::segregate(size_t begin, size_t end, bool constant) {
// Returns true if two sections' associative children are equal.
bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
- auto childClasses = [&](const SectionChunk *sc) {
- std::vector<uint32_t> classes;
- for (const SectionChunk &c : sc->children())
- if (!c.getSectionName().startswith(".debug") &&
- c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y")
- classes.push_back(c.eqClass[cnt % 2]);
- return classes;
+ // Ignore associated metadata sections that don't participate in ICF, such as
+ // debug info and CFGuard metadata.
+ auto considerForICF = [](const SectionChunk &assoc) {
+ StringRef Name = assoc.getSectionName();
+ return !(Name.startswith(".debug") || Name == ".gfids$y" ||
+ Name == ".gljmp$y");
};
- return childClasses(a) == childClasses(b);
+ auto ra = make_filter_range(a->children(), considerForICF);
+ auto rb = make_filter_range(b->children(), considerForICF);
+ return std::equal(ra.begin(), ra.end(), rb.begin(), rb.end(),
+ [&](const SectionChunk &ia, const SectionChunk &ib) {
+ return ia.eqClass[cnt % 2] == ib.eqClass[cnt % 2];
+ });
}
// Compare "non-moving" part of two sections, namely everything
diff --git a/contrib/llvm-project/lld/COFF/InputFiles.cpp b/contrib/llvm-project/lld/COFF/InputFiles.cpp
index 272f9c0717c0..4346b3a2ffa7 100644
--- a/contrib/llvm-project/lld/COFF/InputFiles.cpp
+++ b/contrib/llvm-project/lld/COFF/InputFiles.cpp
@@ -25,6 +25,8 @@
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
@@ -44,32 +46,31 @@ using namespace llvm::COFF;
using namespace llvm::codeview;
using namespace llvm::object;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::coff;
using llvm::Triple;
using llvm::support::ulittle32_t;
-namespace lld {
-
// Returns the last element of a path, which is supposed to be a filename.
static StringRef getBasename(StringRef path) {
return sys::path::filename(path, sys::path::Style::windows);
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string toString(const coff::InputFile *file) {
+std::string lld::toString(const coff::InputFile *file) {
if (!file)
return "<internal>";
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
- return file->getName();
+ return std::string(file->getName());
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
")")
.str();
}
-namespace coff {
-
std::vector<ObjFile *> ObjFile::instances;
+std::map<std::string, PDBInputFile *> PDBInputFile::instances;
std::vector<ImportFile *> ImportFile::instances;
std::vector<BitcodeFile *> BitcodeFile::instances;
@@ -121,7 +122,7 @@ void ArchiveFile::addMember(const Archive::Symbol &sym) {
driver->enqueueArchiveMember(c, sym, getName());
}
-std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
+std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
std::vector<MemoryBufferRef> v;
Error err = Error::success();
for (const Archive::Child &c : file->children(err)) {
@@ -171,8 +172,7 @@ void LazyObjFile::parse() {
if (coffSym.isUndefined() || !coffSym.isExternal() ||
coffSym.isWeakExternal())
continue;
- StringRef name;
- coffObj->getSymbolName(coffSym, name);
+ StringRef name = check(coffObj->getSymbolName(coffSym));
if (coffSym.isAbsolute() && ignoredSymbolName(name))
continue;
symtab->addLazyObject(this, name);
@@ -198,11 +198,11 @@ void ObjFile::parse() {
initializeDependencies();
}
-const coff_section* ObjFile::getSection(uint32_t i) {
- const coff_section *sec;
- if (auto ec = coffObj->getSection(i, sec))
- fatal("getSection failed: #" + Twine(i) + ": " + ec.message());
- return sec;
+const coff_section *ObjFile::getSection(uint32_t i) {
+ auto sec = coffObj->getSection(i);
+ if (!sec)
+ fatal("getSection failed: #" + Twine(i) + ": " + toString(sec.takeError()));
+ return *sec;
}
// We set SectionChunk pointers in the SparseChunks vector to this value
@@ -215,7 +215,6 @@ static SectionChunk *const pendingComdat = reinterpret_cast<SectionChunk *>(1);
void ObjFile::initializeChunks() {
uint32_t numSections = coffObj->getNumberOfSections();
- chunks.reserve(numSections);
sparseChunks.resize(numSections + 1);
for (uint32_t i = 1; i < numSections + 1; ++i) {
const coff_section *sec = getSection(i);
@@ -279,7 +278,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
else if (name == ".gljmp$y")
guardLJmpChunks.push_back(c);
else if (name == ".sxdata")
- sXDataChunks.push_back(c);
+ sxDataChunks.push_back(c);
else if (config->tailMerge && sec->NumberOfRelocations == 0 &&
name == ".rdata" && leaderName.startswith("??_C@"))
// COFF sections that look like string literal sections (i.e. no
@@ -310,9 +309,9 @@ void ObjFile::readAssociativeDefinition(COFFSymbolRef sym,
int32_t sectionNumber = sym.getSectionNumber();
auto diag = [&]() {
- StringRef name, parentName;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
+ StringRef parentName;
const coff_section *parentSec = getSection(parentIndex);
if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
parentName = *e;
@@ -349,13 +348,13 @@ void ObjFile::recordPrevailingSymbolForMingw(
// of the section chunk we actually include instead of discarding it,
// add the symbol to a map to allow using it for implicitly
// associating .[px]data$<func> sections to it.
+ // Use the suffix from the .text$<func> instead of the leader symbol
+ // name, for cases where the names differ (i386 mangling/decorations,
+ // cases where the leader is a weak symbol named .weak.func.default*).
int32_t sectionNumber = sym.getSectionNumber();
SectionChunk *sc = sparseChunks[sectionNumber];
if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
- if (getMachineType() == I386)
- name.consume_front("_");
+ StringRef name = sc->getSectionName().split('$').second;
prevailingSectionMap[name] = sectionNumber;
}
}
@@ -363,8 +362,7 @@ void ObjFile::recordPrevailingSymbolForMingw(
void ObjFile::maybeAssociateSEHForMingw(
COFFSymbolRef sym, const coff_aux_section_definition *def,
const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
if (name.consume_front(".pdata$") || name.consume_front(".xdata$") ||
name.consume_front(".eh_frame$")) {
// For MinGW, treat .[px]data$<func> and .eh_frame$<func> as implicitly
@@ -378,8 +376,7 @@ void ObjFile::maybeAssociateSEHForMingw(
Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
if (sym.isExternal()) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
if (sc)
return symtab->addRegular(this, name, sym.getGeneric(), sc,
sym.getValue());
@@ -447,8 +444,7 @@ void ObjFile::initializeSymbols() {
maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
log("comdat section " + name +
" without leader and unassociated, discarding");
continue;
@@ -461,11 +457,13 @@ void ObjFile::initializeSymbols() {
uint32_t idx = kv.second;
checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
}
+
+ // Free the memory used by sparseChunks now that symbol loading is finished.
+ decltype(sparseChunks)().swap(sparseChunks);
}
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
return symtab->addUndefined(name, this, sym.isWeakExternal());
}
@@ -561,8 +559,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
case IMAGE_COMDAT_SELECT_LARGEST:
if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) {
// Replace the existing comdat symbol with the new one.
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
// FIXME: This is incorrect: With /opt:noref, the previous sections
// make it into the final executable as well. Correct handling would
// be to undo reading of the whole old section that's being replaced,
@@ -586,11 +583,7 @@ Optional<Symbol *> ObjFile::createDefined(
std::vector<const coff_aux_section_definition *> &comdatDefs,
bool &prevailing) {
prevailing = false;
- auto getName = [&]() {
- StringRef s;
- coffObj->getSymbolName(sym, s);
- return s;
- };
+ auto getName = [&]() { return check(coffObj->getSymbolName(sym)); };
if (sym.isCommon()) {
auto *c = make<CommonChunk>(sym);
@@ -767,10 +760,11 @@ void ObjFile::initializeDependencies() {
if (data.empty())
return;
+ // Get the first type record. It will indicate if this object uses a type
+ // server (/Zi) or a PCH file (/Yu).
CVTypeArray types;
BinaryStreamReader reader(data, support::little);
cantFail(reader.readArray(types, reader.getLength()));
-
CVTypeArray::Iterator firstType = types.begin();
if (firstType == types.end())
return;
@@ -778,28 +772,120 @@ void ObjFile::initializeDependencies() {
// Remember the .debug$T or .debug$P section.
debugTypes = data;
+ // This object file is a PCH file that others will depend on.
if (isPCH) {
debugTypesObj = makePrecompSource(this);
return;
}
+ // This object file was compiled with /Zi. Enqueue the PDB dependency.
if (firstType->kind() == LF_TYPESERVER2) {
TypeServer2Record ts = cantFail(
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
- debugTypesObj = makeUseTypeServerSource(this, &ts);
+ debugTypesObj = makeUseTypeServerSource(this, ts);
+ PDBInputFile::enqueue(ts.getName(), this);
return;
}
+ // This object was compiled with /Yu. It uses types from another object file
+ // with a matching signature.
if (firstType->kind() == LF_PRECOMP) {
PrecompRecord precomp = cantFail(
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
- debugTypesObj = makeUsePrecompSource(this, &precomp);
+ debugTypesObj = makeUsePrecompSource(this, precomp);
return;
}
+ // This is a plain old object file.
debugTypesObj = makeTpiSource(this);
}
+// Make a PDB path assuming the PDB is in the same folder as the OBJ
+static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) {
+ StringRef localPath =
+ !file->parentName.empty() ? file->parentName : file->getName();
+ SmallString<128> path = sys::path::parent_path(localPath);
+
+ // Currently, type server PDBs are only created by MSVC cl, which only runs
+ // on Windows, so we can assume type server paths are Windows style.
+ sys::path::append(path,
+ sys::path::filename(tSPath, sys::path::Style::windows));
+ return std::string(path.str());
+}
+
+// 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
+// PDBInputFile::instances map, at least on Windows.
+static std::string normalizePdbPath(StringRef path) {
+#if defined(_WIN32)
+ return path.lower();
+#else // LINUX
+ return std::string(path);
+#endif
+}
+
+// If existing, return the actual PDB path on disk.
+static Optional<std::string> findPdbPath(StringRef pdbPath,
+ ObjFile *dependentFile) {
+ // Ensure the file exists before anything else. In some cases, if the path
+ // points to a removable device, Driver::enqueuePath() would fail with an
+ // error (EAGAIN, "resource unavailable try again") which we want to skip
+ // silently.
+ if (llvm::sys::fs::exists(pdbPath))
+ return normalizePdbPath(pdbPath);
+ std::string ret = getPdbBaseName(dependentFile, pdbPath);
+ if (llvm::sys::fs::exists(ret))
+ return normalizePdbPath(ret);
+ return None;
+}
+
+PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {}
+
+PDBInputFile::~PDBInputFile() = default;
+
+PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path,
+ ObjFile *fromFile) {
+ auto p = findPdbPath(path.str(), fromFile);
+ if (!p)
+ return nullptr;
+ auto it = PDBInputFile::instances.find(*p);
+ if (it != PDBInputFile::instances.end())
+ return it->second;
+ 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() {
+ PDBInputFile::instances[mb.getBufferIdentifier().str()] = this;
+
+ std::unique_ptr<pdb::IPDBSession> thisSession;
+ loadErr.emplace(pdb::NativeSession::createFromPdb(
+ MemoryBuffer::getMemBuffer(mb, false), thisSession));
+ if (*loadErr)
+ return; // fail silently at this point - the error will be handled later,
+ // when merging the debug type stream
+
+ session.reset(static_cast<pdb::NativeSession *>(thisSession.release()));
+
+ pdb::PDBFile &pdbFile = session->getPDBFile();
+ auto expectedInfo = pdbFile.getPDBInfoStream();
+ // All PDB Files should have an Info stream.
+ if (!expectedInfo) {
+ loadErr.emplace(expectedInfo.takeError());
+ return;
+ }
+ debugTypesObj = makeTypeServerSource(this);
+}
+
// Used only for DWARF debug info, which is not common (except in MinGW
// environments). This returns an optional pair of file name and line
// number for where the variable was defined.
@@ -831,7 +917,7 @@ Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
return dwarf->getDILineInfo(offset, sectionIndex);
}
-StringRef ltrim1(StringRef s, const char *chars) {
+static StringRef ltrim1(StringRef s, const char *chars) {
if (!s.empty() && strchr(chars, s[0]))
return s.substr(1);
return s;
@@ -849,7 +935,7 @@ void ImportFile::parse() {
StringRef name = saver.save(StringRef(buf + sizeof(*hdr)));
StringRef impName = saver.save("__imp_" + name);
const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1;
- dllName = StringRef(nameStart);
+ dllName = std::string(StringRef(nameStart));
StringRef extName;
switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
@@ -907,8 +993,9 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
// filename unique.
MemoryBufferRef mbref(
mb.getBuffer(),
- saver.save(archiveName + path +
- (archiveName.empty() ? "" : utostr(offsetInArchive))));
+ saver.save(archiveName.empty() ? path
+ : archiveName + sys::path::filename(path) +
+ utostr(offsetInArchive)));
obj = check(lto::InputFile::create(mbref));
}
@@ -932,7 +1019,7 @@ void BitcodeFile::parse() {
} else if (objSym.isWeak() && objSym.isIndirect()) {
// Weak external.
sym = symtab->addUndefined(symName, this, true);
- std::string fallback = objSym.getCOFFWeakExternalFallback();
+ std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
Symbol *alias = symtab->addUndefined(saver.save(fallback));
checkAndSetWeakAlias(symtab, this, sym, alias);
} else if (comdatIndex != -1) {
@@ -967,14 +1054,11 @@ MachineTypes BitcodeFile::getMachineType() {
}
}
-std::string replaceThinLTOSuffix(StringRef path) {
+std::string lld::coff::replaceThinLTOSuffix(StringRef path) {
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
StringRef repl = config->thinLTOObjectSuffixReplace.second;
if (path.consume_back(suffix))
return (path + repl).str();
- return path;
+ return std::string(path);
}
-
-} // namespace coff
-} // namespace lld
diff --git a/contrib/llvm-project/lld/COFF/InputFiles.h b/contrib/llvm-project/lld/COFF/InputFiles.h
index 805d9121d8bc..50323f596e2c 100644
--- a/contrib/llvm-project/lld/COFF/InputFiles.h
+++ b/contrib/llvm-project/lld/COFF/InputFiles.h
@@ -26,6 +26,7 @@ namespace llvm {
struct DILineInfo;
namespace pdb {
class DbiModuleDescriptorBuilder;
+class NativeSession;
}
namespace lto {
class InputFile;
@@ -64,6 +65,7 @@ public:
ArchiveKind,
ObjectKind,
LazyObjectKind,
+ PDBKind,
ImportKind,
BitcodeKind
};
@@ -140,7 +142,7 @@ public:
MachineTypes getMachineType() override;
ArrayRef<Chunk *> getChunks() { return chunks; }
ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
- ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; }
+ ArrayRef<SectionChunk *> getSXDataChunks() { return sxDataChunks; }
ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
@@ -276,29 +278,55 @@ private:
// Chunks containing symbol table indices of exception handlers. Only used for
// 32-bit x86.
- std::vector<SectionChunk *> sXDataChunks;
+ std::vector<SectionChunk *> sxDataChunks;
// Chunks containing symbol table indices of address taken symbols and longjmp
// targets. These are not linked into the final binary when /guard:cf is set.
std::vector<SectionChunk *> guardFidChunks;
std::vector<SectionChunk *> guardLJmpChunks;
- // This vector contains the same chunks as Chunks, but they are
- // indexed such that you can get a SectionChunk by section index.
- // Nonexistent section indices are filled with null pointers.
- // (Because section number is 1-based, the first slot is always a
- // null pointer.)
- std::vector<SectionChunk *> sparseChunks;
-
// This vector contains a list of all symbols defined or referenced by this
// file. They are indexed such that you can get a Symbol by symbol
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
std::vector<Symbol *> symbols;
+ // This vector contains the same chunks as Chunks, but they are
+ // indexed such that you can get a SectionChunk by section index.
+ // Nonexistent section indices are filled with null pointers.
+ // (Because section number is 1-based, the first slot is always a
+ // null pointer.) This vector is only valid during initialization.
+ std::vector<SectionChunk *> sparseChunks;
+
DWARFCache *dwarf = nullptr;
};
+// This is a PDB type server dependency, that is not a input file per se, but
+// needs to be treated like one. Such files are discovered from the debug type
+// stream.
+class PDBInputFile : public InputFile {
+public:
+ explicit PDBInputFile(MemoryBufferRef m);
+ ~PDBInputFile();
+ static bool classof(const InputFile *f) { return f->kind() == PDBKind; }
+ void parse() override;
+
+ static void enqueue(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
+ llvm::Optional<Error> loadErr;
+
+ // This is the actual interface to the PDB (if it was opened successfully)
+ std::unique_ptr<llvm::pdb::NativeSession> session;
+
+ // If the PDB has a .debug$T stream, this tells how it will be handled.
+ TpiSource *debugTypesObj = nullptr;
+};
+
// This type represents import library members that contain DLL names
// and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7
// for details about the format.
diff --git a/contrib/llvm-project/lld/COFF/LLDMapFile.cpp b/contrib/llvm-project/lld/COFF/LLDMapFile.cpp
new file mode 100644
index 000000000000..79df33a3535f
--- /dev/null
+++ b/contrib/llvm-project/lld/COFF/LLDMapFile.cpp
@@ -0,0 +1,123 @@
+//===- LLDMapFile.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the /lldmap option. It shows lists in order and
+// hierarchically the output sections, input sections, input files and
+// symbol:
+//
+// Address Size Align Out File Symbol
+// 00201000 00000015 4 .text
+// 00201000 0000000e 4 test.o:(.text)
+// 0020100e 00000000 0 local
+// 00201005 00000000 0 f(int)
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDMapFile.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
+
+using SymbolMapTy =
+ DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+
+static constexpr char indent8[] = " "; // 8 spaces
+static constexpr char indent16[] = " "; // 16 spaces
+
+// Print out the first three columns of a line.
+static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
+ uint64_t align) {
+ os << format("%08llx %08llx %5lld ", addr, size, align);
+}
+
+// Returns a list of all symbols that we want to print out.
+static std::vector<DefinedRegular *> getSymbols() {
+ std::vector<DefinedRegular *> v;
+ for (ObjFile *file : ObjFile::instances)
+ for (Symbol *b : file->getSymbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ if (sym && !sym->getCOFFSymbol().isSectionDefinition())
+ v.push_back(sym);
+ return v;
+}
+
+// Returns a map from sections to their symbols.
+static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
+ SymbolMapTy ret;
+ for (DefinedRegular *s : syms)
+ ret[s->getChunk()].push_back(s);
+
+ // Sort symbols by address.
+ for (auto &it : ret) {
+ SmallVectorImpl<DefinedRegular *> &v = it.second;
+ std::stable_sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
+ return a->getRVA() < b->getRVA();
+ });
+ }
+ return ret;
+}
+
+// Construct a map from symbols to their stringified representations.
+static DenseMap<DefinedRegular *, std::string>
+getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ writeHeader(os, syms[i]->getRVA(), 0, 0);
+ os << indent16 << toString(*syms[i]);
+ });
+
+ DenseMap<DefinedRegular *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
+}
+
+void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
+ if (config->lldmapFile.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
+ if (ec)
+ fatal("cannot open " + config->lldmapFile + ": " + ec.message());
+
+ // Collect symbol info that we want to print out.
+ std::vector<DefinedRegular *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+
+ // Print out the header line.
+ os << "Address Size Align Out In Symbol\n";
+
+ // Print out file contents.
+ for (OutputSection *sec : outputSections) {
+ writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
+ os << sec->name << '\n';
+
+ for (Chunk *c : sec->chunks) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
+ continue;
+
+ writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
+ os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
+ << ")\n";
+ for (DefinedRegular *sym : sectionSyms[sc])
+ os << symStr[sym] << '\n';
+ }
+ }
+}
diff --git a/contrib/llvm-project/lld/COFF/LLDMapFile.h b/contrib/llvm-project/lld/COFF/LLDMapFile.h
new file mode 100644
index 000000000000..b731293a8625
--- /dev/null
+++ b/contrib/llvm-project/lld/COFF/LLDMapFile.h
@@ -0,0 +1,21 @@
+//===- LLDMapFile.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_LLDMAPFILE_H
+#define LLD_COFF_LLDMAPFILE_H
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lld {
+namespace coff {
+class OutputSection;
+void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
+}
+}
+
+#endif
diff --git a/contrib/llvm-project/lld/COFF/LTO.cpp b/contrib/llvm-project/lld/COFF/LTO.cpp
index 1c21236dce2b..bb44819e60f8 100644
--- a/contrib/llvm-project/lld/COFF/LTO.cpp
+++ b/contrib/llvm-project/lld/COFF/LTO.cpp
@@ -38,9 +38,8 @@
using namespace llvm;
using namespace llvm::object;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
// Creates an empty file to and returns a raw_fd_ostream to write to it.
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
@@ -55,9 +54,9 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
}
static std::string getThinLTOOutputFile(StringRef path) {
- return lto::getThinLTOOutputFile(path,
- config->thinLTOPrefixReplace.first,
- config->thinLTOPrefixReplace.second);
+ return lto::getThinLTOOutputFile(
+ std::string(path), std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second));
}
static lto::Config createConfig() {
@@ -82,6 +81,7 @@ static lto::Config createConfig() {
c.CPU = getCPUStr();
c.MAttrs = getMAttrs();
c.CGOptLevel = args::getCGOptLevel(config->ltoo);
+ c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
if (config->saveTemps)
checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
@@ -99,10 +99,12 @@ BitcodeCompiler::BitcodeCompiler() {
if (config->thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
- config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second),
config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
- } else if (config->thinLTOJobs != 0) {
- backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ } else {
+ backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
@@ -143,7 +145,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
-std::vector<StringRef> BitcodeCompiler::compile() {
+std::vector<InputFile *> BitcodeCompiler::compile() {
unsigned maxTasks = ltoObj->getMaxTasks();
buf.resize(maxTasks);
files.resize(maxTasks);
@@ -187,25 +189,32 @@ std::vector<StringRef> BitcodeCompiler::compile() {
if (!config->ltoCache.empty())
pruneCache(config->ltoCache, config->ltoCachePolicy);
- std::vector<StringRef> ret;
+ std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i) {
- if (buf[i].empty())
+ // Assign unique names to LTO objects. This ensures they have unique names
+ // in the PDB if one is produced. The names should look like:
+ // - foo.exe.lto.obj
+ // - foo.exe.lto.1.obj
+ // - ...
+ StringRef ltoObjName =
+ saver.save(Twine(config->outputFile) + ".lto" +
+ (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
+
+ // Get the native object contents either from the cache or from memory. Do
+ // not use the cached MemoryBuffer directly, or the PDB will not be
+ // deterministic.
+ StringRef objBuf;
+ if (files[i])
+ objBuf = files[i]->getBuffer();
+ else
+ objBuf = buf[i];
+ if (objBuf.empty())
continue;
- if (config->saveTemps) {
- if (i == 0)
- saveBuffer(buf[i], config->outputFile + ".lto.obj");
- else
- saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj");
- }
- ret.emplace_back(buf[i].data(), buf[i].size());
- }
- for (std::unique_ptr<MemoryBuffer> &file : files)
- if (file)
- ret.push_back(file->getBuffer());
+ if (config->saveTemps)
+ saveBuffer(buf[i], ltoObjName);
+ ret.push_back(make<ObjFile>(MemoryBufferRef(objBuf, ltoObjName)));
+ }
return ret;
}
-
-} // namespace coff
-} // namespace lld
diff --git a/contrib/llvm-project/lld/COFF/LTO.h b/contrib/llvm-project/lld/COFF/LTO.h
index 2a0cfa061c95..a2b321df7901 100644
--- a/contrib/llvm-project/lld/COFF/LTO.h
+++ b/contrib/llvm-project/lld/COFF/LTO.h
@@ -45,7 +45,7 @@ public:
~BitcodeCompiler();
void add(BitcodeFile &f);
- std::vector<StringRef> compile();
+ std::vector<InputFile *> compile();
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
diff --git a/contrib/llvm-project/lld/COFF/MapFile.cpp b/contrib/llvm-project/lld/COFF/MapFile.cpp
index 0fea60aab99b..41e169ef56e2 100644
--- a/contrib/llvm-project/lld/COFF/MapFile.cpp
+++ b/contrib/llvm-project/lld/COFF/MapFile.cpp
@@ -6,16 +6,25 @@
//
//===----------------------------------------------------------------------===//
//
-// This file implements the /lldmap option. It shows lists in order and
-// hierarchically the output sections, input sections, input files and
-// symbol:
+// This file implements the /map option in the same format as link.exe
+// (based on observations)
//
-// Address Size Align Out File Symbol
-// 00201000 00000015 4 .text
-// 00201000 0000000e 4 test.o:(.text)
-// 0020100e 00000000 0 local
-// 00201005 00000000 0 f(int)
+// Header (program name, timestamp info, preferred load address)
//
+// Section list (Start = Section index:Base address):
+// Start Length Name Class
+// 0001:00001000 00000015H .text CODE
+//
+// Symbols list:
+// Address Publics by Value Rva + Base Lib:Object
+// 0001:00001000 main 0000000140001000 main.obj
+// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj
+//
+// entry point at 0001:00000360
+//
+// Static symbols
+//
+// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
//===----------------------------------------------------------------------===//
#include "MapFile.h"
@@ -23,71 +32,176 @@
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
+#include "lld/Common/Timer.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
-namespace lld {
-namespace 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);
-using SymbolMapTy =
- DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+// Print out the first two columns of a line.
+static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
+ os << format(" %04x:%08llx", sec, addr);
+}
-static constexpr char indent8[] = " "; // 8 spaces
-static constexpr char indent16[] = " "; // 16 spaces
+// Write the time stamp with the format used by link.exe
+// It seems identical to strftime with "%c" on msvc build, but we need a
+// locale-agnostic version.
+static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
+ constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+ constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec"};
+ tm *time = localtime(&tds);
+ os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
+ months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
+ time->tm_sec, time->tm_year + 1900);
+}
-// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
- uint64_t align) {
- os << format("%08llx %08llx %5lld ", addr, size, align);
+static void sortUniqueSymbols(std::vector<Defined *> &syms) {
+ // Build helper vector
+ using SortEntry = std::pair<Defined *, size_t>;
+ std::vector<SortEntry> v;
+ v.resize(syms.size());
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ v[i] = SortEntry(syms[i], i);
+
+ // Remove duplicate symbol pointers
+ parallelSort(v, std::less<SortEntry>());
+ auto end = std::unique(v.begin(), v.end(),
+ [](const SortEntry &a, const SortEntry &b) {
+ return a.first == b.first;
+ });
+ v.erase(end, v.end());
+
+ // Sort by RVA then original order
+ parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
+ // Add config->imageBase to avoid comparing "negative" RVAs.
+ // This can happen with symbols of Absolute kind
+ uint64_t rvaa = config->imageBase + a.first->getRVA();
+ uint64_t rvab = config->imageBase + b.first->getRVA();
+ return rvaa < rvab || (rvaa == rvab && a.second < b.second);
+ });
+
+ syms.resize(v.size());
+ for (size_t i = 0, e = v.size(); i < e; ++i)
+ syms[i] = v[i].first;
}
-// Returns a list of all symbols that we want to print out.
-static std::vector<DefinedRegular *> getSymbols() {
- std::vector<DefinedRegular *> v;
+// Returns the lists of all symbols that we want to print out.
+static void getSymbols(std::vector<Defined *> &syms,
+ std::vector<Defined *> &staticSyms) {
+
for (ObjFile *file : ObjFile::instances)
- for (Symbol *b : file->getSymbols())
- if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
- if (sym && !sym->getCOFFSymbol().isSectionDefinition())
- v.push_back(sym);
- return v;
-}
+ for (Symbol *b : file->getSymbols()) {
+ if (!b || !b->isLive())
+ continue;
+ if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
+ COFFSymbolRef symRef = sym->getCOFFSymbol();
+ if (!symRef.isSectionDefinition() &&
+ symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
+ if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
+ staticSyms.push_back(sym);
+ else
+ syms.push_back(sym);
+ }
+ } else if (auto *sym = dyn_cast<Defined>(b)) {
+ syms.push_back(sym);
+ }
+ }
+
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
+ continue;
+
+ if (!file->thunkSym)
+ continue;
+
+ if (!file->thunkLive)
+ continue;
+
+ if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
+ syms.push_back(thunkSym);
-// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
- SymbolMapTy ret;
- for (DefinedRegular *s : syms)
- ret[s->getChunk()].push_back(s);
-
- // Sort symbols by address.
- for (auto &it : ret) {
- SmallVectorImpl<DefinedRegular *> &v = it.second;
- std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
- return a->getRVA() < b->getRVA();
- });
+ if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
+ syms.push_back(impSym);
}
- return ret;
+
+ sortUniqueSymbols(syms);
+ sortUniqueSymbols(staticSyms);
}
// Construct a map from symbols to their stringified representations.
-static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+static DenseMap<Defined *, std::string>
+getSymbolStrings(ArrayRef<Defined *> syms) {
std::vector<std::string> str(syms.size());
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
- writeHeader(os, syms[i]->getRVA(), 0, 0);
- os << indent16 << toString(*syms[i]);
+ Defined *sym = syms[i];
+
+ uint16_t sectionIdx = 0;
+ uint64_t address = 0;
+ SmallString<128> fileDescr;
+
+ if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
+ address = absSym->getVA();
+ fileDescr = "<absolute>";
+ } else if (isa<DefinedSynthetic>(sym)) {
+ fileDescr = "<linker-defined>";
+ } else if (isa<DefinedCommon>(sym)) {
+ fileDescr = "<common>";
+ } else if (Chunk *chunk = sym->getChunk()) {
+ address = sym->getRVA();
+ if (OutputSection *sec = chunk->getOutputSection())
+ address -= sec->header.VirtualAddress;
+
+ sectionIdx = chunk->getOutputSectionIdx();
+
+ InputFile *file;
+ if (auto *impSym = dyn_cast<DefinedImportData>(sym))
+ file = impSym->file;
+ else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
+ file = thunkSym->wrappedSym->file;
+ else
+ file = sym->getFile();
+
+ if (file) {
+ if (!file->parentName.empty()) {
+ fileDescr = sys::path::filename(file->parentName);
+ sys::path::replace_extension(fileDescr, "");
+ fileDescr += ":";
+ }
+ fileDescr += sys::path::filename(file->getName());
+ }
+ }
+ writeHeader(os, sectionIdx, address);
+ os << " ";
+ os << left_justify(sym->getName(), 26);
+ os << " ";
+ os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
+ if (!fileDescr.empty()) {
+ os << " "; // FIXME : Handle "f" and "i" flags sometimes generated
+ // by link.exe in those spaces
+ os << fileDescr;
+ }
});
- DenseMap<DefinedRegular *, std::string> ret;
+ DenseMap<Defined *, std::string> ret;
for (size_t i = 0, e = syms.size(); i < e; ++i)
ret[syms[i]] = std::move(str[i]);
return ret;
}
-void writeMapFile(ArrayRef<OutputSection *> outputSections) {
+void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (config->mapFile.empty())
return;
@@ -96,32 +210,113 @@ void writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (ec)
fatal("cannot open " + config->mapFile + ": " + ec.message());
+ ScopedTimer t1(totalMapTimer);
+
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> syms = getSymbols();
- SymbolMapTy sectionSyms = getSectionSyms(syms);
- DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+ ScopedTimer t2(symbolGatherTimer);
+ std::vector<Defined *> syms;
+ std::vector<Defined *> staticSyms;
+ getSymbols(syms, staticSyms);
+ t2.stop();
- // Print out the header line.
- os << "Address Size Align Out In Symbol\n";
+ ScopedTimer t3(symbolStringsTimer);
+ DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
+ DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
+ t3.stop();
- // Print out file contents.
- for (OutputSection *sec : outputSections) {
- writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
- os << sec->name << '\n';
+ ScopedTimer t4(writeTimer);
+ SmallString<128> AppName = sys::path::filename(config->outputFile);
+ sys::path::replace_extension(AppName, "");
+
+ // Print out the file header
+ os << " " << AppName << "\n";
+ os << "\n";
+ os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
+ if (config->repro) {
+ os << "Repro mode";
+ } else {
+ writeFormattedTimestamp(os, config->timestamp);
+ }
+ os << ")\n";
+
+ os << "\n";
+ os << " Preferred load address is "
+ << format_hex_no_prefix(config->imageBase, 16) << "\n";
+ os << "\n";
+
+ // Print out section table.
+ os << " Start Length Name Class\n";
+
+ for (OutputSection *sec : outputSections) {
+ // Merge display of chunks with same sectionName
+ std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
for (Chunk *c : sec->chunks) {
auto *sc = dyn_cast<SectionChunk>(c);
if (!sc)
continue;
- writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
- os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
- << ")\n";
- for (DefinedRegular *sym : sectionSyms[sc])
- os << symStr[sym] << '\n';
+ if (ChunkRanges.empty() ||
+ c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
+ ChunkRanges.emplace_back(sc, sc);
+ } else {
+ ChunkRanges.back().second = sc;
+ }
+ }
+
+ const bool isCodeSection =
+ (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
+ StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
+
+ for (auto &cr : ChunkRanges) {
+ size_t size =
+ cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
+
+ auto address = cr.first->getRVA() - sec->header.VirtualAddress;
+ writeHeader(os, sec->sectionIndex, address);
+ os << " " << format_hex_no_prefix(size, 8) << "H";
+ os << " " << left_justify(cr.first->getSectionName(), 23);
+ os << " " << SectionClass;
+ os << '\n';
}
}
-}
-} // namespace coff
-} // namespace lld
+ // Print out the symbols table (without static symbols)
+ os << "\n";
+ os << " Address Publics by Value Rva+Base"
+ " Lib:Object\n";
+ os << "\n";
+ for (Defined *sym : syms)
+ os << symStr[sym] << '\n';
+
+ // Print out the entry point.
+ os << "\n";
+
+ uint16_t entrySecIndex = 0;
+ uint64_t entryAddress = 0;
+
+ if (!config->noEntry) {
+ Defined *entry = dyn_cast_or_null<Defined>(config->entry);
+ if (entry) {
+ Chunk *chunk = entry->getChunk();
+ entrySecIndex = chunk->getOutputSectionIdx();
+ entryAddress =
+ entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
+ }
+ }
+ os << " entry point at ";
+ os << format("%04x:%08llx", entrySecIndex, entryAddress);
+ os << "\n";
+
+ // Print out the static symbols
+ os << "\n";
+ os << " Static symbols\n";
+ os << "\n";
+ for (Defined *sym : staticSyms)
+ os << staticSymStr[sym] << '\n';
+
+ t4.stop();
+ t1.stop();
+}
diff --git a/contrib/llvm-project/lld/COFF/MinGW.cpp b/contrib/llvm-project/lld/COFF/MinGW.cpp
index 270cdaab4d9c..e24cdca6ee34 100644
--- a/contrib/llvm-project/lld/COFF/MinGW.cpp
+++ b/contrib/llvm-project/lld/COFF/MinGW.cpp
@@ -15,9 +15,8 @@
using namespace llvm;
using namespace llvm::COFF;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
AutoExporter::AutoExporter() {
excludeLibs = {
@@ -35,6 +34,11 @@ AutoExporter::AutoExporter() {
"libclang_rt.builtins-arm",
"libclang_rt.builtins-i386",
"libclang_rt.builtins-x86_64",
+ "libclang_rt.profile",
+ "libclang_rt.profile-aarch64",
+ "libclang_rt.profile-arm",
+ "libclang_rt.profile-i386",
+ "libclang_rt.profile-x86_64",
"libc++",
"libc++abi",
"libunwind",
@@ -58,6 +62,10 @@ AutoExporter::AutoExporter() {
"__builtin_",
// Artificial symbols such as .refptr
".",
+ // profile generate symbols
+ "__profc_",
+ "__profd_",
+ "__profvp_",
};
excludeSymbolSuffixes = {
@@ -147,7 +155,7 @@ bool AutoExporter::shouldExport(Defined *sym) const {
return !excludeObjects.count(fileName);
}
-void writeDefFile(StringRef name) {
+void lld::coff::writeDefFile(StringRef name) {
std::error_code ec;
raw_fd_ostream os(name, ec, sys::fs::OF_None);
if (ec)
@@ -165,6 +173,3 @@ void writeDefFile(StringRef name) {
os << "\n";
}
}
-
-} // namespace coff
-} // namespace lld
diff --git a/contrib/llvm-project/lld/COFF/Options.td b/contrib/llvm-project/lld/COFF/Options.td
index 468e4da7d054..087d53b5d2dd 100644
--- a/contrib/llvm-project/lld/COFF/Options.td
+++ b/contrib/llvm-project/lld/COFF/Options.td
@@ -16,6 +16,13 @@ multiclass B<string name, string help_on, string help_off> {
def _no : F<name#":no">, HelpText<help_off>;
}
+// Same as B<> above, but without help texts, for private undocumented
+// options.
+multiclass B_priv<string name> {
+ def "" : F<name>;
+ def _no : F<name#":no">;
+}
+
def align : P<"align", "Section alignment">;
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
@@ -64,7 +71,11 @@ def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
def pdb : P<"pdb", "PDB file path">;
+def pdbstripped : P<"pdbstripped", "Stripped PDB file path">;
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
+def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">,
+ MetaVarName<"<name>=<file>">,
+ HelpText<"Embed the contents of <file> in the PDB as named stream <name>">;
def section : P<"section", "Specify section attributes">;
def stack : P<"stack", "Size of the stack">;
def stub : P<"stub", "Specify DOS stub file">;
@@ -147,6 +158,8 @@ defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
defm appcontainer : B<"appcontainer",
"Image can only be run in an app container",
"Image can run outside an app container (default)">;
+defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
+ "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
"Disable ASLR (default when /fixed)">;
defm fixed : B<"fixed", "Disable base relocations",
@@ -178,6 +191,8 @@ def help : F<"help">;
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions
+defm auto_import : B_priv<"auto-import">;
+defm runtime_pseudo_reloc : B_priv<"runtime-pseudo-reloc">;
def end_lib : F<"end-lib">,
HelpText<"Ends group of objects treated as if they were in a library">;
def exclude_all_symbols : F<"exclude-all-symbols">;
@@ -189,6 +204,7 @@ def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">,
HelpText<"Add symbol as undefined, but allow it to remain undefined">;
def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
+def noseh : F<"noseh">;
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
"Base path used to make relative source file path absolute in PDB">;
@@ -216,13 +232,15 @@ def lto_obj_path : P<
"output native object for merged LTO unit to this path">;
def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+def threads
+ : P<"threads", "Number of threads. '1' disables multi-threading. By "
+ "default all available hardware threads are used">;
// Flags for debugging
def lldmap : F<"lldmap">;
def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
+def map : F<"map">;
+def map_file : Joined<["/", "-", "/?", "-?"], "map:">;
def show_timing : F<"time">;
def summary : F<"summary">;
diff --git a/contrib/llvm-project/lld/COFF/PDB.cpp b/contrib/llvm-project/lld/COFF/PDB.cpp
index f68c60a13254..49d04add5be0 100644
--- a/contrib/llvm-project/lld/COFF/PDB.cpp
+++ b/contrib/llvm-project/lld/COFF/PDB.cpp
@@ -16,7 +16,6 @@
#include "TypeMerger.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
@@ -27,11 +26,7 @@
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
-#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
-#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
-#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
-#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
@@ -63,12 +58,11 @@
using namespace llvm;
using namespace llvm::codeview;
+using namespace lld;
+using namespace lld::coff;
using llvm::object::coff_section;
-namespace lld {
-namespace coff {
-
static ExitOnError exitOnErr;
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
@@ -76,7 +70,7 @@ static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
static Timer typeMergingTimer("Type Merging", addObjectsTimer);
static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
-static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer);
+static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer);
static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
@@ -88,7 +82,7 @@ class PDBLinker {
public:
PDBLinker(SymbolTable *symtab)
- : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) {
+ : symtab(symtab), builder(bAlloc), tMerger(bAlloc) {
// 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
// order to maintain as much byte-for-byte compatibility as possible.
@@ -101,53 +95,26 @@ public:
/// Add natvis files specified on the command line.
void addNatvisFiles();
+ /// Add named streams specified on the command line.
+ void addNamedStreams();
+
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
+ /// Add every live, defined public symbol to the PDB.
+ void addPublicsToPDB();
+
/// Link info for each import file in the symbol table into the PDB.
void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
/// Link CodeView from a single object file into the target (output) PDB.
/// When a precompiled headers object is linked, its TPI map might be provided
/// externally.
- void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr);
-
- /// Produce a mapping from the type and item indices used in the object
- /// file to those in the destination PDB.
- ///
- /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
- /// and IPI from the type server PDB and return a map for it. Each unique type
- /// server PDB is merged at most once, so this may return an existing index
- /// mapping.
- ///
- /// If the object does not use a type server PDB (compiled with /Z7), we merge
- /// all the type and item records from the .debug$S stream and fill in the
- /// caller-provided objectIndexMap.
- Expected<const CVIndexMap &> mergeDebugT(ObjFile *file,
- CVIndexMap *objectIndexMap);
-
- /// Reads and makes available a PDB.
- Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *file);
-
- /// Merges a precompiled headers TPI map into the current TPI map. The
- /// precompiled headers object will also be loaded and remapped in the
- /// process.
- Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap);
-
- /// Reads and makes available a precompiled headers object.
- ///
- /// This is a requirement for objects compiled with cl.exe /Yu. In that
- /// case, the referenced object (which was compiled with /Yc) has to be loaded
- /// first. This is mainly because the current object's TPI stream has external
- /// references to the precompiled headers object.
- ///
- /// If the precompiled headers object was already loaded, this function will
- /// simply return its (remapped) TPI map.
- Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *file);
-
- /// Adds a precompiled headers object signature -> TPI mapping.
- std::pair<CVIndexMap &, bool /*already there*/>
- registerPrecompiledHeaders(uint32_t signature);
+ void addDebug(TpiSource *source);
+
+ const CVIndexMap *mergeTypeRecords(TpiSource *source, CVIndexMap *localMap);
+
+ void addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap);
void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
std::vector<ulittle32_t *> &stringTableRefs,
@@ -164,8 +131,6 @@ public:
void printStats();
private:
- BumpPtrAllocator alloc;
-
SymbolTable *symtab;
pdb::PDBFileBuilder builder;
@@ -178,24 +143,10 @@ private:
llvm::SmallString<128> nativePath;
- std::vector<pdb::SecMapEntry> sectionMap;
-
- /// Type index mappings of type server PDBs that we've loaded so far.
- std::map<codeview::GUID, CVIndexMap> typeServerIndexMappings;
-
- /// Type index mappings of precompiled objects type map that we've loaded so
- /// far.
- std::map<uint32_t, CVIndexMap> precompTypeIndexMappings;
-
// For statistics
uint64_t globalSymbols = 0;
uint64_t moduleSymbols = 0;
uint64_t publicSymbols = 0;
-
- // When showSummary is enabled, these are histograms of TPI and IPI records
- // keyed by type index.
- SmallVector<uint32_t, 0> tpiCounts;
- SmallVector<uint32_t, 0> ipiCounts;
};
class DebugSHandler {
@@ -205,24 +156,20 @@ class DebugSHandler {
ObjFile &file;
/// The result of merging type indices.
- const CVIndexMap &indexMap;
+ const CVIndexMap *indexMap;
/// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by
/// index from other records in the .debug$S section. All of these strings
/// need to be added to the global PDB string table, and all references to
/// these strings need to have their indices re-written to refer to the
/// global PDB string table.
- DebugStringTableSubsectionRef cVStrTab;
+ DebugStringTableSubsectionRef cvStrTab;
/// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to
/// by other records in the .debug$S section and need to be merged into the
/// PDB.
DebugChecksumsSubsectionRef checksums;
- /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per
- /// object file.
- DebugInlineeLinesSubsectionRef inlineeLines;
-
/// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of
/// these and they need not appear in any specific order. However, they
/// contain string table references which need to be re-written, so we
@@ -238,14 +185,13 @@ class DebugSHandler {
/// references.
std::vector<ulittle32_t *> stringTableReferences;
+ void mergeInlineeLines(const DebugSubsectionRecord &inlineeLines);
+
public:
- DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap)
+ DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap *indexMap)
: linker(linker), file(file), indexMap(indexMap) {}
- void handleDebugS(lld::coff::SectionChunk &debugS);
-
- std::shared_ptr<DebugInlineeLinesSubsection>
- mergeInlineeLines(DebugChecksumsSubsection *newChecksums);
+ void handleDebugS(ArrayRef<uint8_t> relocatedDebugContents);
void finish();
};
@@ -290,42 +236,6 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
fileName = std::move(absoluteFileName);
}
-// A COFF .debug$H section is currently a clang extension. This function checks
-// if a .debug$H section is in a format that we expect / understand, so that we
-// can ignore any sections which are coincidentally also named .debug$H but do
-// not contain a format we recognize.
-static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
- if (debugH.size() < sizeof(object::debug_h_header))
- return false;
- auto *header =
- reinterpret_cast<const object::debug_h_header *>(debugH.data());
- debugH = debugH.drop_front(sizeof(object::debug_h_header));
- return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
- header->Version == 0 &&
- header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
- (debugH.size() % 8 == 0);
-}
-
-static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
- SectionChunk *sec =
- SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
- if (!sec)
- return llvm::None;
- ArrayRef<uint8_t> contents = sec->getContents();
- if (!canUseDebugH(contents))
- return None;
- return contents;
-}
-
-static ArrayRef<GloballyHashedType>
-getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
- assert(canUseDebugH(debugH));
-
- debugH = debugH.drop_front(sizeof(object::debug_h_header));
- uint32_t count = debugH.size() / sizeof(GloballyHashedType);
- return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
-}
-
static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
TypeCollection &typeTable) {
// Start the TPI or IPI stream header.
@@ -340,281 +250,6 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
});
}
-Expected<const CVIndexMap &>
-PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) {
- ScopedTimer t(typeMergingTimer);
-
- if (!file->debugTypesObj)
- return *objectIndexMap; // no Types stream
-
- // Precompiled headers objects need to save the index map for further
- // reference by other objects which use the precompiled headers.
- if (file->debugTypesObj->kind == TpiSource::PCH) {
- uint32_t pchSignature = file->pchSignature.getValueOr(0);
- if (pchSignature == 0)
- fatal("No signature found for the precompiled headers OBJ (" +
- file->getName() + ")");
-
- // When a precompiled headers object comes first on the command-line, we
- // update the mapping here. Otherwise, if an object referencing the
- // precompiled headers object comes first, the mapping is created in
- // aquirePrecompObj(), thus we would skip this block.
- if (!objectIndexMap->isPrecompiledTypeMap) {
- auto r = registerPrecompiledHeaders(pchSignature);
- if (r.second)
- fatal(
- "A precompiled headers OBJ with the same signature was already "
- "provided! (" +
- file->getName() + ")");
-
- objectIndexMap = &r.first;
- }
- }
-
- if (file->debugTypesObj->kind == TpiSource::UsingPDB) {
- // Look through type servers. If we've already seen this type server,
- // don't merge any type information.
- return maybeMergeTypeServerPDB(file);
- }
-
- CVTypeArray types;
- BinaryStreamReader reader(file->debugTypes, support::little);
- cantFail(reader.readArray(types, reader.getLength()));
-
- if (file->debugTypesObj->kind == TpiSource::UsingPCH) {
- // This object was compiled with /Yu, so process the corresponding
- // precompiled headers object (/Yc) first. Some type indices in the current
- // object are referencing data in the precompiled headers object, so we need
- // both to be loaded.
- Error e = mergeInPrecompHeaderObj(file, objectIndexMap);
- if (e)
- return std::move(e);
-
- // Drop LF_PRECOMP record from the input stream, as it has been replaced
- // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
- // call above. Note that we can't just call Types.drop_front(), as we
- // explicitly want to rebase the stream.
- CVTypeArray::Iterator firstType = types.begin();
- types.setUnderlyingStream(
- types.getUnderlyingStream().drop_front(firstType->RecordData.size()));
- }
-
- // Fill in the temporary, caller-provided ObjectIndexMap.
- if (config->debugGHashes) {
- ArrayRef<GloballyHashedType> hashes;
- std::vector<GloballyHashedType> ownedHashes;
- if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
- hashes = getHashesFromDebugH(*debugH);
- else {
- ownedHashes = GloballyHashedType::hashTypes(types);
- hashes = ownedHashes;
- }
-
- if (auto err = mergeTypeAndIdRecords(
- tMerger.globalIDTable, tMerger.globalTypeTable,
- objectIndexMap->tpiMap, types, hashes, file->pchSignature))
- fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(err)));
- } else {
- if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable,
- objectIndexMap->tpiMap, types,
- file->pchSignature))
- fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(err)));
- }
-
- if (config->showSummary) {
- // Count how many times we saw each type record in our input. This
- // calculation requires a second pass over the type records to classify each
- // record as a type or index. This is slow, but this code executes when
- // collecting statistics.
- tpiCounts.resize(tMerger.getTypeTable().size());
- ipiCounts.resize(tMerger.getIDTable().size());
- uint32_t srcIdx = 0;
- for (CVType &ty : types) {
- TypeIndex dstIdx = objectIndexMap->tpiMap[srcIdx++];
- // Type merging may fail, so a complex source type may become the simple
- // NotTranslated type, which cannot be used as an array index.
- if (dstIdx.isSimple())
- continue;
- SmallVectorImpl<uint32_t> &counts =
- isIdRecord(ty.kind()) ? ipiCounts : tpiCounts;
- ++counts[dstIdx.toArrayIndex()];
- }
- }
-
- return *objectIndexMap;
-}
-
-Expected<const CVIndexMap &> PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) {
- Expected<llvm::pdb::NativeSession *> pdbSession = findTypeServerSource(file);
- if (!pdbSession)
- return pdbSession.takeError();
-
- pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile();
- pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
-
- auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap());
- CVIndexMap &indexMap = it.first->second;
- if (!it.second)
- return indexMap; // already merged
-
- // Mark this map as a type server map.
- indexMap.isTypeServerMap = true;
-
- Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
- if (auto e = expectedTpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(e)));
- pdb::TpiStream *maybeIpi = nullptr;
- if (pdbFile.hasPDBIpiStream()) {
- Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
- if (auto e = expectedIpi.takeError())
- fatal("Error getting type server IPI stream: " + toString(std::move(e)));
- maybeIpi = &*expectedIpi;
- }
-
- if (config->debugGHashes) {
- // PDBs do not actually store global hashes, so when merging a type server
- // PDB we have to synthesize global hashes. To do this, we first synthesize
- // global hashes for the TPI stream, since it is independent, then we
- // synthesize hashes for the IPI stream, using the hashes for the TPI stream
- // as inputs.
- auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
- Optional<uint32_t> endPrecomp;
- // Merge TPI first, because the IPI stream will reference type indices.
- if (auto err =
- mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap,
- expectedTpi->typeArray(), tpiHashes, endPrecomp))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
-
- // Merge IPI.
- if (maybeIpi) {
- auto ipiHashes =
- GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
- if (auto err =
- mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap,
- indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
- }
- } else {
- // Merge TPI first, because the IPI stream will reference type indices.
- if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap,
- expectedTpi->typeArray()))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
-
- // Merge IPI.
- if (maybeIpi) {
- if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap,
- indexMap.ipiMap, maybeIpi->typeArray()))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
- }
- }
-
- if (config->showSummary) {
- // Count how many times we saw each type record in our input. If a
- // destination type index is present in the source to destination type index
- // map, that means we saw it once in the input. Add it to our histogram.
- tpiCounts.resize(tMerger.getTypeTable().size());
- ipiCounts.resize(tMerger.getIDTable().size());
- for (TypeIndex ti : indexMap.tpiMap)
- if (!ti.isSimple())
- ++tpiCounts[ti.toArrayIndex()];
- for (TypeIndex ti : indexMap.ipiMap)
- if (!ti.isSimple())
- ++ipiCounts[ti.toArrayIndex()];
- }
-
- return indexMap;
-}
-
-Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file,
- CVIndexMap *objectIndexMap) {
- const PrecompRecord &precomp =
- retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
-
- Expected<const CVIndexMap &> e = aquirePrecompObj(file);
- if (!e)
- return e.takeError();
-
- const CVIndexMap &precompIndexMap = *e;
- assert(precompIndexMap.isPrecompiledTypeMap);
-
- if (precompIndexMap.tpiMap.empty())
- return Error::success();
-
- assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
- assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size());
- // Use the previously remapped index map from the precompiled headers.
- objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(),
- precompIndexMap.tpiMap.begin() +
- precomp.getTypesCount());
- return Error::success();
-}
-
-static bool equals_path(StringRef path1, StringRef path2) {
-#if defined(_WIN32)
- return path1.equals_lower(path2);
-#else
- return path1.equals(path2);
-#endif
-}
-// Find by name an OBJ provided on the command line
-static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
- uint32_t precompSignature) {
- for (ObjFile *f : ObjFile::instances) {
- StringRef currentFileName = sys::path::filename(f->getName());
-
- if (f->pchSignature.hasValue() &&
- f->pchSignature.getValue() == precompSignature &&
- equals_path(fileNameOnly, currentFileName))
- return f;
- }
- return nullptr;
-}
-
-std::pair<CVIndexMap &, bool /*already there*/>
-PDBLinker::registerPrecompiledHeaders(uint32_t signature) {
- auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()});
- CVIndexMap &indexMap = insertion.first->second;
- if (!insertion.second)
- return {indexMap, true};
- // Mark this map as a precompiled types map.
- indexMap.isPrecompiledTypeMap = true;
- return {indexMap, false};
-}
-
-Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
- const PrecompRecord &precomp =
- retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
-
- // First, check if we already loaded the precompiled headers object with this
- // signature. Return the type index mapping if we've already seen it.
- auto r = registerPrecompiledHeaders(precomp.getSignature());
- if (r.second)
- return r.first;
-
- CVIndexMap &indexMap = r.first;
-
- // 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,
- // the paths embedded in the OBJs are in the Windows format.
- SmallString<128> precompFileName = sys::path::filename(
- precomp.getPrecompFilePath(), sys::path::Style::windows);
-
- // link.exe requires that a precompiled headers object must always be provided
- // on the command-line, even if that's not necessary.
- auto precompFile =
- findObjWithPrecompSignature(precompFileName, precomp.Signature);
- if (!precompFile)
- return createFileError(
- precomp.getPrecompFilePath().str(),
- make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
-
- addObjFile(precompFile, &indexMap);
-
- return indexMap;
-}
-
static bool remapTypeIndex(TypeIndex &ti, ArrayRef<TypeIndex> typeIndexMap) {
if (ti.isSimple())
return true;
@@ -695,7 +330,7 @@ static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) {
/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
- TypeCollection &iDTable) {
+ TypeCollection &idTable) {
RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
SymbolKind kind = symbolKind(recordData);
@@ -725,11 +360,10 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
// Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
// in both cases we just need the second type index.
if (!ti->isSimple() && !ti->isNoneType()) {
- CVType funcIdData = iDTable.getType(*ti);
- SmallVector<TypeIndex, 2> indices;
- discoverTypeIndices(funcIdData, indices);
- assert(indices.size() == 2);
- *ti = indices[1];
+ CVType funcIdData = idTable.getType(*ti);
+ ArrayRef<uint8_t> tiBuf = funcIdData.data().slice(8, 4);
+ assert(tiBuf.size() == 4 && "corrupt LF_[MEM]FUNC_ID record");
+ *ti = *reinterpret_cast<const TypeIndex *>(tiBuf.data());
}
kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
@@ -795,6 +429,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) {
switch (sym.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_GTHREAD32:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
// since they are synthesized by the linker in response to S_GPROC32 and
// S_LPROC32, but if we do see them, don't put them in the module stream I
@@ -807,17 +442,18 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) {
return !isGlobalScope;
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
case SymbolKind::S_LDATA32:
+ case SymbolKind::S_LTHREAD32:
default:
return true;
}
}
-static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
+static bool symbolGoesInGlobalsStream(const CVSymbol &sym,
+ bool isFunctionScope) {
switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_GDATA32:
- // S_LDATA32 goes in both the module stream and the globals stream.
- case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GTHREAD32:
case SymbolKind::S_GPROC32:
case SymbolKind::S_LPROC32:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
@@ -826,9 +462,11 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
return true;
- // S_UDT records go in the globals stream if it is a global S_UDT.
+ // Records that go in the globals stream, unless they are function-local.
case SymbolKind::S_UDT:
- return isGlobalScope;
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_LTHREAD32:
+ return !isFunctionScope;
default:
return false;
}
@@ -840,6 +478,8 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex,
case SymbolKind::S_CONSTANT:
case SymbolKind::S_UDT:
case SymbolKind::S_GDATA32:
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
@@ -899,7 +539,7 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
MutableArrayRef<uint8_t> alignedSymbolMem;
if (needsRealignment) {
void *alignedData =
- alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
+ bAlloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
alignedSymbolMem = makeMutableArrayRef(
reinterpret_cast<uint8_t *>(alignedData), totalRealignedSize);
}
@@ -953,7 +593,7 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
// adding the symbol to the module since we may need to get the next
// symbol offset, and writing to the module's symbol stream will update
// that offset.
- if (symbolGoesInGlobalsStream(sym, scopes.empty())) {
+ if (symbolGoesInGlobalsStream(sym, !scopes.empty())) {
addGlobalSymbol(builder.getGsiBuilder(),
file->moduleDBI->getModuleIndex(), curSymOffset, sym);
++globalSymbols;
@@ -980,16 +620,6 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
file->moduleDBI->addSymbolsInBulk(bulkSymbols);
}
-// Allocate memory for a .debug$S / .debug$F section and relocate it.
-static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &alloc,
- SectionChunk &debugChunk) {
- uint8_t *buffer = alloc.Allocate<uint8_t>(debugChunk.getSize());
- assert(debugChunk.getOutputSectionIdx() == 0 &&
- "debug sections should not be in output sections");
- debugChunk.writeTo(buffer);
- return makeArrayRef(buffer, debugChunk.getSize());
-}
-
static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
OutputSection *os = c ? c->getOutputSection() : nullptr;
pdb::SectionContrib sc;
@@ -1027,15 +657,19 @@ translateStringTableIndex(uint32_t objIndex,
return pdbStrTable.insert(*expectedString);
}
-void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
- DebugSubsectionArray subsections;
-
- ArrayRef<uint8_t> relocatedDebugContents = SectionChunk::consumeDebugMagic(
- relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName());
+void DebugSHandler::handleDebugS(ArrayRef<uint8_t> relocatedDebugContents) {
+ relocatedDebugContents =
+ SectionChunk::consumeDebugMagic(relocatedDebugContents, ".debug$S");
+ DebugSubsectionArray subsections;
BinaryStreamReader reader(relocatedDebugContents, support::little);
exitOnErr(reader.readArray(subsections, relocatedDebugContents.size()));
+ // If there is no index map, use an empty one.
+ CVIndexMap tempIndexMap;
+ if (!indexMap)
+ indexMap = &tempIndexMap;
+
for (const DebugSubsectionRecord &ss : subsections) {
// Ignore subsections with the 'ignore' bit. Some versions of the Visual C++
// runtime have subsections with this bit set.
@@ -1044,9 +678,9 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!cVStrTab.valid() &&
+ assert(!cvStrTab.valid() &&
"Encountered multiple string table subsections!");
- exitOnErr(cVStrTab.initialize(ss.getRecordData()));
+ exitOnErr(cvStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
@@ -1060,9 +694,10 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
file.moduleDBI->addDebugSubsection(ss);
break;
case DebugSubsectionKind::InlineeLines:
- assert(!inlineeLines.valid() &&
- "Encountered multiple inlinee lines subsections!");
- exitOnErr(inlineeLines.initialize(ss.getRecordData()));
+ // The inlinee lines subsection also has file checksum table references
+ // that can be used directly, but it contains function id references that
+ // must be remapped.
+ mergeInlineeLines(ss);
break;
case DebugSubsectionKind::FrameData: {
// We need to re-write string table indices here, so save off all
@@ -1074,7 +709,7 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
break;
}
case DebugSubsectionKind::Symbols: {
- linker.mergeSymbolRecords(&file, indexMap, stringTableReferences,
+ linker.mergeSymbolRecords(&file, *indexMap, stringTableReferences,
ss.getRecordData());
break;
}
@@ -1114,39 +749,24 @@ getFileName(const DebugStringTableSubsectionRef &strings,
return strings.getString(offset);
}
-std::shared_ptr<DebugInlineeLinesSubsection>
-DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) {
- auto newInlineeLines = std::make_shared<DebugInlineeLinesSubsection>(
- *newChecksums, inlineeLines.hasExtraFiles());
+void DebugSHandler::mergeInlineeLines(
+ const DebugSubsectionRecord &inlineeSubsection) {
+ DebugInlineeLinesSubsectionRef inlineeLines;
+ exitOnErr(inlineeLines.initialize(inlineeSubsection.getRecordData()));
+ // Remap type indices in inlinee line records in place.
for (const InlineeSourceLine &line : inlineeLines) {
- TypeIndex inlinee = line.Header->Inlinee;
- uint32_t fileID = line.Header->FileID;
- uint32_t sourceLine = line.Header->SourceLineNum;
-
+ TypeIndex &inlinee = *const_cast<TypeIndex *>(&line.Header->Inlinee);
ArrayRef<TypeIndex> typeOrItemMap =
- indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap;
+ indexMap->isTypeServerMap ? indexMap->ipiMap : indexMap->tpiMap;
if (!remapTypeIndex(inlinee, typeOrItemMap)) {
- log("ignoring inlinee line record in " + file.getName() +
+ log("bad inlinee line record in " + file.getName() +
" with bad inlinee index 0x" + utohexstr(inlinee.getIndex()));
- continue;
- }
-
- SmallString<128> filename =
- exitOnErr(getFileName(cVStrTab, checksums, fileID));
- pdbMakeAbsolute(filename);
- newInlineeLines->addInlineSite(inlinee, filename, sourceLine);
-
- if (inlineeLines.hasExtraFiles()) {
- for (uint32_t extraFileId : line.ExtraFiles) {
- filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId));
- pdbMakeAbsolute(filename);
- newInlineeLines->addExtraFile(filename);
- }
}
}
- return newInlineeLines;
+ // Add the modified inlinee line subsection directly.
+ file.moduleDBI->addDebugSubsection(inlineeSubsection);
}
void DebugSHandler::finish() {
@@ -1155,7 +775,7 @@ void DebugSHandler::finish() {
// We should have seen all debug subsections across the entire object file now
// which means that if a StringTable subsection and Checksums subsection were
// present, now is the time to handle them.
- if (!cVStrTab.valid()) {
+ if (!cvStrTab.valid()) {
if (checksums.valid())
fatal(".debug$S sections with a checksums subsection must also contain a "
"string table subsection");
@@ -1173,77 +793,92 @@ void DebugSHandler::finish() {
for (codeview::FrameData fd : fds) {
fd.RvaStart += *reloc;
fd.FrameFunc =
- translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab);
+ translateStringTableIndex(fd.FrameFunc, cvStrTab, linker.pdbStrTab);
dbiBuilder.addNewFpoData(fd);
}
}
for (ulittle32_t *ref : stringTableReferences)
- *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab);
+ *ref = translateStringTableIndex(*ref, cvStrTab, linker.pdbStrTab);
// Make a new file checksum table that refers to offsets in the PDB-wide
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
- // subsections.
+ // subsections. The new checksum table must have the exact same layout and
+ // size as the original. Otherwise, the file references in the line and
+ // inlinee line tables will be incorrect.
auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
for (FileChecksumEntry &fc : checksums) {
SmallString<128> filename =
- exitOnErr(cVStrTab.getString(fc.FileNameOffset));
+ exitOnErr(cvStrTab.getString(fc.FileNameOffset));
pdbMakeAbsolute(filename);
exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
}
-
- // Rewrite inlinee item indices if present.
- if (inlineeLines.valid())
- file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get()));
+ assert(checksums.getArray().getUnderlyingStream().getLength() ==
+ newChecksums->calculateSerializedSize() &&
+ "file checksum table must have same layout");
file.moduleDBI->addDebugSubsection(std::move(newChecksums));
}
-void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
- if (file->mergedIntoPDB)
+static void warnUnusable(InputFile *f, Error e) {
+ if (!config->warnDebugInfoUnusable) {
+ consumeError(std::move(e));
return;
- file->mergedIntoPDB = true;
+ }
+ auto msg = "Cannot use debug info for '" + toString(f) + "' [LNK4099]";
+ if (e)
+ warn(msg + "\n>>> failed to load reference " + toString(std::move(e)));
+ else
+ warn(msg);
+}
+const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source,
+ CVIndexMap *localMap) {
+ ScopedTimer t(typeMergingTimer);
// Before we can process symbol substreams from .debug$S, we need to process
// type information, file checksums, and the string table. Add type info to
// the PDB first, so that we can get the map from object file type and item
// indices to PDB type and item indices.
- CVIndexMap objectIndexMap;
- auto indexMapResult =
- mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap);
+ Expected<const CVIndexMap *> r = source->mergeDebugT(&tMerger, localMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
- if (!indexMapResult) {
- if (!config->warnDebugInfoUnusable) {
- consumeError(indexMapResult.takeError());
- return;
- }
- warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" +
- ">>> failed to load reference " +
- StringRef(toString(indexMapResult.takeError())));
- return;
+ if (!r) {
+ warnUnusable(source->file, r.takeError());
+ return nullptr;
}
+ return *r;
+}
- ScopedTimer t(symbolMergingTimer);
+// Allocate memory for a .debug$S / .debug$F section and relocate it.
+static ArrayRef<uint8_t> relocateDebugChunk(SectionChunk &debugChunk) {
+ uint8_t *buffer = bAlloc.Allocate<uint8_t>(debugChunk.getSize());
+ assert(debugChunk.getOutputSectionIdx() == 0 &&
+ "debug sections should not be in output sections");
+ debugChunk.writeTo(buffer);
+ return makeArrayRef(buffer, debugChunk.getSize());
+}
+void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) {
+ ScopedTimer t(symbolMergingTimer);
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
- DebugSHandler dsh(*this, *file, *indexMapResult);
+ DebugSHandler dsh(*this, *file, indexMap);
// Now do all live .debug$S and .debug$F sections.
for (SectionChunk *debugChunk : file->getDebugChunks()) {
if (!debugChunk->live || debugChunk->getSize() == 0)
continue;
- if (debugChunk->getSectionName() == ".debug$S") {
- dsh.handleDebugS(*debugChunk);
+ bool isDebugS = debugChunk->getSectionName() == ".debug$S";
+ bool isDebugF = debugChunk->getSectionName() == ".debug$F";
+ if (!isDebugS && !isDebugF)
continue;
- }
- if (debugChunk->getSectionName() == ".debug$F") {
- ArrayRef<uint8_t> relocatedDebugContents =
- relocateDebugChunk(alloc, *debugChunk);
+ ArrayRef<uint8_t> relocatedDebugContents = relocateDebugChunk(*debugChunk);
+ if (isDebugS) {
+ dsh.handleDebugS(relocatedDebugContents);
+ } else if (isDebugF) {
FixedStreamArray<object::FpoData> fpoRecords;
BinaryStreamReader reader(relocatedDebugContents, support::little);
uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData);
@@ -1253,7 +888,6 @@ void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
// can just copy it.
for (const object::FpoData &fd : fpoRecords)
dbiBuilder.addOldFpoData(fd);
- continue;
}
}
@@ -1265,43 +899,54 @@ void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
// path to the object into the PDB. If this is a plain object, we make its
// path absolute. If it's an object in an archive, we make the archive path
// absolute.
-static void createModuleDBI(pdb::PDBFileBuilder &builder) {
+static void createModuleDBI(pdb::PDBFileBuilder &builder, ObjFile *file) {
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
SmallString<128> objName;
- for (ObjFile *file : ObjFile::instances) {
+ bool inArchive = !file->parentName.empty();
+ objName = inArchive ? file->parentName : file->getName();
+ pdbMakeAbsolute(objName);
+ StringRef modName = inArchive ? file->getName() : StringRef(objName);
- bool inArchive = !file->parentName.empty();
- objName = inArchive ? file->parentName : file->getName();
- pdbMakeAbsolute(objName);
- StringRef modName = inArchive ? file->getName() : StringRef(objName);
+ file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
+ file->moduleDBI->setObjFileName(objName);
- file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
- file->moduleDBI->setObjFileName(objName);
+ ArrayRef<Chunk *> chunks = file->getChunks();
+ uint32_t modi = file->moduleDBI->getModuleIndex();
- ArrayRef<Chunk *> chunks = file->getChunks();
- uint32_t modi = file->moduleDBI->getModuleIndex();
-
- for (Chunk *c : chunks) {
- auto *secChunk = dyn_cast<SectionChunk>(c);
- if (!secChunk || !secChunk->live)
- continue;
- pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
- file->moduleDBI->setFirstSectionContrib(sc);
- break;
- }
+ for (Chunk *c : chunks) {
+ auto *secChunk = dyn_cast<SectionChunk>(c);
+ if (!secChunk || !secChunk->live)
+ continue;
+ pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
+ file->moduleDBI->setFirstSectionContrib(sc);
+ break;
}
}
-static PublicSym32 createPublic(Defined *def) {
- PublicSym32 pub(SymbolKind::S_PUB32);
- pub.Name = def->getName();
+void PDBLinker::addDebug(TpiSource *source) {
+ CVIndexMap localMap;
+ const CVIndexMap *indexMap = mergeTypeRecords(source, &localMap);
+
+ if (source->kind == TpiSource::PDB)
+ return; // No symbols in TypeServer PDBs
+
+ addDebugSymbols(source->file, indexMap);
+}
+
+static pdb::BulkPublic createPublic(Defined *def) {
+ pdb::BulkPublic pub;
+ pub.Name = def->getName().data();
+ pub.NameLen = def->getName().size();
+
+ PublicSymFlags flags = PublicSymFlags::None;
if (auto *d = dyn_cast<DefinedCOFF>(def)) {
if (d->getCOFFSymbol().isFunctionDefinition())
- pub.Flags = PublicSymFlags::Function;
+ flags = PublicSymFlags::Function;
} else if (isa<DefinedImportThunk>(def)) {
- pub.Flags = PublicSymFlags::Function;
+ flags = PublicSymFlags::Function;
}
+ pub.setFlags(flags);
OutputSection *os = def->getChunk()->getOutputSection();
assert(os && "all publics should be in final image");
@@ -1315,10 +960,30 @@ static PublicSym32 createPublic(Defined *def) {
void PDBLinker::addObjectsToPDB() {
ScopedTimer t1(addObjectsTimer);
- createModuleDBI(builder);
+ // Create module descriptors
+ for_each(ObjFile::instances,
+ [&](ObjFile *obj) { createModuleDBI(builder, obj); });
- for (ObjFile *file : ObjFile::instances)
- addObjFile(file);
+ // Merge OBJs that do not have debug types
+ for_each(ObjFile::instances, [&](ObjFile *obj) {
+ if (obj->debugTypesObj)
+ return;
+ // Even if there're no types, still merge non-symbol .Debug$S and .Debug$F
+ // sections
+ addDebugSymbols(obj, nullptr);
+ });
+
+ // Merge dependencies
+ TpiSource::forEachSource([&](TpiSource *source) {
+ if (source->isDependency())
+ addDebug(source);
+ });
+
+ // Merge regular and dependent OBJs
+ TpiSource::forEachSource([&](TpiSource *source) {
+ if (!source->isDependency())
+ addDebug(source);
+ });
builder.getStringTableBuilder().setStrings(pdbStrTab);
t1.stop();
@@ -1328,13 +993,16 @@ void PDBLinker::addObjectsToPDB() {
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
t2.stop();
+}
- ScopedTimer t3(globalsLayoutTimer);
- // Compute the public and global symbols.
+void PDBLinker::addPublicsToPDB() {
+ ScopedTimer t3(publicsLayoutTimer);
+ // Compute the public symbols.
auto &gsiBuilder = builder.getGsiBuilder();
- std::vector<PublicSym32> publics;
+ std::vector<pdb::BulkPublic> publics;
symtab->forEachSymbol([&publics](Symbol *s) {
- // Only emit defined, live symbols that have a chunk.
+ // Only emit external, defined, live symbols that have a chunk. Static,
+ // non-external symbols do not appear in the symbol table.
auto *def = dyn_cast<Defined>(s);
if (def && def->isLive() && def->getChunk())
publics.push_back(createPublic(def));
@@ -1342,12 +1010,7 @@ void PDBLinker::addObjectsToPDB() {
if (!publics.empty()) {
publicSymbols = publics.size();
- // Sort the public symbols and add them to the stream.
- parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) {
- return l.Name < r.Name;
- });
- for (const PublicSym32 &pub : publics)
- gsiBuilder.addPublicSymbol(pub);
+ gsiBuilder.addPublicSymbols(std::move(publics));
}
}
@@ -1367,8 +1030,8 @@ void PDBLinker::printStats() {
print(ObjFile::instances.size(),
"Input OBJ files (expanded from all cmd-line inputs)");
- print(typeServerIndexMappings.size(), "PDB type server dependencies");
- print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies");
+ print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies");
+ print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies");
print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(),
"Merged TPI records");
print(pdbStrTab.size(), "Output PDB strings");
@@ -1388,6 +1051,8 @@ void PDBLinker::printStats() {
TypeIndex typeIndex;
uint64_t totalInputSize() const { return uint64_t(dupCount) * typeSize; }
bool operator<(const TypeSizeInfo &rhs) const {
+ if (totalInputSize() == rhs.totalInputSize())
+ return typeIndex < rhs.typeIndex;
return totalInputSize() < rhs.totalInputSize();
}
};
@@ -1420,8 +1085,8 @@ void PDBLinker::printStats() {
}
};
- printLargeInputTypeRecs("TPI", tpiCounts, tMerger.getTypeTable());
- printLargeInputTypeRecs("IPI", ipiCounts, tMerger.getIDTable());
+ printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable());
+ printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable());
message(buffer);
}
@@ -1438,6 +1103,19 @@ void PDBLinker::addNatvisFiles() {
}
}
+void PDBLinker::addNamedStreams() {
+ for (const auto &streamFile : config->namedStreams) {
+ const StringRef stream = streamFile.getKey(), file = streamFile.getValue();
+ ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
+ MemoryBuffer::getFile(file);
+ if (!dataOrErr) {
+ warn("Cannot open input file: " + file);
+ continue;
+ }
+ exitOnErr(builder.addNamedStream(stream, (*dataOrErr)->getBuffer()));
+ }
+}
+
static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) {
switch (machine) {
case COFF::IMAGE_FILE_MACHINE_AMD64:
@@ -1473,7 +1151,7 @@ static std::string quote(ArrayRef<StringRef> args) {
a.split(s, '"');
r.append(join(s, "\"\""));
} else {
- r.append(a);
+ r.append(std::string(a));
}
if (hasWS || hasQ)
r.push_back('"');
@@ -1508,8 +1186,7 @@ static void fillLinkerVerRecord(Compile3Sym &cs) {
}
static void addCommonLinkerModuleSymbols(StringRef path,
- pdb::DbiModuleDescriptorBuilder &mod,
- BumpPtrAllocator &allocator) {
+ pdb::DbiModuleDescriptorBuilder &mod) {
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
@@ -1536,17 +1213,16 @@ static void addCommonLinkerModuleSymbols(StringRef path,
ebs.Fields.push_back("cmd");
ebs.Fields.push_back(argStr);
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ons, allocator, CodeViewContainer::Pdb));
+ ons, bAlloc, CodeViewContainer::Pdb));
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cs, allocator, CodeViewContainer::Pdb));
+ cs, bAlloc, CodeViewContainer::Pdb));
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ebs, allocator, CodeViewContainer::Pdb));
+ ebs, bAlloc, CodeViewContainer::Pdb));
}
static void addLinkerModuleCoffGroup(PartialSection *sec,
pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os,
- BumpPtrAllocator &allocator) {
+ OutputSection &os) {
// If there's a section, there's at least one chunk
assert(!sec->chunks.empty());
const Chunk *firstChunk = *sec->chunks.begin();
@@ -1567,12 +1243,11 @@ static void addLinkerModuleCoffGroup(PartialSection *sec,
cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE;
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cgs, allocator, CodeViewContainer::Pdb));
+ cgs, bAlloc, CodeViewContainer::Pdb));
}
static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os,
- BumpPtrAllocator &allocator) {
+ OutputSection &os) {
SectionSym sym(SymbolRecordKind::SectionSym);
sym.Alignment = 12; // 2^12 = 4KB
sym.Characteristics = os.header.Characteristics;
@@ -1581,7 +1256,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
sym.Rva = os.getRVA();
sym.SectionNumber = os.sectionIndex;
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- sym, allocator, CodeViewContainer::Pdb));
+ sym, bAlloc, CodeViewContainer::Pdb));
// Skip COFF groups in MinGW because it adds a significant footprint to the
// PDB, due to each function being in its own section
@@ -1590,7 +1265,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
// Output COFF groups for individual chunks of this section.
for (PartialSection *sec : os.contribSections) {
- addLinkerModuleCoffGroup(sec, mod, os, allocator);
+ addLinkerModuleCoffGroup(sec, mod, os);
}
}
@@ -1657,18 +1332,18 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA();
mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ons, alloc, CodeViewContainer::Pdb));
+ ons, bAlloc, CodeViewContainer::Pdb));
mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cs, alloc, CodeViewContainer::Pdb));
+ cs, bAlloc, CodeViewContainer::Pdb));
SmallVector<SymbolScope, 4> scopes;
CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol(
- ts, alloc, CodeViewContainer::Pdb);
+ ts, bAlloc, CodeViewContainer::Pdb);
scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym);
mod->addSymbol(newSym);
- newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc,
+ newSym = codeview::SymbolSerializer::writeOneSymbol(es, bAlloc,
CodeViewContainer::Pdb);
scopeStackClose(scopes, mod->getNextSymbolOffset(), file);
@@ -1681,10 +1356,10 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
}
// Creates a PDB file.
-void createPDB(SymbolTable *symtab,
- ArrayRef<OutputSection *> outputSections,
- ArrayRef<uint8_t> sectionTable,
- llvm::codeview::DebugInfo *buildId) {
+void lld::coff::createPDB(SymbolTable *symtab,
+ ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId) {
ScopedTimer t1(totalPdbLinkTimer);
PDBLinker pdb(symtab);
@@ -1693,6 +1368,8 @@ void createPDB(SymbolTable *symtab,
pdb.addImportFilesToPDB(outputSections);
pdb.addSections(outputSections, sectionTable);
pdb.addNatvisFiles();
+ pdb.addNamedStreams();
+ pdb.addPublicsToPDB();
ScopedTimer t2(diskCommitTimer);
codeview::GUID guid;
@@ -1743,11 +1420,11 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
linkerModule.setPdbFilePathNI(pdbFilePathNI);
- addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc);
+ addCommonLinkerModuleSymbols(nativePath, linkerModule);
// Add section contributions. They must be ordered by ascending RVA.
for (OutputSection *os : outputSections) {
- addLinkerModuleSectionSymbol(linkerModule, *os, alloc);
+ addLinkerModuleSectionSymbol(linkerModule, *os);
for (Chunk *c : os->chunks) {
pdb::SectionContrib sc =
createSectionContrib(c, linkerModule.getModuleIndex());
@@ -1766,8 +1443,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
ArrayRef<object::coff_section> sections = {
(const object::coff_section *)sectionTable.data(),
sectionTable.size() / sizeof(object::coff_section)};
- sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections);
- dbiBuilder.setSectionMap(sectionMap);
+ dbiBuilder.createSectionMap(sections);
// Add COFF section header stream.
exitOnErr(
@@ -1801,7 +1477,7 @@ static uint32_t getSecrelReloc() {
// table are stored in the output arguments. Returns whether a line table was
// found.
static bool findLineTable(const SectionChunk *c, uint32_t addr,
- DebugStringTableSubsectionRef &cVStrTab,
+ DebugStringTableSubsectionRef &cvStrTab,
DebugChecksumsSubsectionRef &checksums,
DebugLinesSubsectionRef &lines,
uint32_t &offsetInLinetable) {
@@ -1833,9 +1509,9 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
for (const DebugSubsectionRecord &ss : subsections) {
switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!cVStrTab.valid() &&
+ assert(!cvStrTab.valid() &&
"Encountered multiple string table subsections!");
- exitOnErr(cVStrTab.initialize(ss.getRecordData()));
+ exitOnErr(cvStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
@@ -1871,7 +1547,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
break;
}
- if (cVStrTab.valid() && checksums.valid() && lines.header())
+ if (cvStrTab.valid() && checksums.valid() && lines.header())
return true;
}
}
@@ -1883,15 +1559,15 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
// offset into the given chunk and return them, or None if a line table was
// not found.
Optional<std::pair<StringRef, uint32_t>>
-getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
+lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
ExitOnError exitOnErr;
- DebugStringTableSubsectionRef cVStrTab;
+ DebugStringTableSubsectionRef cvStrTab;
DebugChecksumsSubsectionRef checksums;
DebugLinesSubsectionRef lines;
uint32_t offsetInLinetable;
- if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
+ if (!findLineTable(c, addr, cvStrTab, checksums, lines, offsetInLinetable))
return None;
Optional<uint32_t> nameIndex;
@@ -1905,7 +1581,7 @@ getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
lineNumber = li.getStartLine();
}
StringRef filename =
- exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
return std::make_pair(filename, *lineNumber);
}
nameIndex = entry.NameIndex;
@@ -1914,9 +1590,6 @@ getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
}
if (!nameIndex)
return None;
- StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ StringRef filename = exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
return std::make_pair(filename, *lineNumber);
}
-
-} // namespace coff
-} // namespace lld
diff --git a/contrib/llvm-project/lld/COFF/SymbolTable.cpp b/contrib/llvm-project/lld/COFF/SymbolTable.cpp
index 7072f4d8d0e3..173e32f628ef 100644
--- a/contrib/llvm-project/lld/COFF/SymbolTable.cpp
+++ b/contrib/llvm-project/lld/COFF/SymbolTable.cpp
@@ -136,12 +136,16 @@ getFileLine(const SectionChunk *c, uint32_t addr) {
// of all references to that symbol from that file. If no debug information is
// available, returns just the name of the file, else one string per actual
// reference as described in the debug info.
-std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
+// Returns up to maxStrings string descriptions, along with the total number of
+// locations found.
+static std::pair<std::vector<std::string>, size_t>
+getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) {
struct Location {
Symbol *sym;
std::pair<StringRef, uint32_t> fileLine;
};
std::vector<Location> locations;
+ size_t numLocations = 0;
for (Chunk *c : file->getChunks()) {
auto *sc = dyn_cast<SectionChunk>(c);
@@ -150,6 +154,10 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
for (const coff_relocation &r : sc->getRelocs()) {
if (r.SymbolTableIndex != symIndex)
continue;
+ numLocations++;
+ if (locations.size() >= maxStrings)
+ continue;
+
Optional<std::pair<StringRef, uint32_t>> fileLine =
getFileLine(sc, r.VirtualAddress);
Symbol *sym = getSymbol(sc, r.VirtualAddress);
@@ -160,8 +168,12 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
}
}
- if (locations.empty())
- return std::vector<std::string>({"\n>>> referenced by " + toString(file)});
+ if (maxStrings == 0)
+ return std::make_pair(std::vector<std::string>(), numLocations);
+
+ if (numLocations == 0)
+ return std::make_pair(
+ std::vector<std::string>{"\n>>> referenced by " + toString(file)}, 1);
std::vector<std::string> symbolLocations(locations.size());
size_t i = 0;
@@ -175,17 +187,26 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
if (loc.sym)
os << ":(" << toString(*loc.sym) << ')';
}
- return symbolLocations;
+ return std::make_pair(symbolLocations, numLocations);
}
-std::vector<std::string> getSymbolLocations(InputFile *file,
- uint32_t symIndex) {
+std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
+ return getSymbolLocations(file, symIndex, SIZE_MAX).first;
+}
+
+static std::pair<std::vector<std::string>, size_t>
+getSymbolLocations(InputFile *file, uint32_t symIndex, size_t maxStrings) {
if (auto *o = dyn_cast<ObjFile>(file))
- return getSymbolLocations(o, symIndex);
- if (auto *b = dyn_cast<BitcodeFile>(file))
- return getSymbolLocations(b);
+ return getSymbolLocations(o, symIndex, maxStrings);
+ if (auto *b = dyn_cast<BitcodeFile>(file)) {
+ std::vector<std::string> symbolLocations = getSymbolLocations(b);
+ size_t numLocations = symbolLocations.size();
+ if (symbolLocations.size() > maxStrings)
+ symbolLocations.resize(maxStrings);
+ return std::make_pair(symbolLocations, numLocations);
+ }
llvm_unreachable("unsupported file type passed to getSymbolLocations");
- return {};
+ return std::make_pair(std::vector<std::string>(), (size_t)0);
}
// For an undefined symbol, stores all files referencing it and the index of
@@ -204,21 +225,22 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
llvm::raw_string_ostream os(out);
os << "undefined symbol: " << toString(*undefDiag.sym);
- const size_t maxUndefReferences = 10;
- size_t i = 0, numRefs = 0;
+ const size_t maxUndefReferences = 3;
+ size_t numDisplayedRefs = 0, numRefs = 0;
for (const UndefinedDiag::File &ref : undefDiag.files) {
- std::vector<std::string> symbolLocations =
- getSymbolLocations(ref.file, ref.symIndex);
- numRefs += symbolLocations.size();
+ std::vector<std::string> symbolLocations;
+ size_t totalLocations = 0;
+ std::tie(symbolLocations, totalLocations) = getSymbolLocations(
+ ref.file, ref.symIndex, maxUndefReferences - numDisplayedRefs);
+
+ numRefs += totalLocations;
+ numDisplayedRefs += symbolLocations.size();
for (const std::string &s : symbolLocations) {
- if (i >= maxUndefReferences)
- break;
os << s;
- i++;
}
}
- if (i < numRefs)
- os << "\n>>> referenced " << numRefs - i << " more times";
+ if (numDisplayedRefs < numRefs)
+ os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
errorOrWarn(os.str());
}
@@ -438,7 +460,7 @@ void SymbolTable::resolveRemainingUndefines() {
if (name.contains("_PchSym_"))
continue;
- if (config->mingw && handleMinGWAutomaticImport(sym, name))
+ if (config->autoImport && handleMinGWAutomaticImport(sym, name))
continue;
// Remaining undefined symbols are not fatal if /force is specified.
@@ -789,20 +811,16 @@ Symbol *SymbolTable::addUndefined(StringRef name) {
return addUndefined(name, nullptr, false);
}
-std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
- lto.reset(new BitcodeCompiler);
- for (BitcodeFile *f : BitcodeFile::instances)
- lto->add(*f);
- return lto->compile();
-}
-
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFile::instances.empty())
return;
ScopedTimer t(ltoTimer);
- for (StringRef object : compileBitcodeFiles()) {
- auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp"));
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *f : BitcodeFile::instances)
+ lto->add(*f);
+ for (InputFile *newObj : lto->compile()) {
+ ObjFile *obj = cast<ObjFile>(newObj);
obj->parse();
ObjFile::instances.push_back(obj);
}
diff --git a/contrib/llvm-project/lld/COFF/SymbolTable.h b/contrib/llvm-project/lld/COFF/SymbolTable.h
index cd8a53dcecdc..870a7151fa8e 100644
--- a/contrib/llvm-project/lld/COFF/SymbolTable.h
+++ b/contrib/llvm-project/lld/COFF/SymbolTable.h
@@ -77,7 +77,6 @@ public:
// BitcodeFiles and add them to the symbol table. Called after all files are
// added and before the writer writes results to a file.
void addCombinedLTOObjects();
- std::vector<StringRef> compileBitcodeFiles();
// Creates an Undefined symbol for a given name.
Symbol *addUndefined(StringRef name);
diff --git a/contrib/llvm-project/lld/COFF/Symbols.cpp b/contrib/llvm-project/lld/COFF/Symbols.cpp
index 938c9c527ffa..60ff72aeb522 100644
--- a/contrib/llvm-project/lld/COFF/Symbols.cpp
+++ b/contrib/llvm-project/lld/COFF/Symbols.cpp
@@ -36,12 +36,12 @@ static std::string maybeDemangleSymbol(StringRef symName) {
StringRef demangleInput = prefixless;
if (config->machine == I386)
demangleInput.consume_front("_");
- std::string demangled = demangle(demangleInput);
+ std::string demangled = demangle(std::string(demangleInput));
if (demangled != demangleInput)
- return prefix + demangle(demangleInput);
+ return prefix + demangle(std::string(demangleInput));
return (prefix + prefixless).str();
}
- return symName;
+ return std::string(symName);
}
std::string toString(coff::Symbol &b) {
return maybeDemangleSymbol(b.getName());
@@ -52,23 +52,15 @@ std::string toCOFFString(const Archive::Symbol &b) {
namespace coff {
-StringRef Symbol::getName() {
- // COFF symbol names are read lazily for a performance reason.
- // Non-external symbol names are never used by the linker except for logging
- // or debugging. Their internal references are resolved not by name but by
- // symbol index. And because they are not external, no one can refer them by
- // name. Object files contain lots of non-external symbols, and creating
- // StringRefs for them (which involves lots of strlen() on the string table)
- // is a waste of time.
- if (nameData == nullptr) {
- auto *d = cast<DefinedCOFF>(this);
- StringRef nameStr;
- cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr);
- nameData = nameStr.data();
- nameSize = nameStr.size();
- assert(nameSize == nameStr.size() && "name length truncated");
- }
- return StringRef(nameData, nameSize);
+void Symbol::computeName() {
+ assert(nameData == nullptr &&
+ "should only compute the name once for DefinedCOFF symbols");
+ auto *d = cast<DefinedCOFF>(this);
+ StringRef nameStr =
+ check(cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym));
+ nameData = nameStr.data();
+ nameSize = nameStr.size();
+ assert(nameSize == nameStr.size() && "name length truncated");
}
InputFile *Symbol::getFile() {
diff --git a/contrib/llvm-project/lld/COFF/Symbols.h b/contrib/llvm-project/lld/COFF/Symbols.h
index a8e70320b995..1da4df366966 100644
--- a/contrib/llvm-project/lld/COFF/Symbols.h
+++ b/contrib/llvm-project/lld/COFF/Symbols.h
@@ -69,7 +69,18 @@ public:
Kind kind() const { return static_cast<Kind>(symbolKind); }
// Returns the symbol name.
- StringRef getName();
+ StringRef getName() {
+ // COFF symbol names are read lazily for a performance reason.
+ // Non-external symbol names are never used by the linker except for logging
+ // or debugging. Their internal references are resolved not by name but by
+ // symbol index. And because they are not external, no one can refer them by
+ // name. Object files contain lots of non-external symbols, and creating
+ // StringRefs for them (which involves lots of strlen() on the string table)
+ // is a waste of time.
+ if (nameData == nullptr)
+ computeName();
+ return StringRef(nameData, nameSize);
+ }
void replaceKeepingName(Symbol *other, size_t size);
@@ -84,6 +95,9 @@ public:
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
+private:
+ void computeName();
+
protected:
friend SymbolTable;
explicit Symbol(Kind k, StringRef n = "")
diff --git a/contrib/llvm-project/lld/COFF/TypeMerger.h b/contrib/llvm-project/lld/COFF/TypeMerger.h
index e2cfe668cfa5..858f55b6856d 100644
--- a/contrib/llvm-project/lld/COFF/TypeMerger.h
+++ b/contrib/llvm-project/lld/COFF/TypeMerger.h
@@ -20,7 +20,7 @@ namespace coff {
class TypeMerger {
public:
TypeMerger(llvm::BumpPtrAllocator &alloc)
- : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc),
+ : typeTable(alloc), idTable(alloc), globalTypeTable(alloc),
globalIDTable(alloc) {}
/// Get the type table or the global type table if /DEBUG:GHASH is enabled.
@@ -34,20 +34,25 @@ public:
inline llvm::codeview::TypeCollection &getIDTable() {
if (config->debugGHashes)
return globalIDTable;
- return iDTable;
+ return idTable;
}
/// Type records that will go into the PDB TPI stream.
llvm::codeview::MergingTypeTableBuilder typeTable;
/// Item records that will go into the PDB IPI stream.
- llvm::codeview::MergingTypeTableBuilder iDTable;
+ llvm::codeview::MergingTypeTableBuilder idTable;
/// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
llvm::codeview::GlobalTypeTableBuilder globalTypeTable;
/// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
llvm::codeview::GlobalTypeTableBuilder globalIDTable;
+
+ // When showSummary is enabled, these are histograms of TPI and IPI records
+ // keyed by type index.
+ SmallVector<uint32_t, 0> tpiCounts;
+ SmallVector<uint32_t, 0> ipiCounts;
};
/// Map from type index and item index in a type server PDB to the
@@ -62,4 +67,4 @@ struct CVIndexMap {
} // namespace coff
} // namespace lld
-#endif \ No newline at end of file
+#endif
diff --git a/contrib/llvm-project/lld/COFF/Writer.cpp b/contrib/llvm-project/lld/COFF/Writer.cpp
index fcad7739d300..082de5b8c1d6 100644
--- a/contrib/llvm-project/lld/COFF/Writer.cpp
+++ b/contrib/llvm-project/lld/COFF/Writer.cpp
@@ -10,13 +10,13 @@
#include "Config.h"
#include "DLL.h"
#include "InputFiles.h"
+#include "LLDMapFile.h"
#include "MapFile.h"
#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
@@ -41,9 +41,8 @@ using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
/* To re-generate DOSProgram:
$ cat > /tmp/DOSProgram.asm
@@ -92,7 +91,8 @@ namespace {
class DebugDirectoryChunk : public NonSectionChunk {
public:
- DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
+ DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
+ bool writeRepro)
: records(r), writeRepro(writeRepro) {}
size_t getSize() const override {
@@ -102,11 +102,11 @@ public:
void writeTo(uint8_t *b) const override {
auto *d = reinterpret_cast<debug_directory *>(b);
- for (const Chunk *record : records) {
- OutputSection *os = record->getOutputSection();
- uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA());
- fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(),
- record->getRVA(), offs);
+ for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
+ Chunk *c = record.second;
+ OutputSection *os = c->getOutputSection();
+ uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
+ fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
++d;
}
@@ -141,7 +141,7 @@ private:
}
mutable std::vector<support::ulittle32_t *> timeDateStamps;
- const std::vector<Chunk *> &records;
+ const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
bool writeRepro;
};
@@ -166,6 +166,17 @@ public:
mutable codeview::DebugInfo *buildId = nullptr;
};
+class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
+public:
+ ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}
+
+ size_t getSize() const override { return 4; }
+
+ void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }
+
+ uint32_t characteristics = 0;
+};
+
// PartialSection represents a group of chunks that contribute to an
// OutputSection. Collating a collection of PartialSections of same name and
// characteristics constitutes the OutputSection.
@@ -251,7 +262,7 @@ private:
bool setNoSEHCharacteristic = false;
DebugDirectoryChunk *debugDirectory = nullptr;
- std::vector<Chunk *> debugRecords;
+ std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
CVDebugRecordChunk *buildId = nullptr;
ArrayRef<uint8_t> sectionTable;
@@ -290,7 +301,7 @@ private:
static Timer codeLayoutTimer("Code Layout", Timer::root());
static Timer diskCommitTimer("Commit Output File", Timer::root());
-void writeResult() { Writer().run(); }
+void lld::coff::writeResult() { Writer().run(); }
void OutputSection::addChunk(Chunk *c) {
chunks.push_back(c);
@@ -622,6 +633,7 @@ void Writer::run() {
}
writeBuildId();
+ writeLLDMapFile(outputSections);
writeMapFile(outputSections);
if (errorCount())
@@ -921,8 +933,9 @@ void Writer::createMiscChunks() {
// Create Debug Information Chunks
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
- if (config->debug || config->repro) {
+ if (config->debug || config->repro || config->cetCompat) {
debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
+ debugDirectory->setAlignment(4);
debugInfoSec->addChunk(debugDirectory);
}
@@ -932,10 +945,20 @@ void Writer::createMiscChunks() {
// allowing a debugger to match a PDB and an executable. So we need it even
// if we're ultimately not going to write CodeView data to the PDB.
buildId = make<CVDebugRecordChunk>();
- debugRecords.push_back(buildId);
+ debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
+ }
- for (Chunk *c : debugRecords)
- debugInfoSec->addChunk(c);
+ if (config->cetCompat) {
+ ExtendedDllCharacteristicsChunk *extendedDllChars =
+ make<ExtendedDllCharacteristicsChunk>(
+ IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT);
+ debugRecords.push_back(
+ {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars});
+ }
+
+ if (debugRecords.size() > 0) {
+ for (std::pair<COFF::DebugType, Chunk *> r : debugRecords)
+ debugInfoSec->addChunk(r.second);
}
// Create SEH table. x86-only.
@@ -946,11 +969,11 @@ void Writer::createMiscChunks() {
if (config->guardCF != GuardCFLevel::Off)
createGuardCFTables();
- if (config->mingw) {
+ if (config->autoImport)
createRuntimePseudoRelocs();
+ if (config->mingw)
insertCtorDtorSymbols();
- }
}
// Create .idata section for the DLL-imported symbol table.
@@ -1370,7 +1393,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;
if (config->integrityCheck)
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;
- if (setNoSEHCharacteristic)
+ if (setNoSEHCharacteristic || config->noSEH)
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
if (config->terminalServerAware)
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
@@ -1699,6 +1722,15 @@ void Writer::createRuntimePseudoRelocs() {
sc->getRuntimePseudoRelocs(rels);
}
+ if (!config->pseudoRelocs) {
+ // Not writing any pseudo relocs; if some were needed, error out and
+ // indicate what required them.
+ for (const RuntimePseudoReloc &rpr : rels)
+ error("automatic dllimport of " + rpr.sym->getName() + " in " +
+ toString(rpr.target->file) + " requires pseudo relocations");
+ return;
+ }
+
if (!rels.empty())
log("Writing " + Twine(rels.size()) + " runtime pseudo relocations");
PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(rels);
@@ -1832,6 +1864,10 @@ void Writer::sortExceptionTable() {
uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
if (config->machine == AMD64) {
struct Entry { ulittle32_t begin, end, unwind; };
+ if ((end - begin) % sizeof(Entry) != 0) {
+ fatal("unexpected .pdata size: " + Twine(end - begin) +
+ " is not a multiple of " + Twine(sizeof(Entry)));
+ }
parallelSort(
MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
@@ -1839,6 +1875,10 @@ void Writer::sortExceptionTable() {
}
if (config->machine == ARMNT || config->machine == ARM64) {
struct Entry { ulittle32_t begin, unwind; };
+ if ((end - begin) % sizeof(Entry) != 0) {
+ fatal("unexpected .pdata size: " + Twine(end - begin) +
+ " is not a multiple of " + Twine(sizeof(Entry)));
+ }
parallelSort(
MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
@@ -1950,6 +1990,3 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
return it->second;
return nullptr;
}
-
-} // namespace coff
-} // namespace lld
diff --git a/contrib/llvm-project/lld/Common/ErrorHandler.cpp b/contrib/llvm-project/lld/Common/ErrorHandler.cpp
index b6066b557cbf..94ff23173d69 100644
--- a/contrib/llvm-project/lld/Common/ErrorHandler.cpp
+++ b/contrib/llvm-project/lld/Common/ErrorHandler.cpp
@@ -8,7 +8,7 @@
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -62,8 +62,11 @@ void lld::exitLld(int val) {
// avoid intermittent crashes on Windows when exiting.
llvm_shutdown();
- lld::outs().flush();
- lld::errs().flush();
+ {
+ std::lock_guard<std::mutex> lock(mu);
+ lld::outs().flush();
+ lld::errs().flush();
+ }
_exit(val);
}
@@ -114,7 +117,7 @@ void lld::checkError(Error e) {
// extracted from an error message using regexps.
std::string ErrorHandler::getLocation(const Twine &msg) {
if (!vsDiagnostics)
- return logName;
+ return std::string(logName);
static std::regex regexes[] = {
std::regex(
@@ -146,7 +149,7 @@ std::string ErrorHandler::getLocation(const Twine &msg) {
return m.str(1) + "(" + m.str(2) + ")";
}
- return logName;
+ return std::string(logName);
}
void ErrorHandler::log(const Twine &msg) {
@@ -191,20 +194,26 @@ void ErrorHandler::error(const Twine &msg) {
}
}
- std::lock_guard<std::mutex> lock(mu);
+ bool exit = false;
+ {
+ std::lock_guard<std::mutex> lock(mu);
+
+ if (errorLimit == 0 || errorCount < errorLimit) {
+ lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
+ << "error: " << Colors::RESET << msg << "\n";
+ } else if (errorCount == errorLimit) {
+ lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
+ << "error: " << Colors::RESET << errorLimitExceededMsg
+ << "\n";
+ exit = exitEarly;
+ }
- if (errorLimit == 0 || errorCount < errorLimit) {
- lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
- << "error: " << Colors::RESET << msg << "\n";
- } else if (errorCount == errorLimit) {
- lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
- << "error: " << Colors::RESET << errorLimitExceededMsg << "\n";
- if (exitEarly)
- exitLld(1);
+ sep = getSeparator(msg);
+ ++errorCount;
}
- sep = getSeparator(msg);
- ++errorCount;
+ if (exit)
+ exitLld(1);
}
void ErrorHandler::fatal(const Twine &msg) {
diff --git a/contrib/llvm-project/lld/Common/Filesystem.cpp b/contrib/llvm-project/lld/Common/Filesystem.cpp
index 75e88dbce1ab..671b352a3f6b 100644
--- a/contrib/llvm-project/lld/Common/Filesystem.cpp
+++ b/contrib/llvm-project/lld/Common/Filesystem.cpp
@@ -11,10 +11,11 @@
//===----------------------------------------------------------------------===//
#include "lld/Common/Filesystem.h"
-#include "lld/Common/Threads.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Path.h"
#if LLVM_ON_UNIX
#include <unistd.h>
#endif
@@ -39,12 +40,41 @@ using namespace lld;
// This function spawns a background thread to remove the file.
// The calling thread returns almost immediately.
void lld::unlinkAsync(StringRef path) {
+ if (!sys::fs::exists(path) || !sys::fs::is_regular_file(path))
+ return;
+
// Removing a file is async on windows.
#if defined(_WIN32)
+ // On Windows co-operative programs can be expected to open LLD's
+ // output in FILE_SHARE_DELETE mode. This allows us to delete the
+ // file (by moving it to a temporary filename and then deleting
+ // it) so that we can link another output file that overwrites
+ // the existing file, even if the current file is in use.
+ //
+ // This is done on a best effort basis - we do not error if the
+ // operation fails. The consequence is merely that the user
+ // experiences an inconvenient work-flow.
+ //
+ // The code here allows LLD to work on all versions of Windows.
+ // However, at Windows 10 1903 it seems that the behavior of
+ // Windows has changed, so that we could simply delete the output
+ // file. This code should be simplified once support for older
+ // versions of Windows is dropped.
+ //
+ // Warning: It seems that the WINVER and _WIN32_WINNT preprocessor
+ // defines affect the behavior of the Windows versions of the calls
+ // we are using here. If this code stops working this is worth
+ // bearing in mind.
+ SmallString<128> tmpName;
+ if (!sys::fs::createUniqueFile(path + "%%%%%%%%.tmp", tmpName)) {
+ if (!sys::fs::rename(path, tmpName))
+ path = tmpName;
+ else
+ sys::fs::remove(tmpName);
+ }
sys::fs::remove(path);
#else
- if (!threadsEnabled || !sys::fs::exists(path) ||
- !sys::fs::is_regular_file(path))
+ if (parallel::strategy.ThreadsRequested == 1)
return;
// We cannot just remove path from a different thread because we are now going
diff --git a/contrib/llvm-project/lld/Common/Reproduce.cpp b/contrib/llvm-project/lld/Common/Reproduce.cpp
index 24210c420418..00309f58b93f 100644
--- a/contrib/llvm-project/lld/Common/Reproduce.cpp
+++ b/contrib/llvm-project/lld/Common/Reproduce.cpp
@@ -24,7 +24,7 @@ using namespace llvm::sys;
std::string lld::relativeToRoot(StringRef path) {
SmallString<128> abs = path;
if (fs::make_absolute(abs))
- return path;
+ return std::string(path);
path::remove_dots(abs, /*remove_dot_dot=*/true);
// This is Windows specific. root_name() returns a drive letter
@@ -45,13 +45,13 @@ std::string lld::relativeToRoot(StringRef path) {
std::string lld::quote(StringRef s) {
if (s.contains(' '))
return ("\"" + s + "\"").str();
- return s;
+ return std::string(s);
}
// Converts an Arg to a string representation suitable for a response file.
// To show an Arg in a diagnostic, use Arg::getAsString() instead.
std::string lld::toString(const opt::Arg &arg) {
- std::string k = arg.getSpelling();
+ std::string k = std::string(arg.getSpelling());
if (arg.getNumValues() == 0)
return k;
std::string v = quote(arg.getValue());
diff --git a/contrib/llvm-project/lld/Common/Strings.cpp b/contrib/llvm-project/lld/Common/Strings.cpp
index 627435f141da..17c2c207491f 100644
--- a/contrib/llvm-project/lld/Common/Strings.cpp
+++ b/contrib/llvm-project/lld/Common/Strings.cpp
@@ -10,6 +10,7 @@
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GlobPattern.h"
#include <algorithm>
#include <mutex>
@@ -26,23 +27,33 @@ std::string lld::demangleItanium(StringRef name) {
// does not look like a C++ symbol name to avoid getting unexpected
// result for a C symbol that happens to match a mangled type name.
if (!name.startswith("_Z"))
- return name;
+ return std::string(name);
- return demangle(name);
+ return demangle(std::string(name));
}
-StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
- for (StringRef s : pat) {
- Expected<GlobPattern> pat = GlobPattern::create(s);
- if (!pat)
- error(toString(pat.takeError()));
- else
- patterns.push_back(*pat);
+SingleStringMatcher::SingleStringMatcher(StringRef Pattern) {
+ if (Pattern.size() > 2 && Pattern.startswith("\"") &&
+ Pattern.endswith("\"")) {
+ ExactMatch = true;
+ ExactPattern = Pattern.substr(1, Pattern.size() - 2);
+ } else {
+ Expected<GlobPattern> Glob = GlobPattern::create(Pattern);
+ if (!Glob) {
+ error(toString(Glob.takeError()));
+ return;
+ }
+ ExactMatch = false;
+ GlobPatternMatcher = *Glob;
}
}
+bool SingleStringMatcher::match(StringRef s) const {
+ return ExactMatch ? (ExactPattern == s) : GlobPatternMatcher.match(s);
+}
+
bool StringMatcher::match(StringRef s) const {
- for (const GlobPattern &pat : patterns)
+ for (const SingleStringMatcher &pat : patterns)
if (pat.match(s))
return true;
return false;
diff --git a/contrib/llvm-project/lld/Common/TargetOptionsCommandFlags.cpp b/contrib/llvm-project/lld/Common/TargetOptionsCommandFlags.cpp
index 0137feb63f37..9b166a3e130a 100644
--- a/contrib/llvm-project/lld/Common/TargetOptionsCommandFlags.cpp
+++ b/contrib/llvm-project/lld/Common/TargetOptionsCommandFlags.cpp
@@ -5,35 +5,26 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
-// This file exists as a place for global variables defined in LLVM's
-// CodeGen/CommandFlags.inc. By putting the resulting object file in
-// an archive and linking with it, the definitions will automatically be
-// included when needed and skipped when already present.
-//
-//===----------------------------------------------------------------------===//
#include "lld/Common/TargetOptionsCommandFlags.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/Target/TargetOptions.h"
-// Define an externally visible version of
-// initTargetOptionsFromCodeGenFlags, so that its functionality can be
-// used without having to include llvm/CodeGen/CommandFlags.inc, which
-// would lead to multiple definitions of the command line flags.
+static llvm::codegen::RegisterCodeGenFlags CGF;
+
llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
- return ::InitTargetOptionsFromCodeGenFlags();
+ return llvm::codegen::InitTargetOptionsFromCodeGenFlags();
}
llvm::Optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
- return getRelocModel();
+ return llvm::codegen::getExplicitRelocModel();
}
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
- return getCodeModel();
+ return llvm::codegen::getExplicitCodeModel();
}
-std::string lld::getCPUStr() { return ::getCPUStr(); }
+std::string lld::getCPUStr() { return llvm::codegen::getCPUStr(); }
-std::vector<std::string> lld::getMAttrs() { return ::MAttrs; }
+std::vector<std::string> lld::getMAttrs() { return llvm::codegen::getMAttrs(); }
diff --git a/contrib/llvm-project/lld/Common/Timer.cpp b/contrib/llvm-project/lld/Common/Timer.cpp
index 4b7d11003b2c..ea221fd86f3e 100644
--- a/contrib/llvm-project/lld/Common/Timer.cpp
+++ b/contrib/llvm-project/lld/Common/Timer.cpp
@@ -13,29 +13,22 @@
using namespace lld;
using namespace llvm;
-ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); }
+ScopedTimer::ScopedTimer(Timer &t) : t(&t) {
+ startTime = std::chrono::high_resolution_clock::now();
+}
void ScopedTimer::stop() {
if (!t)
return;
- t->stop();
+ t->addToTotal(std::chrono::high_resolution_clock::now() - startTime);
t = nullptr;
}
ScopedTimer::~ScopedTimer() { stop(); }
-Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {}
-Timer::Timer(llvm::StringRef name, Timer &parent)
- : name(name), parent(&parent) {}
-
-void Timer::start() {
- if (parent && total.count() == 0)
- parent->children.push_back(this);
- startTime = std::chrono::high_resolution_clock::now();
-}
-
-void Timer::stop() {
- total += (std::chrono::high_resolution_clock::now() - startTime);
+Timer::Timer(llvm::StringRef name) : name(std::string(name)) {}
+Timer::Timer(llvm::StringRef name, Timer &parent) : name(std::string(name)) {
+ parent.children.push_back(this);
}
Timer &Timer::root() {
@@ -49,7 +42,8 @@ void Timer::print() {
// We want to print the grand total under all the intermediate phases, so we
// print all children first, then print the total under that.
for (const auto &child : children)
- child->print(1, totalDuration);
+ if (child->total > 0)
+ child->print(1, totalDuration);
message(std::string(49, '-'));
@@ -58,7 +52,7 @@ void Timer::print() {
double Timer::millis() const {
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
- total)
+ std::chrono::nanoseconds(total))
.count();
}
@@ -74,6 +68,7 @@ void Timer::print(int depth, double totalDuration, bool recurse) const {
if (recurse) {
for (const auto &child : children)
- child->print(depth + 1, totalDuration);
+ if (child->total > 0)
+ child->print(depth + 1, totalDuration);
}
}
diff --git a/contrib/llvm-project/lld/Common/Version.cpp b/contrib/llvm-project/lld/Common/Version.cpp
index ae10f2f28b22..cd9fcd4f4059 100644
--- a/contrib/llvm-project/lld/Common/Version.cpp
+++ b/contrib/llvm-project/lld/Common/Version.cpp
@@ -19,9 +19,16 @@
// Returns a version string, e.g.:
// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef)
std::string lld::getLLDVersion() {
+#ifdef LLD_VENDOR
+#define LLD_VENDOR_DISPLAY LLD_VENDOR " "
+#else
+#define LLD_VENDOR_DISPLAY
+#endif
#if defined(LLD_REPOSITORY) && defined(LLD_REVISION)
- return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")";
+ return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY
+ " " LLD_REVISION ")";
#else
- return "LLD " LLD_VERSION_STRING;
+ return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING;
#endif
+#undef LLD_VENDOR_DISPLAY
}
diff --git a/contrib/llvm-project/lld/ELF/AArch64ErrataFix.cpp b/contrib/llvm-project/lld/ELF/AArch64ErrataFix.cpp
index 398320af71e3..724d668449b7 100644
--- a/contrib/llvm-project/lld/ELF/AArch64ErrataFix.cpp
+++ b/contrib/llvm-project/lld/ELF/AArch64ErrataFix.cpp
@@ -44,9 +44,8 @@ using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Helper functions to identify instructions and conditions needed to trigger
// the Cortex-A53-843419 erratum.
@@ -371,7 +370,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
return patchOff;
}
-class Patch843419Section : public SyntheticSection {
+class elf::Patch843419Section : public SyntheticSection {
public:
Patch843419Section(InputSection *p, uint64_t off);
@@ -421,7 +420,7 @@ void Patch843419Section::writeTo(uint8_t *buf) {
// Return address is the next instruction after the one we have just copied.
uint64_t s = getLDSTAddr() + 4;
uint64_t p = patchSym->getVA() + 4;
- target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p);
+ target->relocateNoSym(buf + 4, R_AARCH64_JUMP26, s - p);
}
void AArch64Err843419Patcher::init() {
@@ -645,5 +644,3 @@ bool AArch64Err843419Patcher::createFixes() {
}
return addressesChanged;
}
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/ARMErrataFix.cpp b/contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
index 91cd2b5a2f5f..bd6f689b5844 100644
--- a/contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
+++ b/contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
@@ -33,9 +33,8 @@ using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// The documented title for Erratum 657417 is:
// "A 32bit branch instruction that spans two 4K regions can result in an
@@ -71,7 +70,7 @@ namespace elf {
// 00001002 2 - bytes padding
// 00001004 __CortexA8657417_00000FFE: B.w func
-class Patch657417Section : public SyntheticSection {
+class elf::Patch657417Section : public SyntheticSection {
public:
Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM);
@@ -189,7 +188,7 @@ void Patch657417Section::writeTo(uint8_t *buf) {
// been altered to point to us!
uint64_t s = getThumbDestAddr(getBranchAddr(), instr);
uint64_t p = getVA(4);
- target->relocateOne(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p);
+ target->relocateNoSym(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p);
}
// Given a branch instruction spanning two 4KiB regions, at offset off from the
@@ -527,6 +526,3 @@ bool ARMErr657417Patcher::createFixes() {
}
return addressesChanged;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/AArch64.cpp b/contrib/llvm-project/lld/ELF/Arch/AArch64.cpp
index df41a12f7454..637046e90bbd 100644
--- a/contrib/llvm-project/lld/ELF/Arch/AArch64.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/AArch64.cpp
@@ -17,14 +17,13 @@
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Page(Expr) is the page address of the expression Expr, defined
// as (Expr & ~0xFFF). (This applies even if the machine page size
// supported by the platform has a different value.)
-uint64_t getAArch64Page(uint64_t expr) {
+uint64_t elf::getAArch64Page(uint64_t expr) {
return expr & ~static_cast<uint64_t>(0xFFF);
}
@@ -45,12 +44,16 @@ public:
uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
bool usesOnlyLowPageBits(RelType type) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -123,6 +126,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_CONDBR19:
case R_AARCH64_JUMP26:
case R_AARCH64_TSTBR14:
+ case R_AARCH64_PLT32:
return R_PLT_PC;
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
@@ -208,10 +212,10 @@ void AArch64::writePltHeader(uint8_t *buf) const {
uint64_t got = in.gotPlt->getVA();
uint64_t plt = in.plt->getVA();
- relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(got + 16) - getAArch64Page(plt + 4));
- relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
- relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(got + 16) - getAArch64Page(plt + 4));
+ relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
}
void AArch64::writePlt(uint8_t *buf, const Symbol &sym,
@@ -225,10 +229,10 @@ void AArch64::writePlt(uint8_t *buf, const Symbol &sym,
memcpy(buf, inst, sizeof(inst));
uint64_t gotPltEntryAddr = sym.getGotPltVA();
- relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
- relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
- relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
+ relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
}
bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
@@ -241,7 +245,8 @@ bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
// ELF for the ARM 64-bit architecture, section Call and Jump relocations
// only permits range extension thunks for R_AARCH64_CALL26 and
// R_AARCH64_JUMP26 relocation types.
- if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
+ type != R_AARCH64_PLT32)
return false;
uint64_t dst = expr == R_PLT_PC ? s.getPltVA() : s.getVA(a);
return !inBranchRange(type, branchAddr, dst);
@@ -255,11 +260,13 @@ uint32_t AArch64::getThunkSectionSpacing() const {
}
bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
- if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
+ type != R_AARCH64_PLT32)
return true;
// The AArch64 call and unconditional branch instructions have a range of
- // +/- 128 MiB.
- uint64_t range = 128 * 1024 * 1024;
+ // +/- 128 MiB. The PLT32 relocation supports a range up to +/- 2 GiB.
+ uint64_t range =
+ type == R_AARCH64_PLT32 ? (UINT64_C(1) << 31) : (128 * 1024 * 1024);
if (dst > src) {
// Immediate of branch is signed.
range -= 4;
@@ -309,16 +316,21 @@ static void writeSMovWImm(uint8_t *loc, uint32_t imm) {
write32le(loc, inst | ((imm & 0xFFFF) << 5));
}
-void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void AArch64::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt(loc, val, 32, type);
+ checkIntUInt(loc, val, 32, rel);
+ write32le(loc, val);
+ break;
+ case R_AARCH64_PLT32:
+ checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_AARCH64_ABS64:
@@ -332,13 +344,13 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
- checkInt(loc, val, 33, type);
+ checkInt(loc, val, 33, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_ADR_PREL_PG_HI21_NC:
write32AArch64Addr(loc, val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
write32AArch64Addr(loc, val);
break;
case R_AARCH64_JUMP26:
@@ -352,13 +364,13 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
write32le(loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
- checkInt(loc, val, 28, type);
+ checkInt(loc, val, 28, rel);
or32le(loc, (val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 21, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 21, rel);
or32le(loc, (val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
@@ -367,12 +379,12 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
- checkAlignment(loc, val, 2, type);
+ checkAlignment(loc, val, 2, rel);
or32AArch64Imm(loc, getBits(val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
- checkAlignment(loc, val, 4, type);
+ checkAlignment(loc, val, 4, rel);
or32AArch64Imm(loc, getBits(val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
@@ -380,28 +392,28 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12:
- checkAlignment(loc, val, 8, type);
+ checkAlignment(loc, val, 8, rel);
or32AArch64Imm(loc, getBits(val, 3, 11));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
- checkAlignment(loc, val, 16, type);
+ checkAlignment(loc, val, 16, rel);
or32AArch64Imm(loc, getBits(val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0:
- checkUInt(loc, val, 16, type);
+ checkUInt(loc, val, 16, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G0_NC:
or32le(loc, (val & 0xFFFF) << 5);
break;
case R_AARCH64_MOVW_UABS_G1:
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G1_NC:
or32le(loc, (val & 0xFFFF0000) >> 11);
break;
case R_AARCH64_MOVW_UABS_G2:
- checkUInt(loc, val, 48, type);
+ checkUInt(loc, val, 48, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G2_NC:
or32le(loc, (val & 0xFFFF00000000) >> 27);
@@ -412,7 +424,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_AARCH64_MOVW_PREL_G0:
case R_AARCH64_MOVW_SABS_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
@@ -421,7 +433,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_AARCH64_MOVW_PREL_G1:
case R_AARCH64_MOVW_SABS_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
- checkInt(loc, val, 33, type);
+ checkInt(loc, val, 33, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G1_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
@@ -430,7 +442,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_AARCH64_MOVW_PREL_G2:
case R_AARCH64_MOVW_SABS_G2:
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
- checkInt(loc, val, 49, type);
+ checkInt(loc, val, 49, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G2_NC:
writeSMovWImm(loc, val >> 32);
@@ -439,11 +451,11 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
writeSMovWImm(loc, val >> 48);
break;
case R_AARCH64_TSTBR14:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
or32le(loc, (val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkUInt(loc, val, 24, type);
+ checkUInt(loc, val, 24, rel);
or32AArch64Imm(loc, val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
@@ -455,7 +467,8 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void AArch64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
@@ -467,9 +480,9 @@ void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// movk x0, #0x10
// nop
// nop
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
- switch (type) {
+ switch (rel.type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
write32le(loc, 0xd503201f); // nop
@@ -485,7 +498,8 @@ void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+void AArch64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
@@ -498,34 +512,35 @@ void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// nop
// nop
- switch (type) {
+ switch (rel.type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
write32le(loc, 0xd503201f); // nop
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
write32le(loc, 0x90000000); // adrp
- relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
+ relocateNoSym(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
break;
case R_AARCH64_TLSDESC_LD64_LO12:
write32le(loc, 0xf9400000); // ldr
- relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
+ relocateNoSym(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
- checkUInt(loc, val, 32, type);
+void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ checkUInt(loc, val, 32, rel);
- if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
+ if (rel.type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
uint32_t regNo = read32le(loc) & 0x1f;
write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5));
return;
}
- if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
+ if (rel.type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
// Generate MOVK.
uint32_t regNo = read32le(loc) & 0x1f;
write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5));
@@ -593,8 +608,10 @@ AArch64BtiPac::AArch64BtiPac() {
// the function in an executable being taken by a shared library.
// FIXME: There is a potential optimization to omit the BTI if we detect
// that the address of the PLT entry isn't taken.
+ // The PAC PLT entries require dynamic loader support and this isn't known
+ // from properties in the objects, so we use the command line flag.
btiEntry = btiHeader && !config->shared;
- pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC);
+ pacEntry = config->zPacPlt;
if (btiEntry || pacEntry) {
pltEntrySize = 24;
@@ -627,10 +644,10 @@ void AArch64BtiPac::writePltHeader(uint8_t *buf) const {
}
memcpy(buf, pltData, sizeof(pltData));
- relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(got + 16) - getAArch64Page(plt + 8));
- relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
- relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(got + 16) - getAArch64Page(plt + 8));
+ relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
if (!btiHeader)
// We didn't add the BTI c instruction so round out size with NOP.
memcpy(buf + sizeof(pltData), nopData, sizeof(nopData));
@@ -664,11 +681,10 @@ void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t gotPltEntryAddr = sym.getGotPltVA();
memcpy(buf, addrInst, sizeof(addrInst));
- relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(gotPltEntryAddr) -
- getAArch64Page(pltEntryAddr));
- relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
- relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
+ relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
if (pacEntry)
memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
@@ -689,7 +705,4 @@ static TargetInfo *getTargetInfo() {
return &t;
}
-TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); }
-
-} // namespace elf
-} // namespace lld
+TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
diff --git a/contrib/llvm-project/lld/ELF/Arch/AMDGPU.cpp b/contrib/llvm-project/lld/ELF/Arch/AMDGPU.cpp
index b42ca7746742..3610a38692d6 100644
--- a/contrib/llvm-project/lld/ELF/Arch/AMDGPU.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/AMDGPU.cpp
@@ -17,16 +17,16 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class AMDGPU final : public TargetInfo {
public:
AMDGPU();
uint32_t calcEFlags() const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
@@ -58,8 +58,8 @@ uint32_t AMDGPU::calcEFlags() const {
return ret;
}
-void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
case R_AMDGPU_GOTPCREL32_LO:
@@ -108,10 +108,7 @@ RelType AMDGPU::getDynRel(RelType type) const {
return R_AMDGPU_NONE;
}
-TargetInfo *getAMDGPUTargetInfo() {
+TargetInfo *elf::getAMDGPUTargetInfo() {
static AMDGPU target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/ARM.cpp b/contrib/llvm-project/lld/ELF/Arch/ARM.cpp
index 08cae59b294b..fd90557cc4f6 100644
--- a/contrib/llvm-project/lld/ELF/Arch/ARM.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/ARM.cpp
@@ -18,9 +18,8 @@
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class ARM final : public TargetInfo {
@@ -43,7 +42,8 @@ public:
int64_t a) const override;
uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -64,6 +64,7 @@ ARM::ARM() {
ipltEntrySize = 16;
trapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
needsThunks = true;
+ defaultMaxPageSize = 65536;
}
uint32_t ARM::calcEFlags() const {
@@ -120,6 +121,8 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
return R_TLSGD_PC;
case R_ARM_TLS_LDM32:
return R_TLSLD_PC;
+ case R_ARM_TLS_LDO32:
+ return R_DTPREL;
case R_ARM_BASE_PREL:
// B(S) + A - P
// FIXME: currently B(S) assumed to be .got, this may not hold for all
@@ -131,6 +134,19 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
return R_PC;
+ case R_ARM_ALU_PC_G0:
+ case R_ARM_LDR_PC_G0:
+ case R_ARM_THM_ALU_PREL_11_0:
+ case R_ARM_THM_PC8:
+ case R_ARM_THM_PC12:
+ return R_ARM_PCA;
+ case R_ARM_MOVW_BREL_NC:
+ case R_ARM_MOVW_BREL:
+ case R_ARM_MOVT_BREL:
+ case R_ARM_THM_MOVW_BREL_NC:
+ case R_ARM_THM_MOVW_BREL:
+ case R_ARM_THM_MOVT_BREL:
+ return R_ARM_SBREL;
case R_ARM_NONE:
return R_NONE;
case R_ARM_TLS_LE32:
@@ -262,7 +278,8 @@ void ARM::addPltSymbols(InputSection &isec, uint64_t off) const {
}
bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
- uint64_t branchAddr, const Symbol &s, int64_t /*a*/) const {
+ uint64_t branchAddr, const Symbol &s,
+ int64_t /*a*/) const {
// If S is an undefined weak symbol and does not have a PLT entry then it
// will be resolved as a branch to the next instruction.
if (s.isUndefWeak() && !s.isInPlt())
@@ -375,8 +392,82 @@ bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
return distance <= range;
}
-void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+// Helper to produce message text when LLD detects that a CALL relocation to
+// a non STT_FUNC symbol that may result in incorrect interworking between ARM
+// or Thumb.
+static void stateChangeWarning(uint8_t *loc, RelType relt, const Symbol &s) {
+ assert(!s.isFunc());
+ if (s.isSection()) {
+ // Section symbols must be defined and in a section. Users cannot change
+ // the type. Use the section name as getName() returns an empty string.
+ warn(getErrorLocation(loc) + "branch and link relocation: " +
+ toString(relt) + " to STT_SECTION symbol " +
+ cast<Defined>(s).section->name + " ; interworking not performed");
+ } else {
+ // Warn with hint on how to alter the symbol type.
+ warn(getErrorLocation(loc) + "branch and link relocation: " +
+ toString(relt) + " to non STT_FUNC symbol: " + s.getName() +
+ " interworking not performed; consider using directive '.type " +
+ s.getName() +
+ ", %function' to give symbol type STT_FUNC if"
+ " interworking between ARM and Thumb is required");
+ }
+}
+
+// Utility functions taken from ARMAddressingModes.h, only changes are LLD
+// coding style.
+
+// Rotate a 32-bit unsigned value right by a specified amt of bits.
+static uint32_t rotr32(uint32_t val, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (val >> amt) | (val << ((32 - amt) & 31));
+}
+
+// Rotate a 32-bit unsigned value left by a specified amt of bits.
+static uint32_t rotl32(uint32_t val, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (val << amt) | (val >> ((32 - amt) & 31));
+}
+
+// Try to encode a 32-bit unsigned immediate imm with an immediate shifter
+// operand, this form is an 8-bit immediate rotated right by an even number of
+// bits. We compute the rotate amount to use. If this immediate value cannot be
+// handled with a single shifter-op, determine a good rotate amount that will
+// take a maximal chunk of bits out of the immediate.
+static uint32_t getSOImmValRotate(uint32_t imm) {
+ // 8-bit (or less) immediates are trivially shifter_operands with a rotate
+ // of zero.
+ if ((imm & ~255U) == 0)
+ return 0;
+
+ // Use CTZ to compute the rotate amount.
+ unsigned tz = llvm::countTrailingZeros(imm);
+
+ // Rotate amount must be even. Something like 0x200 must be rotated 8 bits,
+ // not 9.
+ unsigned rotAmt = tz & ~1;
+
+ // If we can handle this spread, return it.
+ if ((rotr32(imm, rotAmt) & ~255U) == 0)
+ return (32 - rotAmt) & 31; // HW rotates right, not left.
+
+ // For values like 0xF000000F, we should ignore the low 6 bits, then
+ // retry the hunt.
+ if (imm & 63U) {
+ unsigned tz2 = countTrailingZeros(imm & ~63U);
+ unsigned rotAmt2 = tz2 & ~1;
+ if ((rotr32(imm, rotAmt2) & ~255U) == 0)
+ return (32 - rotAmt2) & 31; // HW rotates right, not left.
+ }
+
+ // Otherwise, we have no way to cover this span of bits with a single
+ // shifter_op immediate. Return a chunk of bits that will be useful to
+ // handle.
+ return (32 - rotAmt) & 31; // HW rotates right, not left.
+}
+
+void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
case R_ARM_GOTOFF32:
@@ -397,40 +488,49 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
write32le(loc, val);
break;
case R_ARM_PREL31:
- checkInt(loc, val, 31, type);
+ checkInt(loc, val, 31, rel);
write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000));
break;
- case R_ARM_CALL:
- // R_ARM_CALL is used for BL and BLX instructions, depending on the
- // value of bit 0 of Val, we must select a BL or BLX instruction
- if (val & 1) {
- // If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
+ case R_ARM_CALL: {
+ // R_ARM_CALL is used for BL and BLX instructions, for symbols of type
+ // STT_FUNC we choose whether to write a BL or BLX depending on the
+ // value of bit 0 of Val. With bit 0 == 1 denoting Thumb. If the symbol is
+ // not of type STT_FUNC then we must preserve the original instruction.
+ // PLT entries are always ARM state so we know we don't need to interwork.
+ assert(rel.sym); // R_ARM_CALL is always reached via relocate().
+ bool bit0Thumb = val & 1;
+ bool isBlx = (read32le(loc) & 0xfe000000) == 0xfa000000;
+ // lld 10.0 and before always used bit0Thumb when deciding to write a BLX
+ // even when type not STT_FUNC.
+ if (!rel.sym->isFunc() && isBlx != bit0Thumb)
+ stateChangeWarning(loc, rel.type, *rel.sym);
+ if (rel.sym->isFunc() ? bit0Thumb : isBlx) {
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
- checkInt(loc, val, 26, type);
+ checkInt(loc, val, 26, rel);
write32le(loc, 0xfa000000 | // opcode
((val & 2) << 23) | // H
((val >> 2) & 0x00ffffff)); // imm24
break;
}
- if ((read32le(loc) & 0xfe000000) == 0xfa000000)
- // BLX (always unconditional) instruction to an ARM Target, select an
- // unconditional BL.
- write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
+ // BLX (always unconditional) instruction to an ARM Target, select an
+ // unconditional BL.
+ write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
// fall through as BL encoding is shared with B
+ }
LLVM_FALLTHROUGH;
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- checkInt(loc, val, 26, type);
+ checkInt(loc, val, 26, rel);
write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt(loc, val, 12, type);
+ checkInt(loc, val, 12, rel);
write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff));
break;
case R_ARM_THM_JUMP19:
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
write16le(loc,
(read16le(loc) & 0xfbc0) | // opcode cond
((val >> 10) & 0x0400) | // S
@@ -441,20 +541,32 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
((val >> 5) & 0x2000) | // J1
((val >> 1) & 0x07ff)); // imm11
break;
- case R_ARM_THM_CALL:
- // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the
- // value of bit 0 of Val, we must select a BL or BLX instruction
- if ((val & 1) == 0) {
- // Ensure BLX destination is 4-byte aligned. As BLX instruction may
- // only be two byte aligned. This must be done before overflow check
+ case R_ARM_THM_CALL: {
+ // R_ARM_THM_CALL is used for BL and BLX instructions, for symbols of type
+ // STT_FUNC we choose whether to write a BL or BLX depending on the
+ // value of bit 0 of Val. With bit 0 == 0 denoting ARM, if the symbol is
+ // not of type STT_FUNC then we must preserve the original instruction.
+ // PLT entries are always ARM state so we know we need to interwork.
+ assert(rel.sym); // R_ARM_THM_CALL is always reached via relocate().
+ bool bit0Thumb = val & 1;
+ bool isBlx = (read16le(loc + 2) & 0x1000) == 0;
+ // lld 10.0 and before always used bit0Thumb when deciding to write a BLX
+ // even when type not STT_FUNC. PLT entries generated by LLD are always ARM.
+ if (!rel.sym->isFunc() && !rel.sym->isInPlt() && isBlx == bit0Thumb)
+ stateChangeWarning(loc, rel.type, *rel.sym);
+ if (rel.sym->isFunc() || rel.sym->isInPlt() ? !bit0Thumb : isBlx) {
+ // We are writing a BLX. Ensure BLX destination is 4-byte aligned. As
+ // the BLX instruction may only be two byte aligned. This must be done
+ // before overflow check.
val = alignTo(val, 4);
+ write16le(loc + 2, read16le(loc + 2) & ~0x1000);
+ } else {
+ write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | 1 << 12);
}
- // Bit 12 is 0 for BLX, 1 for BL
- write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12);
if (!config->armJ1J2BranchEncoding) {
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
// different encoding rules and range due to J1 and J2 always being 1.
- checkInt(loc, val, 23, type);
+ checkInt(loc, val, 23, rel);
write16le(loc,
0xf000 | // opcode
((val >> 12) & 0x07ff)); // imm11
@@ -464,11 +576,12 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
((val >> 1) & 0x07ff)); // imm11
break;
}
+ }
// Fall through as rest of encoding is the same as B.W
LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
- checkInt(loc, val, 25, type);
+ checkInt(loc, val, 25, rel);
write16le(loc,
0xf000 | // opcode
((val >> 14) & 0x0400) | // S
@@ -481,16 +594,19 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVW_PREL_NC:
+ case R_ARM_MOVW_BREL_NC:
write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) |
(val & 0x0fff));
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
+ case R_ARM_MOVT_BREL:
write32le(loc, (read32le(loc) & ~0x000f0fff) |
(((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
+ case R_ARM_THM_MOVT_BREL:
// Encoding T1: A = imm4:i:imm3:imm8
write16le(loc,
0xf2c0 | // opcode
@@ -503,6 +619,7 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVW_PREL_NC:
+ case R_ARM_THM_MOVW_BREL_NC:
// Encoding T3: A = imm4:i:imm3:imm8
write16le(loc,
0xf240 | // opcode
@@ -513,8 +630,92 @@ void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
((val << 4) & 0x7000) | // imm3
(val & 0x00ff)); // imm8
break;
+ case R_ARM_ALU_PC_G0: {
+ // ADR (literal) add = bit23, sub = bit22
+ // literal is a 12-bit modified immediate, made up of a 4-bit even rotate
+ // right and an 8-bit immediate. The code-sequence here is derived from
+ // ARMAddressingModes.h in llvm/Target/ARM/MCTargetDesc. In our case we
+ // want to give an error if we cannot encode the constant.
+ uint32_t opcode = 0x00800000;
+ if (val >> 63) {
+ opcode = 0x00400000;
+ val = ~val + 1;
+ }
+ if ((val & ~255U) != 0) {
+ uint32_t rotAmt = getSOImmValRotate(val);
+ // Error if we cannot encode this with a single shift
+ if (rotr32(~255U, rotAmt) & val)
+ error(getErrorLocation(loc) + "unencodeable immediate " +
+ Twine(val).str() + " for relocation " + toString(rel.type));
+ val = rotl32(val, rotAmt) | ((rotAmt >> 1) << 8);
+ }
+ write32le(loc, (read32le(loc) & 0xff0ff000) | opcode | val);
+ break;
+ }
+ case R_ARM_LDR_PC_G0: {
+ // R_ARM_LDR_PC_G0 is S + A - P, we have ((S + A) | T) - P, if S is a
+ // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+ // bottom bit to recover S + A - P.
+ if (rel.sym->isFunc())
+ val &= ~0x1;
+ // LDR (literal) u = bit23
+ int64_t imm = val;
+ uint32_t u = 0x00800000;
+ if (imm < 0) {
+ imm = -imm;
+ u = 0;
+ }
+ checkUInt(loc, imm, 12, rel);
+ write32le(loc, (read32le(loc) & 0xff7ff000) | u | imm);
+ break;
+ }
+ case R_ARM_THM_ALU_PREL_11_0: {
+ // ADR encoding T2 (sub), T3 (add) i:imm3:imm8
+ int64_t imm = val;
+ uint16_t sub = 0;
+ if (imm < 0) {
+ imm = -imm;
+ sub = 0x00a0;
+ }
+ checkUInt(loc, imm, 12, rel);
+ write16le(loc, (read16le(loc) & 0xfb0f) | sub | (imm & 0x800) >> 1);
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0x8f00) | (imm & 0x700) << 4 | (imm & 0xff));
+ break;
+ }
+ case R_ARM_THM_PC8:
+ // ADR and LDR literal encoding T1 positive offset only imm8:00
+ // R_ARM_THM_PC8 is S + A - Pa, we have ((S + A) | T) - Pa, if S is a
+ // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+ // bottom bit to recover S + A - Pa.
+ if (rel.sym->isFunc())
+ val &= ~0x1;
+ checkUInt(loc, val, 10, rel);
+ checkAlignment(loc, val, 4, rel);
+ write16le(loc, (read16le(loc) & 0xff00) | (val & 0x3fc) >> 2);
+ break;
+ case R_ARM_THM_PC12: {
+ // LDR (literal) encoding T2, add = (U == '1') imm12
+ // imm12 is unsigned
+ // R_ARM_THM_PC12 is S + A - Pa, we have ((S + A) | T) - Pa, if S is a
+ // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+ // bottom bit to recover S + A - Pa.
+ if (rel.sym->isFunc())
+ val &= ~0x1;
+ int64_t imm12 = val;
+ uint16_t u = 0x0080;
+ if (imm12 < 0) {
+ imm12 = -imm12;
+ u = 0;
+ }
+ checkUInt(loc, imm12, 12, rel);
+ write16le(loc, read16le(loc) | u);
+ write16le(loc + 2, (read16le(loc + 2) & 0xf000) | imm12);
+ break;
+ }
default:
- error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ error(getErrorLocation(loc) + "unrecognized relocation " +
+ toString(rel.type));
}
}
@@ -582,14 +783,18 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
- case R_ARM_MOVT_PREL: {
+ case R_ARM_MOVT_PREL:
+ case R_ARM_MOVW_BREL_NC:
+ case R_ARM_MOVT_BREL: {
uint64_t val = read32le(buf) & 0x000f0fff;
return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff));
}
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVW_PREL_NC:
- case R_ARM_THM_MOVT_PREL: {
+ case R_ARM_THM_MOVT_PREL:
+ case R_ARM_THM_MOVW_BREL_NC:
+ case R_ARM_THM_MOVT_BREL: {
// Encoding T3: A = imm4:i:imm3:imm8
uint16_t hi = read16le(buf);
uint16_t lo = read16le(buf + 2);
@@ -598,13 +803,50 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
((lo & 0x7000) >> 4) | // imm3
(lo & 0x00ff)); // imm8
}
+ case R_ARM_ALU_PC_G0: {
+ // 12-bit immediate is a modified immediate made up of a 4-bit even
+ // right rotation and 8-bit constant. After the rotation the value
+ // is zero-extended. When bit 23 is set the instruction is an add, when
+ // bit 22 is set it is a sub.
+ uint32_t instr = read32le(buf);
+ uint32_t val = rotr32(instr & 0xff, ((instr & 0xf00) >> 8) * 2);
+ return (instr & 0x00400000) ? -val : val;
+ }
+ case R_ARM_LDR_PC_G0: {
+ // ADR (literal) add = bit23, sub = bit22
+ // LDR (literal) u = bit23 unsigned imm12
+ bool u = read32le(buf) & 0x00800000;
+ uint32_t imm12 = read32le(buf) & 0xfff;
+ return u ? imm12 : -imm12;
+ }
+ case R_ARM_THM_ALU_PREL_11_0: {
+ // Thumb2 ADR, which is an alias for a sub or add instruction with an
+ // unsigned immediate.
+ // ADR encoding T2 (sub), T3 (add) i:imm3:imm8
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ uint64_t imm = (hi & 0x0400) << 1 | // i
+ (lo & 0x7000) >> 4 | // imm3
+ (lo & 0x00ff); // imm8
+ // For sub, addend is negative, add is positive.
+ return (hi & 0x00f0) ? -imm : imm;
+ }
+ case R_ARM_THM_PC8:
+ // ADR and LDR (literal) encoding T1
+ // From ELF for the ARM Architecture the initial signed addend is formed
+ // from an unsigned field using expression (((imm8:00 + 4) & 0x3ff) – 4)
+ // this trick permits the PC bias of -4 to be encoded using imm8 = 0xff
+ return ((((read16le(buf) & 0xff) << 2) + 4) & 0x3ff) - 4;
+ case R_ARM_THM_PC12: {
+ // LDR (literal) encoding T2, add = (U == '1') imm12
+ bool u = read16le(buf) & 0x0080;
+ uint64_t imm12 = read16le(buf + 2) & 0x0fff;
+ return u ? imm12 : -imm12;
+ }
}
}
-TargetInfo *getARMTargetInfo() {
+TargetInfo *elf::getARMTargetInfo() {
static ARM target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/AVR.cpp b/contrib/llvm-project/lld/ELF/Arch/AVR.cpp
index cb33ff448ba4..4513a970b32d 100644
--- a/contrib/llvm-project/lld/ELF/Arch/AVR.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/AVR.cpp
@@ -36,9 +36,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class AVR final : public TargetInfo {
@@ -46,7 +45,8 @@ public:
AVR();
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -54,11 +54,131 @@ AVR::AVR() { noneRel = R_AVR_NONE; }
RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
- return R_ABS;
+ switch (type) {
+ case R_AVR_7_PCREL:
+ case R_AVR_13_PCREL:
+ return R_PC;
+ default:
+ return R_ABS;
+ }
}
-void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+static void writeLDI(uint8_t *loc, uint64_t val) {
+ write16le(loc, (read16le(loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f));
+}
+
+void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
+ case R_AVR_8:
+ checkUInt(loc, val, 8, rel);
+ *loc = val;
+ break;
+ case R_AVR_16:
+ // Note: this relocation is often used between code and data space, which
+ // are 0x800000 apart in the output ELF file. The bitmask cuts off the high
+ // bit.
+ write16le(loc, val & 0xffff);
+ break;
+ case R_AVR_16_PM:
+ checkAlignment(loc, val, 2, rel);
+ checkUInt(loc, val >> 1, 16, rel);
+ write16le(loc, val >> 1);
+ break;
+ case R_AVR_32:
+ checkUInt(loc, val, 32, rel);
+ write32le(loc, val);
+ break;
+
+ case R_AVR_LDI:
+ checkUInt(loc, val, 8, rel);
+ writeLDI(loc, val & 0xff);
+ break;
+
+ case R_AVR_LO8_LDI_NEG:
+ writeLDI(loc, -val & 0xff);
+ break;
+ case R_AVR_LO8_LDI:
+ writeLDI(loc, val & 0xff);
+ break;
+ case R_AVR_HI8_LDI_NEG:
+ writeLDI(loc, (-val >> 8) & 0xff);
+ break;
+ case R_AVR_HI8_LDI:
+ writeLDI(loc, (val >> 8) & 0xff);
+ break;
+ case R_AVR_HH8_LDI_NEG:
+ writeLDI(loc, (-val >> 16) & 0xff);
+ break;
+ case R_AVR_HH8_LDI:
+ writeLDI(loc, (val >> 16) & 0xff);
+ break;
+ case R_AVR_MS8_LDI_NEG:
+ writeLDI(loc, (-val >> 24) & 0xff);
+ break;
+ case R_AVR_MS8_LDI:
+ writeLDI(loc, (val >> 24) & 0xff);
+ break;
+
+ case R_AVR_LO8_LDI_PM:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (val >> 1) & 0xff);
+ break;
+ case R_AVR_HI8_LDI_PM:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (val >> 9) & 0xff);
+ break;
+ case R_AVR_HH8_LDI_PM:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (val >> 17) & 0xff);
+ break;
+
+ case R_AVR_LO8_LDI_PM_NEG:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (-val >> 1) & 0xff);
+ break;
+ case R_AVR_HI8_LDI_PM_NEG:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (-val >> 9) & 0xff);
+ break;
+ case R_AVR_HH8_LDI_PM_NEG:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (-val >> 17) & 0xff);
+ break;
+
+ case R_AVR_PORT5:
+ checkUInt(loc, val, 5, rel);
+ write16le(loc, (read16le(loc) & 0xff07) | (val << 3));
+ break;
+ case R_AVR_PORT6:
+ checkUInt(loc, val, 6, rel);
+ write16le(loc, (read16le(loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f));
+ break;
+
+ // Since every jump destination is word aligned we gain an extra bit
+ case R_AVR_7_PCREL: {
+ checkInt(loc, val, 7, rel);
+ checkAlignment(loc, val, 2, rel);
+ const uint16_t target = (val - 2) >> 1;
+ write16le(loc, (read16le(loc) & 0xfc07) | ((target & 0x7f) << 3));
+ break;
+ }
+ case R_AVR_13_PCREL: {
+ checkAlignment(loc, val, 2, rel);
+ const uint16_t target = (val - 2) >> 1;
+ write16le(loc, (read16le(loc) & 0xf000) | (target & 0xfff));
+ break;
+ }
+
+ case R_AVR_6:
+ checkInt(loc, val, 6, rel);
+ write16le(loc, (read16le(loc) & 0xd3f8) | (val & 0x20) << 8 |
+ (val & 0x18) << 7 | (val & 0x07));
+ break;
+ case R_AVR_6_ADIW:
+ checkInt(loc, val, 6, rel);
+ write16le(loc, (read16le(loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F));
+ break;
+
case R_AVR_CALL: {
uint16_t hi = val >> 17;
uint16_t lo = val >> 1;
@@ -67,14 +187,12 @@ void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
}
default:
- error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ error(getErrorLocation(loc) + "unrecognized relocation " +
+ toString(rel.type));
}
}
-TargetInfo *getAVRTargetInfo() {
+TargetInfo *elf::getAVRTargetInfo() {
static AVR target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/Hexagon.cpp b/contrib/llvm-project/lld/ELF/Arch/Hexagon.cpp
index 106bc9bab5bd..7740ce9a71e0 100644
--- a/contrib/llvm-project/lld/ELF/Arch/Hexagon.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/Hexagon.cpp
@@ -19,9 +19,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class Hexagon final : public TargetInfo {
@@ -31,7 +30,8 @@ public:
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
@@ -55,6 +55,8 @@ Hexagon::Hexagon() {
defaultMaxPageSize = 0x10000;
noneRel = R_HEX_NONE;
tlsGotRel = R_HEX_TPREL_32;
+ tlsModuleIndexRel = R_HEX_DTPMOD_32;
+ tlsOffsetRel = R_HEX_DTPREL_32;
}
uint32_t Hexagon::calcEFlags() const {
@@ -102,6 +104,7 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
case R_HEX_32_6_X:
case R_HEX_HI16:
case R_HEX_LO16:
+ case R_HEX_DTPREL_32:
return R_ABS;
case R_HEX_B9_PCREL:
case R_HEX_B13_PCREL:
@@ -115,12 +118,19 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
case R_HEX_PLT_B22_PCREL:
case R_HEX_B22_PCREL_X:
case R_HEX_B32_PCREL_X:
+ case R_HEX_GD_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL_X:
+ case R_HEX_GD_PLT_B32_PCREL_X:
return R_PLT_PC;
case R_HEX_IE_32_6_X:
case R_HEX_IE_16_X:
case R_HEX_IE_HI16:
case R_HEX_IE_LO16:
return R_GOT;
+ case R_HEX_GD_GOT_11_X:
+ case R_HEX_GD_GOT_16_X:
+ case R_HEX_GD_GOT_32_6_X:
+ return R_TLSGD_GOTPLT;
case R_HEX_GOTREL_11_X:
case R_HEX_GOTREL_16_X:
case R_HEX_GOTREL_32_6_X:
@@ -152,6 +162,13 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
}
}
+static bool isDuplex(uint32_t insn) {
+ // Duplex forms have a fixed mask and parse bits 15:14 are always
+ // zero. Non-duplex insns will always have at least one bit set in the
+ // parse field.
+ return (0xC000 & insn) == 0;
+}
+
static uint32_t findMaskR6(uint32_t insn) {
// There are (arguably too) many relocation masks for the DSP's
// R_HEX_6_X type. The table below is used to select the correct mask
@@ -176,10 +193,7 @@ static uint32_t findMaskR6(uint32_t insn) {
{0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0},
{0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}};
- // Duplex forms have a fixed mask and parse bits 15:14 are always
- // zero. Non-duplex insns will always have at least one bit set in the
- // parse field.
- if ((0xC000 & insn) == 0x0)
+ if (isDuplex(insn))
return 0x03f00000;
for (InstructionMask i : r6)
@@ -215,6 +229,9 @@ static uint32_t findMaskR16(uint32_t insn) {
if ((0xff000000 & insn) == 0xb0000000)
return 0x0fe03fe0;
+ if (isDuplex(insn))
+ return 0x03f00000;
+
error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
utohexstr(insn));
return 0;
@@ -222,8 +239,9 @@ static uint32_t findMaskR16(uint32_t insn) {
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
-void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_HEX_NONE:
break;
case R_HEX_6_PCREL_X:
@@ -240,6 +258,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
or32le(loc, applyMask(0x00203fe0, val & 0x3f));
break;
case R_HEX_11_X:
+ case R_HEX_GD_GOT_11_X:
case R_HEX_IE_GOT_11_X:
case R_HEX_GOT_11_X:
case R_HEX_GOTREL_11_X:
@@ -252,6 +271,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_HEX_16_X: // These relocs only have 6 effective bits.
case R_HEX_IE_16_X:
case R_HEX_IE_GOT_16_X:
+ case R_HEX_GD_GOT_16_X:
case R_HEX_GOT_16_X:
case R_HEX_GOTREL_16_X:
case R_HEX_TPREL_16_X:
@@ -262,9 +282,11 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_HEX_32:
case R_HEX_32_PCREL:
+ case R_HEX_DTPREL_32:
or32le(loc, val);
break;
case R_HEX_32_6_X:
+ case R_HEX_GD_GOT_32_6_X:
case R_HEX_GOT_32_6_X:
case R_HEX_GOTREL_32_6_X:
case R_HEX_IE_GOT_32_6_X:
@@ -273,32 +295,35 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_B9_PCREL:
- checkInt(loc, val, 11, type);
+ checkInt(loc, val, 11, rel);
or32le(loc, applyMask(0x003000fe, val >> 2));
break;
case R_HEX_B9_PCREL_X:
or32le(loc, applyMask(0x003000fe, val & 0x3f));
break;
case R_HEX_B13_PCREL:
- checkInt(loc, val, 15, type);
+ checkInt(loc, val, 15, rel);
or32le(loc, applyMask(0x00202ffe, val >> 2));
break;
case R_HEX_B15_PCREL:
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
or32le(loc, applyMask(0x00df20fe, val >> 2));
break;
case R_HEX_B15_PCREL_X:
or32le(loc, applyMask(0x00df20fe, val & 0x3f));
break;
case R_HEX_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
case R_HEX_PLT_B22_PCREL:
- checkInt(loc, val, 22, type);
+ checkInt(loc, val, 22, rel);
or32le(loc, applyMask(0x1ff3ffe, val >> 2));
break;
case R_HEX_B22_PCREL_X:
+ case R_HEX_GD_PLT_B22_PCREL_X:
or32le(loc, applyMask(0x1ff3ffe, val & 0x3f));
break;
case R_HEX_B32_PCREL_X:
+ case R_HEX_GD_PLT_B32_PCREL_X:
or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_GOTREL_HI16:
@@ -335,8 +360,8 @@ void Hexagon::writePltHeader(uint8_t *buf) const {
// Offset from PLT0 to the GOT.
uint64_t off = in.gotPlt->getVA() - in.plt->getVA();
- relocateOne(buf, R_HEX_B32_PCREL_X, off);
- relocateOne(buf + 4, R_HEX_6_PCREL_X, off);
+ relocateNoSym(buf, R_HEX_B32_PCREL_X, off);
+ relocateNoSym(buf + 4, R_HEX_6_PCREL_X, off);
}
void Hexagon::writePlt(uint8_t *buf, const Symbol &sym,
@@ -350,8 +375,8 @@ void Hexagon::writePlt(uint8_t *buf, const Symbol &sym,
memcpy(buf, inst, sizeof(inst));
uint64_t gotPltEntryAddr = sym.getGotPltVA();
- relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
- relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
}
RelType Hexagon::getDynRel(RelType type) const {
@@ -360,10 +385,7 @@ RelType Hexagon::getDynRel(RelType type) const {
return R_HEX_NONE;
}
-TargetInfo *getHexagonTargetInfo() {
+TargetInfo *elf::getHexagonTargetInfo() {
static Hexagon target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/MSP430.cpp b/contrib/llvm-project/lld/ELF/Arch/MSP430.cpp
index f03e8181923b..4af90b40a346 100644
--- a/contrib/llvm-project/lld/ELF/Arch/MSP430.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/MSP430.cpp
@@ -26,9 +26,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class MSP430 final : public TargetInfo {
@@ -36,7 +35,8 @@ public:
MSP430();
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -60,38 +60,36 @@ RelExpr MSP430::getRelExpr(RelType type, const Symbol &s,
}
}
-void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void MSP430::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_MSP430_8:
- checkIntUInt(loc, val, 8, type);
+ checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_MSP430_16:
case R_MSP430_16_PCREL:
case R_MSP430_16_BYTE:
case R_MSP430_16_PCREL_BYTE:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_MSP430_32:
- checkIntUInt(loc, val, 32, type);
+ checkIntUInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_MSP430_10_PCREL: {
int16_t offset = ((int16_t)val >> 1) - 1;
- checkInt(loc, offset, 10, type);
+ checkInt(loc, offset, 10, rel);
write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF));
break;
}
default:
- error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ error(getErrorLocation(loc) + "unrecognized relocation " +
+ toString(rel.type));
}
}
-TargetInfo *getMSP430TargetInfo() {
+TargetInfo *elf::getMSP430TargetInfo() {
static MSP430 target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/Mips.cpp b/contrib/llvm-project/lld/ELF/Arch/Mips.cpp
index ed6f4ca24130..fd1c5f507734 100644
--- a/contrib/llvm-project/lld/ELF/Arch/Mips.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/Mips.cpp
@@ -18,9 +18,9 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
template <class ELFT> class MIPS final : public TargetInfo {
public:
@@ -37,7 +37,8 @@ public:
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
int64_t a) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
bool usesOnlyLowPageBits(RelType type) const override;
};
} // namespace
@@ -274,12 +275,12 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
write16(buf + 18, 0x0f83); // move $28, $3
write16(buf + 20, 0x472b); // jalrc $25
write16(buf + 22, 0x0c00); // nop
- relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
+ relocateNoSym(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
} else {
write16(buf + 18, 0x45f9); // jalrc $25
write16(buf + 20, 0x0f83); // move $28, $3
write16(buf + 22, 0x0c00); // nop
- relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
+ relocateNoSym(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
}
return;
}
@@ -330,13 +331,13 @@ void MIPS<ELFT>::writePlt(uint8_t *buf, const Symbol &sym,
write16(buf + 4, 0xff22); // lw $25, 0($2)
write16(buf + 8, 0x0f02); // move $24, $2
write16(buf + 10, 0x4723); // jrc $25 / jr16 $25
- relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
} else {
write16(buf, 0x7900); // addiupc $2, (GOTPLT) - .
write16(buf + 4, 0xff22); // lw $25, 0($2)
write16(buf + 8, 0x4599); // jrc $25 / jr16 $25
write16(buf + 10, 0x0f02); // move $24, $2
- relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
}
return;
}
@@ -537,8 +538,10 @@ static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
}
template <class ELFT>
-void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+void MIPS<ELFT>::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
const endianness e = ELFT::TargetEndianness;
+ RelType type = rel.type;
if (ELFT::Is64Bits || config->mipsN32Abi)
std::tie(type, val) = calculateMipsRelChain(loc, type, val);
@@ -577,7 +580,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
if (config->relocatable) {
writeValue(loc, val + 0x8000, 16, 16);
} else {
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
writeValue(loc, val, 16, 0);
}
break;
@@ -585,7 +588,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
if (config->relocatable) {
writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
} else {
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
writeShuffleValue<e>(loc, val, 16, 0);
}
break;
@@ -596,7 +599,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
@@ -610,7 +613,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_CALL16:
@@ -622,7 +625,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
- checkInt(loc, val, 7, type);
+ checkInt(loc, val, 7, rel);
writeShuffleValue<e>(loc, val, 7, 2);
break;
case R_MIPS_CALL_HI16:
@@ -665,23 +668,23 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 18, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 18, rel);
writeValue(loc, val, 16, 2);
break;
case R_MIPS_PC19_S2:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 21, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 21, rel);
writeValue(loc, val, 19, 2);
break;
case R_MIPS_PC21_S2:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 23, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 23, rel);
writeValue(loc, val, 21, 2);
break;
case R_MIPS_PC26_S2:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 28, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 28, rel);
writeValue(loc, val, 26, 2);
break;
case R_MIPS_PC32:
@@ -689,35 +692,35 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
- checkInt(loc, val, 27, type);
+ checkInt(loc, val, 27, rel);
writeShuffleValue<e>(loc, val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
- checkInt(loc, val, 8, type);
+ checkInt(loc, val, 8, rel);
writeMicroRelocation16<e>(loc, val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
- checkInt(loc, val, 11, type);
+ checkInt(loc, val, 11, rel);
writeMicroRelocation16<e>(loc, val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
writeShuffleValue<e>(loc, val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
writeShuffleValue<e>(loc, val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
writeShuffleValue<e>(loc, val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
- checkInt(loc, val, 22, type);
+ checkInt(loc, val, 22, rel);
writeShuffleValue<e>(loc, val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
- checkInt(loc, val, 25, type);
+ checkInt(loc, val, 25, rel);
writeShuffleValue<e>(loc, val, 23, 2);
break;
default:
@@ -731,7 +734,7 @@ template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const {
}
// Return true if the symbol is a PIC function.
-template <class ELFT> bool isMipsPIC(const Defined *sym) {
+template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
if (!sym->isFunc())
return false;
@@ -749,20 +752,17 @@ template <class ELFT> bool isMipsPIC(const Defined *sym) {
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
}
-template <class ELFT> TargetInfo *getMipsTargetInfo() {
+template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
static MIPS<ELFT> target;
return &target;
}
-template TargetInfo *getMipsTargetInfo<ELF32LE>();
-template TargetInfo *getMipsTargetInfo<ELF32BE>();
-template TargetInfo *getMipsTargetInfo<ELF64LE>();
-template TargetInfo *getMipsTargetInfo<ELF64BE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
-template bool isMipsPIC<ELF32LE>(const Defined *);
-template bool isMipsPIC<ELF32BE>(const Defined *);
-template bool isMipsPIC<ELF64LE>(const Defined *);
-template bool isMipsPIC<ELF64BE>(const Defined *);
-
-} // namespace elf
-} // namespace lld
+template bool elf::isMipsPIC<ELF32LE>(const Defined *);
+template bool elf::isMipsPIC<ELF32BE>(const Defined *);
+template bool elf::isMipsPIC<ELF64LE>(const Defined *);
+template bool elf::isMipsPIC<ELF64BE>(const Defined *);
diff --git a/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp b/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
index 923458afae0d..85329c3bef53 100644
--- a/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
@@ -23,8 +23,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
struct ArchTreeEdge {
@@ -294,7 +294,7 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
return ret;
}
-template <class ELFT> uint32_t calcMipsEFlags() {
+template <class ELFT> uint32_t elf::calcMipsEFlags() {
std::vector<FileFlags> v;
for (InputFile *f : objectFiles)
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
@@ -350,7 +350,8 @@ static StringRef getMipsFpAbiName(uint8_t fpAbi) {
}
}
-uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) {
+uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ StringRef fileName) {
if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
return newFlag;
if (compareMipsFpAbi(oldFlag, newFlag) < 0)
@@ -366,7 +367,7 @@ template <class ELFT> static bool isN32Abi(const InputFile *f) {
return false;
}
-bool isMipsN32Abi(const InputFile *f) {
+bool elf::isMipsN32Abi(const InputFile *f) {
switch (config->ekind) {
case ELF32LEKind:
return isN32Abi<ELF32LE>(f);
@@ -381,17 +382,14 @@ bool isMipsN32Abi(const InputFile *f) {
}
}
-bool isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
+bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
-bool isMipsR6() {
+bool elf::isMipsR6() {
uint32_t arch = config->eflags & EF_MIPS_ARCH;
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
}
-template uint32_t calcMipsEFlags<ELF32LE>();
-template uint32_t calcMipsEFlags<ELF32BE>();
-template uint32_t calcMipsEFlags<ELF64LE>();
-template uint32_t calcMipsEFlags<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
+template uint32_t elf::calcMipsEFlags<ELF32LE>();
+template uint32_t elf::calcMipsEFlags<ELF32BE>();
+template uint32_t elf::calcMipsEFlags<ELF64LE>();
+template uint32_t elf::calcMipsEFlags<ELF64BE>();
diff --git a/contrib/llvm-project/lld/ELF/Arch/PPC.cpp b/contrib/llvm-project/lld/ELF/Arch/PPC.cpp
index 20f5d41acae4..a004cf74ddd8 100644
--- a/contrib/llvm-project/lld/ELF/Arch/PPC.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/PPC.cpp
@@ -17,9 +17,8 @@
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class PPC final : public TargetInfo {
@@ -44,14 +43,19 @@ public:
int64_t a) const override;
uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
int getTlsGdRelaxSkip(RelType type) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -66,7 +70,7 @@ static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
write32(config->isLE ? loc : loc - 2, insn);
}
-void writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
+void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
// Create canonical PLT entries for non-PIE code. Compilers don't generate
// non-GOT-non-PLT relocations referencing external functions for -fpie/-fPIE.
uint32_t glink = in.plt->getVA(); // VA of .glink
@@ -288,12 +292,12 @@ static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) {
}
}
-void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
RelType newType;
- std::tie(newType, val) = fromDTPREL(type, val);
+ std::tie(newType, val) = fromDTPREL(rel.type, val);
switch (newType) {
case R_PPC_ADDR16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16(loc, val);
break;
case R_PPC_GOT16:
@@ -301,7 +305,7 @@ void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TPREL16:
case R_PPC_TPREL16:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
write16(loc, val);
break;
case R_PPC_ADDR16_HA:
@@ -337,8 +341,8 @@ void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_PPC_REL14: {
uint32_t mask = 0x0000FFFC;
- checkInt(loc, val, 16, type);
- checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 16, rel);
+ checkAlignment(loc, val, 4, rel);
write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
@@ -346,8 +350,8 @@ void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_PPC_LOCAL24PC:
case R_PPC_PLTREL24: {
uint32_t mask = 0x03FFFFFC;
- checkInt(loc, val, 26, type);
- checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 26, rel);
+ checkAlignment(loc, val, 4, rel);
write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
@@ -379,13 +383,14 @@ int PPC::getTlsGdRelaxSkip(RelType type) const {
return 1;
}
-void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_PPC_GOT_TLSGD16: {
// addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA)
uint32_t insn = readFromHalf16(loc);
writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000));
- relocateOne(loc, R_PPC_GOT_TPREL16, val);
+ relocateNoSym(loc, R_PPC_GOT_TPREL16, val);
break;
}
case R_PPC_TLSGD:
@@ -397,8 +402,9 @@ void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_PPC_GOT_TLSGD16:
// addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha
writeFromHalf16(loc, 0x3c620000 | ha(val));
@@ -412,8 +418,9 @@ void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_PPC_GOT_TLSLD16:
// addi r3, rA, x@got@tlsgd --> addis r3, r2, 0
writeFromHalf16(loc, 0x3c620000);
@@ -428,15 +435,16 @@ void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
case R_PPC_DTPREL16_HA:
case R_PPC_DTPREL16_HI:
case R_PPC_DTPREL16_LO:
- relocateOne(loc, type, val);
+ relocate(loc, rel, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
}
-void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_PPC_GOT_TPREL16: {
// lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha
uint32_t rt = readFromHalf16(loc) & 0x03e00000;
@@ -459,10 +467,7 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-TargetInfo *getPPCTargetInfo() {
+TargetInfo *elf::getPPCTargetInfo() {
static PPC target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/PPC64.cpp b/contrib/llvm-project/lld/ELF/Arch/PPC64.cpp
index e48a184c9db2..71c568088fb9 100644
--- a/contrib/llvm-project/lld/ELF/Arch/PPC64.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/PPC64.cpp
@@ -6,20 +6,21 @@
//
//===----------------------------------------------------------------------===//
+#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
static uint64_t ppc64TocOffset = 0x8000;
static uint64_t dynamicThreadPointerOffset = 0x8000;
@@ -61,7 +62,7 @@ enum DFormOpcd {
ADDI = 14
};
-uint64_t getPPC64TocBase() {
+uint64_t elf::getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
// TOC starts where the first of these sections starts. We always create a
// .got when we see a relocation that uses it, so for us the start is always
@@ -75,7 +76,7 @@ uint64_t getPPC64TocBase() {
return tocVA + ppc64TocOffset;
}
-unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
+unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
// The offset is encoded into the 3 most significant bits of the st_other
// field, with some special values described in section 3.4.1 of the ABI:
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use
@@ -100,11 +101,89 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
return 0;
}
-bool isPPC64SmallCodeModelTocReloc(RelType type) {
+bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
// The only small code model relocations that access the .toc section.
return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
}
+static bool addOptional(StringRef name, uint64_t value,
+ std::vector<Defined *> &defined) {
+ Symbol *sym = symtab->find(name);
+ if (!sym || sym->isDefined())
+ return false;
+ sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL,
+ STV_HIDDEN, STT_FUNC, value,
+ /*size=*/0, /*section=*/nullptr});
+ defined.push_back(cast<Defined>(sym));
+ return true;
+}
+
+// If from is 14, write ${prefix}14: firstInsn; ${prefix}15:
+// firstInsn+0x200008; ...; ${prefix}31: firstInsn+(31-14)*0x200008; $tail
+// The labels are defined only if they exist in the symbol table.
+static void writeSequence(MutableArrayRef<uint32_t> buf, const char *prefix,
+ int from, uint32_t firstInsn,
+ ArrayRef<uint32_t> tail) {
+ std::vector<Defined *> defined;
+ char name[16];
+ int first;
+ uint32_t *ptr = buf.data();
+ for (int r = from; r < 32; ++r) {
+ format("%s%d", prefix, r).snprint(name, sizeof(name));
+ if (addOptional(name, 4 * (r - from), defined) && defined.size() == 1)
+ first = r - from;
+ write32(ptr++, firstInsn + 0x200008 * (r - from));
+ }
+ for (uint32_t insn : tail)
+ write32(ptr++, insn);
+ assert(ptr == &*buf.end());
+
+ if (defined.empty())
+ return;
+ // The full section content has the extent of [begin, end). We drop unused
+ // instructions and write [first,end).
+ auto *sec = make<InputSection>(
+ nullptr, SHF_ALLOC, SHT_PROGBITS, 4,
+ makeArrayRef(reinterpret_cast<uint8_t *>(buf.data() + first),
+ 4 * (buf.size() - first)),
+ ".text");
+ inputSections.push_back(sec);
+ for (Defined *sym : defined) {
+ sym->section = sec;
+ sym->value -= 4 * first;
+ }
+}
+
+// Implements some save and restore functions as described by ELF V2 ABI to be
+// compatible with GCC. With GCC -Os, when the number of call-saved registers
+// exceeds a certain threshold, GCC generates _savegpr0_* _restgpr0_* calls and
+// expects the linker to define them. See
+// https://sourceware.org/pipermail/binutils/2002-February/017444.html and
+// https://sourceware.org/pipermail/binutils/2004-August/036765.html . This is
+// weird because libgcc.a would be the natural place. The linker generation
+// approach has the advantage that the linker can generate multiple copies to
+// avoid long branch thunks. However, we don't consider the advantage
+// significant enough to complicate our trunk implementation, so we take the
+// simple approach and synthesize .text sections providing the implementation.
+void elf::addPPC64SaveRestore() {
+ static uint32_t savegpr0[20], restgpr0[21], savegpr1[19], restgpr1[19];
+ constexpr uint32_t blr = 0x4e800020, mtlr_0 = 0x7c0803a6;
+
+ // _restgpr0_14: ld 14, -144(1); _restgpr0_15: ld 15, -136(1); ...
+ // Tail: ld 0, 16(1); mtlr 0; blr
+ writeSequence(restgpr0, "_restgpr0_", 14, 0xe9c1ff70,
+ {0xe8010010, mtlr_0, blr});
+ // _restgpr1_14: ld 14, -144(12); _restgpr1_15: ld 15, -136(12); ...
+ // Tail: blr
+ writeSequence(restgpr1, "_restgpr1_", 14, 0xe9ccff70, {blr});
+ // _savegpr0_14: std 14, -144(1); _savegpr0_15: std 15, -136(1); ...
+ // Tail: std 0, 16(1); blr
+ writeSequence(savegpr0, "_savegpr0_", 14, 0xf9c1ff70, {0xf8010010, blr});
+ // _savegpr1_14: std 14, -144(12); _savegpr1_15: std 15, -136(12); ...
+ // Tail: blr
+ writeSequence(savegpr1, "_savegpr1_", 14, 0xf9ccff70, {blr});
+}
+
// Find the R_PPC64_ADDR64 in .rela.toc with matching offset.
template <typename ELFT>
static std::pair<Defined *, int64_t>
@@ -137,7 +216,7 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
// When accessing a symbol defined in another translation unit, compilers
// reserve a .toc entry, allocate a local label and generate toc-indirect
-// instuctions:
+// instructions:
//
// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA
// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry
@@ -155,8 +234,7 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
// ld/lwa 3, 0(3) # load the value from the address
//
// Returns true if the relaxation is performed.
-bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
- uint8_t *bufLoc) {
+bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) {
assert(config->tocOptimize);
if (rel.addend < 0)
return false;
@@ -186,8 +264,8 @@ bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
if (!isInt<32>(tocRelative))
return false;
- // Add PPC64TocOffset that will be subtracted by relocateOne().
- target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset);
+ // Add PPC64TocOffset that will be subtracted by PPC64::relocate().
+ target->relaxGot(bufLoc, rel, tocRelative + ppc64TocOffset);
return true;
}
@@ -205,7 +283,8 @@ public:
uint64_t pltEntryAddr) const override;
void writeIplt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
void writeGotHeader(uint8_t *buf) const override;
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
@@ -214,11 +293,16 @@ public:
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const override;
@@ -292,6 +376,20 @@ static uint32_t readFromHalf16(const uint8_t *loc) {
return read32(config->isLE ? loc : loc - 2);
}
+// The prefixed instruction is always a 4 byte prefix followed by a 4 byte
+// instruction. Therefore, the prefix is always in lower memory than the
+// instruction (regardless of endianness).
+// As a result, we need to shift the pieces around on little endian machines.
+static void writePrefixedInstruction(uint8_t *loc, uint64_t insn) {
+ insn = config->isLE ? insn << 32 | insn >> 32 : insn;
+ write64(loc, insn);
+}
+
+static uint64_t readPrefixedInstruction(const uint8_t *loc) {
+ uint64_t fullInstr = read64(loc);
+ return config->isLE ? (fullInstr << 32 | fullInstr >> 32) : fullInstr;
+}
+
PPC64::PPC64() {
copyRel = R_PPC64_COPY;
gotRel = R_PPC64_GLOB_DAT;
@@ -365,11 +463,11 @@ uint32_t PPC64::calcEFlags() const {
return 2;
}
-void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_PPC64_TOC16_HA:
// Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop".
- relocateOne(loc, type, val);
+ relocate(loc, rel, val);
break;
case R_PPC64_TOC16_LO_DS: {
// Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or
@@ -378,7 +476,7 @@ void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
if (getPrimaryOpCode(insn) != LD)
error("expected a 'ld' for got-indirect to toc-relative relaxing");
writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000);
- relocateOne(loc, R_PPC64_TOC16_LO, val);
+ relocateNoSym(loc, R_PPC64_TOC16_LO, val);
break;
}
default:
@@ -386,7 +484,8 @@ void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement.
// The general dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
@@ -402,14 +501,14 @@ void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, x@tprel@l
- switch (type) {
+ switch (rel.type) {
case R_PPC64_GOT_TLSGD16_HA:
writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO:
writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13
- relocateOne(loc, R_PPC64_TPREL16_HA, val);
+ relocateNoSym(loc, R_PPC64_TPREL16_HA, val);
break;
case R_PPC64_TLSGD:
write32(loc, 0x60000000); // nop
@@ -417,15 +516,16 @@ void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// Since we are relocating a half16 type relocation and Loc + 4 points to
// the start of an instruction we need to advance the buffer by an extra
// 2 bytes on BE.
- relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
- R_PPC64_TPREL16_LO, val);
+ relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
+ R_PPC64_TPREL16_LO, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement.
// The local dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
@@ -441,7 +541,7 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, 4096
- switch (type) {
+ switch (rel.type) {
case R_PPC64_GOT_TLSLD16_HA:
writeFromHalf16(loc, 0x60000000); // nop
break;
@@ -458,14 +558,14 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
- relocateOne(loc, type, val);
+ relocate(loc, rel, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
}
-unsigned getPPCDFormOp(unsigned secondaryOp) {
+unsigned elf::getPPCDFormOp(unsigned secondaryOp) {
switch (secondaryOp) {
case LBZX:
return LBZ;
@@ -490,7 +590,8 @@ unsigned getPPCDFormOp(unsigned secondaryOp) {
}
}
-void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// The initial exec code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x
@@ -511,7 +612,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
// indexed load or store instructions.
unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0;
- switch (type) {
+ switch (rel.type) {
case R_PPC64_GOT_TPREL16_HA:
write32(loc - offset, 0x60000000); // nop
break;
@@ -519,7 +620,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
case R_PPC64_GOT_TPREL16_DS: {
uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10
write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13
- relocateOne(loc, R_PPC64_TPREL16_HA, val);
+ relocateNoSym(loc, R_PPC64_TPREL16_HA, val);
break;
}
case R_PPC64_TLS: {
@@ -531,7 +632,7 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
if (dFormOp == 0)
error("unrecognized instruction for IE to LE R_PPC64_TLS");
write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF)));
- relocateOne(loc + offset, R_PPC64_TPREL16_LO, val);
+ relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val);
break;
}
default:
@@ -570,6 +671,8 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
case R_PPC64_TOC16_HI:
case R_PPC64_TOC16_LO:
return R_GOTREL;
+ case R_PPC64_GOT_PCREL34:
+ return R_GOT_PC;
case R_PPC64_TOC16_HA:
case R_PPC64_TOC16_LO_DS:
return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL;
@@ -578,11 +681,14 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
case R_PPC64_REL14:
case R_PPC64_REL24:
return R_PPC64_CALL_PLT;
+ case R_PPC64_REL24_NOTOC:
+ return R_PLT_PC;
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HA:
case R_PPC64_REL16_HI:
case R_PPC64_REL32:
case R_PPC64_REL64:
+ case R_PPC64_PCREL34:
return R_PC;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_HA:
@@ -770,11 +876,8 @@ static bool isTocOptType(RelType type) {
}
}
-void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- // We need to save the original relocation type to use in diagnostics, and
- // use the original type to determine if we should toc-optimize the
- // instructions being relocated.
- RelType originalType = type;
+void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ RelType type = rel.type;
bool shouldTocOptimize = isTocOptType(type);
// For dynamic thread pointer relative, toc-relative, and got-indirect
// relocations, proceed in terms of the corresponding ADDR16 relocation type.
@@ -782,27 +885,27 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
switch (type) {
case R_PPC64_ADDR14: {
- checkAlignment(loc, val, 4, type);
+ checkAlignment(loc, val, 4, rel);
// Preserve the AA/LK bits in the branch instruction
uint8_t aalk = loc[3];
write16(loc + 2, (aalk & 3) | (val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- checkIntUInt(loc, val, 16, originalType);
+ checkIntUInt(loc, val, 16, rel);
write16(loc, val);
break;
case R_PPC64_ADDR32:
- checkIntUInt(loc, val, 32, originalType);
+ checkIntUInt(loc, val, 32, rel);
write32(loc, val);
break;
case R_PPC64_ADDR16_DS:
case R_PPC64_TPREL16_DS: {
- checkInt(loc, val, 16, originalType);
+ checkInt(loc, val, 16, rel);
// DQ-form instructions use bits 28-31 as part of the instruction encoding
// DS-form instructions only use bits 30-31.
uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3;
- checkAlignment(loc, lo(val), mask + 1, originalType);
+ checkAlignment(loc, lo(val), mask + 1, rel);
write16(loc, (read16(loc) & mask) | lo(val));
} break;
case R_PPC64_ADDR16_HA:
@@ -857,7 +960,7 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
// DS-form instructions only use bits 30-31.
uint32_t insn = readFromHalf16(loc);
uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3;
- checkAlignment(loc, lo(val), mask + 1, originalType);
+ checkAlignment(loc, lo(val), mask + 1, rel);
if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) {
// When the high-adjusted part of a toc relocation evaluates to 0, it is
// changed into a nop. The lo part then needs to be updated to use the toc
@@ -873,11 +976,11 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
}
} break;
case R_PPC64_TPREL16:
- checkInt(loc, val, 16, originalType);
+ checkInt(loc, val, 16, rel);
write16(loc, val);
break;
case R_PPC64_REL32:
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32(loc, val);
break;
case R_PPC64_ADDR64:
@@ -887,21 +990,44 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break;
case R_PPC64_REL14: {
uint32_t mask = 0x0000FFFC;
- checkInt(loc, val, 16, type);
- checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 16, rel);
+ checkAlignment(loc, val, 4, rel);
write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
- case R_PPC64_REL24: {
+ case R_PPC64_REL24:
+ case R_PPC64_REL24_NOTOC: {
uint32_t mask = 0x03FFFFFC;
- checkInt(loc, val, 26, type);
- checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 26, rel);
+ checkAlignment(loc, val, 4, rel);
write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
case R_PPC64_DTPREL64:
write64(loc, val - dynamicThreadPointerOffset);
break;
+ case R_PPC64_PCREL34: {
+ const uint64_t si0Mask = 0x00000003ffff0000;
+ const uint64_t si1Mask = 0x000000000000ffff;
+ const uint64_t fullMask = 0x0003ffff0000ffff;
+ checkInt(loc, val, 34, rel);
+
+ uint64_t instr = readPrefixedInstruction(loc) & ~fullMask;
+ writePrefixedInstruction(loc, instr | ((val & si0Mask) << 16) |
+ (val & si1Mask));
+ break;
+ }
+ case R_PPC64_GOT_PCREL34: {
+ const uint64_t si0Mask = 0x00000003ffff0000;
+ const uint64_t si1Mask = 0x000000000000ffff;
+ const uint64_t fullMask = 0x0003ffff0000ffff;
+ checkInt(loc, val, 34, rel);
+
+ uint64_t instr = readPrefixedInstruction(loc) & ~fullMask;
+ writePrefixedInstruction(loc, instr | ((val & si0Mask) << 16) |
+ (val & si1Mask));
+ break;
+ }
default:
llvm_unreachable("unknown relocation");
}
@@ -909,13 +1035,30 @@ void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s, int64_t a) const {
- if (type != R_PPC64_REL14 && type != R_PPC64_REL24)
+ if (type != R_PPC64_REL14 && type != R_PPC64_REL24 &&
+ type != R_PPC64_REL24_NOTOC)
return false;
+ // FIXME: Remove the fatal error once the call protocol is implemented.
+ if (type == R_PPC64_REL24_NOTOC && s.isInPlt())
+ fatal("unimplemented feature: external function call with the reltype"
+ " R_PPC64_REL24_NOTOC");
+
// If a function is in the Plt it needs to be called with a call-stub.
if (s.isInPlt())
return true;
+ // FIXME: Remove the fatal error once the call protocol is implemented.
+ if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1)
+ fatal("unimplemented feature: local function call with the reltype"
+ " R_PPC64_REL24_NOTOC and the callee needs toc-pointer setup");
+
+ // This check looks at the st_other bits of the callee with relocation
+ // R_PPC64_REL14 or R_PPC64_REL24. If the value is 1, then the callee
+ // clobbers the TOC and we need an R2 save stub.
+ if (type != R_PPC64_REL24_NOTOC && (s.stOther >> 5) == 1)
+ return true;
+
// If a symbol is a weak undefined and we are compiling an executable
// it doesn't need a range-extending thunk since it can't be called.
if (s.isUndefWeak() && !config->shared)
@@ -941,7 +1084,7 @@ bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
int64_t offset = dst - src;
if (type == R_PPC64_REL14)
return isInt<16>(offset);
- if (type == R_PPC64_REL24)
+ if (type == R_PPC64_REL24 || type == R_PPC64_REL24_NOTOC)
return isInt<26>(offset);
llvm_unreachable("unsupported relocation type used in branch");
}
@@ -972,12 +1115,13 @@ RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data,
// thread pointer.
// Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is
// used as the relaxation hint for both steps 2 and 3.
-void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_PPC64_GOT_TLSGD16_HA:
// This is relaxed from addis rT, r2, sym@got@tlsgd@ha to
// addis rT, r2, sym@got@tprel@ha.
- relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val);
+ relocateNoSym(loc, R_PPC64_GOT_TPREL16_HA, val);
return;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO: {
@@ -985,7 +1129,7 @@ void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// ld r3, sym@got@tprel@l(rA)
uint32_t ra = (readFromHalf16(loc) & (0x1f << 16));
writeFromHalf16(loc, 0xe8600000 | ra);
- relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
+ relocateNoSym(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
return;
}
case R_PPC64_TLSGD:
@@ -1104,10 +1248,7 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
return true;
}
-TargetInfo *getPPC64TargetInfo() {
+TargetInfo *elf::getPPC64TargetInfo() {
static PPC64 target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/RISCV.cpp b/contrib/llvm-project/lld/ELF/Arch/RISCV.cpp
index 527f9db0ef29..b340fd00deee 100644
--- a/contrib/llvm-project/lld/ELF/Arch/RISCV.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/RISCV.cpp
@@ -15,9 +15,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
@@ -33,7 +32,8 @@ public:
RelType getDynRel(RelType type) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // end anonymous namespace
@@ -76,6 +76,7 @@ RISCV::RISCV() {
noneRel = R_RISCV_NONE;
pltRel = R_RISCV_JUMP_SLOT;
relativeRel = R_RISCV_RELATIVE;
+ iRelativeRel = R_RISCV_IRELATIVE;
if (config->is64) {
symbolicRel = R_RISCV_64;
tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64;
@@ -257,11 +258,10 @@ static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
return (v & ((1ULL << (begin + 1)) - 1)) >> end;
}
-void RISCV::relocateOne(uint8_t *loc, const RelType type,
- const uint64_t val) const {
+void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
const unsigned bits = config->wordsize * 8;
- switch (type) {
+ switch (rel.type) {
case R_RISCV_32:
write32le(loc, val);
return;
@@ -270,8 +270,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
return;
case R_RISCV_RVC_BRANCH: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 8, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 8, rel);
+ checkAlignment(loc, val, 2, rel);
uint16_t insn = read16le(loc) & 0xE383;
uint16_t imm8 = extractBits(val, 8, 8) << 12;
uint16_t imm4_3 = extractBits(val, 4, 3) << 10;
@@ -285,8 +285,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
}
case R_RISCV_RVC_JUMP: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 11, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 11, rel);
+ checkAlignment(loc, val, 2, rel);
uint16_t insn = read16le(loc) & 0xE003;
uint16_t imm11 = extractBits(val, 11, 11) << 12;
uint16_t imm4 = extractBits(val, 4, 4) << 11;
@@ -304,7 +304,7 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
case R_RISCV_RVC_LUI: {
int64_t imm = SignExtend64(val + 0x800, bits) >> 12;
- checkInt(loc, imm, 6, type);
+ checkInt(loc, imm, 6, rel);
if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
write16le(loc, (read16le(loc) & 0x0F83) | 0x4000);
} else {
@@ -316,8 +316,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
}
case R_RISCV_JAL: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 20, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 20, rel);
+ checkAlignment(loc, val, 2, rel);
uint32_t insn = read32le(loc) & 0xFFF;
uint32_t imm20 = extractBits(val, 20, 20) << 31;
@@ -331,8 +331,8 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
}
case R_RISCV_BRANCH: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 12, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 12, rel);
+ checkAlignment(loc, val, 2, rel);
uint32_t insn = read32le(loc) & 0x1FFF07F;
uint32_t imm12 = extractBits(val, 12, 12) << 31;
@@ -349,10 +349,10 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
int64_t hi = SignExtend64(val + 0x800, bits) >> 12;
- checkInt(loc, hi, 20, type);
+ checkInt(loc, hi, 20, rel);
if (isInt<20>(hi)) {
- relocateOne(loc, R_RISCV_PCREL_HI20, val);
- relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val);
+ relocateNoSym(loc, R_RISCV_PCREL_HI20, val);
+ relocateNoSym(loc + 4, R_RISCV_PCREL_LO12_I, val);
}
return;
}
@@ -364,7 +364,7 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
case R_RISCV_TPREL_HI20:
case R_RISCV_HI20: {
uint64_t hi = val + 0x800;
- checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type);
+ checkInt(loc, SignExtend64(hi, bits) >> 12, 20, rel);
write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
return;
}
@@ -445,10 +445,7 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
}
}
-TargetInfo *getRISCVTargetInfo() {
+TargetInfo *elf::getRISCVTargetInfo() {
static RISCV target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/SPARCV9.cpp b/contrib/llvm-project/lld/ELF/Arch/SPARCV9.cpp
index 08ef52099de9..f137c21fc898 100644
--- a/contrib/llvm-project/lld/ELF/Arch/SPARCV9.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/SPARCV9.cpp
@@ -16,9 +16,8 @@
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class SPARCV9 final : public TargetInfo {
@@ -28,7 +27,8 @@ public:
const uint8_t *loc) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -54,6 +54,14 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
case R_SPARC_UA32:
case R_SPARC_64:
case R_SPARC_UA64:
+ case R_SPARC_H44:
+ case R_SPARC_M44:
+ case R_SPARC_L44:
+ case R_SPARC_HH22:
+ case R_SPARC_HM10:
+ case R_SPARC_LM22:
+ case R_SPARC_HI22:
+ case R_SPARC_LO10:
return R_ABS;
case R_SPARC_PC10:
case R_SPARC_PC22:
@@ -68,6 +76,9 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
return R_PLT_PC;
case R_SPARC_NONE:
return R_NONE;
+ case R_SPARC_TLS_LE_HIX22:
+ case R_SPARC_TLS_LE_LOX10:
+ return R_TLS;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
@@ -75,38 +86,45 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
}
}
-void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
write32be(loc, val);
break;
case R_SPARC_DISP32:
// V-disp32
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32be(loc, val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
- checkUInt(loc, val, 22, type);
+ checkUInt(loc, val, 22, rel);
write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
break;
case R_SPARC_GOT22:
case R_SPARC_PC22:
+ case R_SPARC_LM22:
// T-imm22
write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
break;
+ case R_SPARC_HI22:
+ // V-imm22
+ checkUInt(loc, val >> 10, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+ break;
case R_SPARC_WDISP19:
// V-disp19
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
@@ -114,11 +132,45 @@ void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
// T-simm10
write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
break;
+ case R_SPARC_LO10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
+ break;
case R_SPARC_64:
case R_SPARC_UA64:
// V-xword64
write64be(loc, val);
break;
+ case R_SPARC_HH22:
+ // V-imm22
+ checkUInt(loc, val >> 42, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 42) & 0x003fffff));
+ break;
+ case R_SPARC_HM10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | ((val >> 32) & 0x000003ff));
+ break;
+ case R_SPARC_H44:
+ // V-imm22
+ checkUInt(loc, val >> 22, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 22) & 0x003fffff));
+ break;
+ case R_SPARC_M44:
+ // T-imm10
+ write32be(loc, (read32be(loc) & ~0x000003ff) | ((val >> 12) & 0x000003ff));
+ break;
+ case R_SPARC_L44:
+ // T-imm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00000fff));
+ break;
+ case R_SPARC_TLS_LE_HIX22:
+ // T-imm22
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
+ break;
+ case R_SPARC_TLS_LE_LOX10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00);
+ break;
default:
llvm_unreachable("unknown relocation");
}
@@ -139,14 +191,11 @@ void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/,
memcpy(buf, pltData, sizeof(pltData));
uint64_t off = pltEntryAddr - in.plt->getVA();
- relocateOne(buf, R_SPARC_22, off);
- relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
+ relocateNoSym(buf, R_SPARC_22, off);
+ relocateNoSym(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
}
-TargetInfo *getSPARCV9TargetInfo() {
+TargetInfo *elf::getSPARCV9TargetInfo() {
static SPARCV9 target;
return &target;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/X86.cpp b/contrib/llvm-project/lld/ELF/Arch/X86.cpp
index b4daedc0f5dc..8c8824d53cce 100644
--- a/contrib/llvm-project/lld/ELF/Arch/X86.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/X86.cpp
@@ -16,9 +16,8 @@
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class X86 : public TargetInfo {
@@ -35,14 +34,19 @@ public:
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
@@ -262,21 +266,21 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
}
}
-void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_386_8:
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
// being used for some 16-bit programs such as boot loaders, so
// we want to support them.
- checkIntUInt(loc, val, 8, type);
+ checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_386_PC8:
- checkInt(loc, val, 8, type);
+ checkInt(loc, val, 8, rel);
*loc = val;
break;
case R_386_16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_386_PC16:
@@ -290,7 +294,7 @@ void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
// current location subtracted from it.
// We just check that Val fits in 17 bits. This misses some cases, but
// should have no false positives.
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
write16le(loc, val);
break;
case R_386_32:
@@ -312,7 +316,7 @@ void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
default:
@@ -320,7 +324,7 @@ void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
@@ -335,7 +339,7 @@ void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
write32le(loc + 5, val);
}
-void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
@@ -352,14 +356,15 @@ void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
-void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
// position dependent code.
uint8_t reg = (loc[-1] >> 3) & 7;
- if (type == R_386_TLS_IE) {
+ if (rel.type == R_386_TLS_IE) {
if (loc[-1] == 0xa1) {
// "movl foo@indntpoff,%eax" -> "movl $foo,%eax"
// This case is different from the generic case below because
@@ -375,7 +380,7 @@ void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
loc[-1] = 0xc0 | reg;
}
} else {
- assert(type == R_386_TLS_GOTIE);
+ assert(rel.type == R_386_TLS_GOTIE);
if (loc[-2] == 0x8b) {
// "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg"
loc[-2] = 0xc7;
@@ -389,8 +394,9 @@ void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
write32le(loc, val);
}
-void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_386_TLS_LDO_32) {
+void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_386_TLS_LDO_32) {
write32le(loc, val);
return;
}
@@ -608,7 +614,7 @@ void RetpolineNoPic::writePlt(uint8_t *buf, const Symbol &sym,
write32le(buf + 22, -off - 26);
}
-TargetInfo *getX86TargetInfo() {
+TargetInfo *elf::getX86TargetInfo() {
if (config->zRetpolineplt) {
if (config->isPic) {
static RetpolinePic t;
@@ -626,6 +632,3 @@ TargetInfo *getX86TargetInfo() {
static X86 t;
return &t;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Arch/X86_64.cpp b/contrib/llvm-project/lld/ELF/Arch/X86_64.cpp
index 74b72eb91293..24711ec210a4 100644
--- a/contrib/llvm-project/lld/ELF/Arch/X86_64.cpp
+++ b/contrib/llvm-project/lld/ELF/Arch/X86_64.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
@@ -18,9 +19,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class X86_64 : public TargetInfo {
@@ -35,20 +35,44 @@ public:
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void applyJumpInstrMod(uint8_t *loc, JumpModType type,
+ unsigned size) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const override;
+ bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
+ InputSection *nextIS) const override;
};
} // namespace
+// This is vector of NOP instructions of sizes from 1 to 8 bytes. The
+// appropriately sized instructions are used to fill the gaps between sections
+// which are executed during fall through.
+static const std::vector<std::vector<uint8_t>> nopInstructions = {
+ {0x90},
+ {0x66, 0x90},
+ {0x0f, 0x1f, 0x00},
+ {0x0f, 0x1f, 0x40, 0x00},
+ {0x0f, 0x1f, 0x44, 0x00, 0x00},
+ {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00},
+ {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00},
+ {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
X86_64::X86_64() {
copyRel = R_X86_64_COPY;
gotRel = R_X86_64_GLOB_DAT;
@@ -65,6 +89,7 @@ X86_64::X86_64() {
pltEntrySize = 16;
ipltEntrySize = 16;
trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+ nopInstrs = nopInstructions;
// Align to the large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
@@ -73,6 +98,216 @@ X86_64::X86_64() {
int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; }
+// Opcodes for the different X86_64 jmp instructions.
+enum JmpInsnOpcode : uint32_t {
+ J_JMP_32,
+ J_JNE_32,
+ J_JE_32,
+ J_JG_32,
+ J_JGE_32,
+ J_JB_32,
+ J_JBE_32,
+ J_JL_32,
+ J_JLE_32,
+ J_JA_32,
+ J_JAE_32,
+ J_UNKNOWN,
+};
+
+// Given the first (optional) and second byte of the insn's opcode, this
+// returns the corresponding enum value.
+static JmpInsnOpcode getJmpInsnType(const uint8_t *first,
+ const uint8_t *second) {
+ if (*second == 0xe9)
+ return J_JMP_32;
+
+ if (first == nullptr)
+ return J_UNKNOWN;
+
+ if (*first == 0x0f) {
+ switch (*second) {
+ case 0x84:
+ return J_JE_32;
+ case 0x85:
+ return J_JNE_32;
+ case 0x8f:
+ return J_JG_32;
+ case 0x8d:
+ return J_JGE_32;
+ case 0x82:
+ return J_JB_32;
+ case 0x86:
+ return J_JBE_32;
+ case 0x8c:
+ return J_JL_32;
+ case 0x8e:
+ return J_JLE_32;
+ case 0x87:
+ return J_JA_32;
+ case 0x83:
+ return J_JAE_32;
+ }
+ }
+ return J_UNKNOWN;
+}
+
+// Return the relocation index for input section IS with a specific Offset.
+// Returns the maximum size of the vector if no such relocation is found.
+static unsigned getRelocationWithOffset(const InputSection &is,
+ uint64_t offset) {
+ unsigned size = is.relocations.size();
+ for (unsigned i = size - 1; i + 1 > 0; --i) {
+ if (is.relocations[i].offset == offset && is.relocations[i].expr != R_NONE)
+ return i;
+ }
+ return size;
+}
+
+// Returns true if R corresponds to a relocation used for a jump instruction.
+// TODO: Once special relocations for relaxable jump instructions are available,
+// this should be modified to use those relocations.
+static bool isRelocationForJmpInsn(Relocation &R) {
+ return R.type == R_X86_64_PLT32 || R.type == R_X86_64_PC32 ||
+ R.type == R_X86_64_PC8;
+}
+
+// Return true if Relocation R points to the first instruction in the
+// next section.
+// TODO: Delete this once psABI reserves a new relocation type for fall thru
+// jumps.
+static bool isFallThruRelocation(InputSection &is, InputFile *file,
+ InputSection *nextIS, Relocation &r) {
+ if (!isRelocationForJmpInsn(r))
+ return false;
+
+ uint64_t addrLoc = is.getOutputSection()->addr + is.outSecOff + r.offset;
+ uint64_t targetOffset = InputSectionBase::getRelocTargetVA(
+ file, r.type, r.addend, addrLoc, *r.sym, r.expr);
+
+ // If this jmp is a fall thru, the target offset is the beginning of the
+ // next section.
+ uint64_t nextSectionOffset =
+ nextIS->getOutputSection()->addr + nextIS->outSecOff;
+ return (addrLoc + 4 + targetOffset) == nextSectionOffset;
+}
+
+// Return the jmp instruction opcode that is the inverse of the given
+// opcode. For example, JE inverted is JNE.
+static JmpInsnOpcode invertJmpOpcode(const JmpInsnOpcode opcode) {
+ switch (opcode) {
+ case J_JE_32:
+ return J_JNE_32;
+ case J_JNE_32:
+ return J_JE_32;
+ case J_JG_32:
+ return J_JLE_32;
+ case J_JGE_32:
+ return J_JL_32;
+ case J_JB_32:
+ return J_JAE_32;
+ case J_JBE_32:
+ return J_JA_32;
+ case J_JL_32:
+ return J_JGE_32;
+ case J_JLE_32:
+ return J_JG_32;
+ case J_JA_32:
+ return J_JBE_32;
+ case J_JAE_32:
+ return J_JB_32;
+ default:
+ return J_UNKNOWN;
+ }
+}
+
+// Deletes direct jump instruction in input sections that jumps to the
+// following section as it is not required. If there are two consecutive jump
+// instructions, it checks if they can be flipped and one can be deleted.
+// For example:
+// .section .text
+// a.BB.foo:
+// ...
+// 10: jne aa.BB.foo
+// 16: jmp bar
+// aa.BB.foo:
+// ...
+//
+// can be converted to:
+// a.BB.foo:
+// ...
+// 10: je bar #jne flipped to je and the jmp is deleted.
+// aa.BB.foo:
+// ...
+bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file,
+ InputSection *nextIS) const {
+ const unsigned sizeOfDirectJmpInsn = 5;
+
+ if (nextIS == nullptr)
+ return false;
+
+ if (is.getSize() < sizeOfDirectJmpInsn)
+ return false;
+
+ // If this jmp insn can be removed, it is the last insn and the
+ // relocation is 4 bytes before the end.
+ unsigned rIndex = getRelocationWithOffset(is, is.getSize() - 4);
+ if (rIndex == is.relocations.size())
+ return false;
+
+ Relocation &r = is.relocations[rIndex];
+
+ // Check if the relocation corresponds to a direct jmp.
+ const uint8_t *secContents = is.data().data();
+ // If it is not a direct jmp instruction, there is nothing to do here.
+ if (*(secContents + r.offset - 1) != 0xe9)
+ return false;
+
+ if (isFallThruRelocation(is, file, nextIS, r)) {
+ // This is a fall thru and can be deleted.
+ r.expr = R_NONE;
+ r.offset = 0;
+ is.drop_back(sizeOfDirectJmpInsn);
+ is.nopFiller = true;
+ return true;
+ }
+
+ // Now, check if flip and delete is possible.
+ const unsigned sizeOfJmpCCInsn = 6;
+ // To flip, there must be atleast one JmpCC and one direct jmp.
+ if (is.getSize() < sizeOfDirectJmpInsn + sizeOfJmpCCInsn)
+ return 0;
+
+ unsigned rbIndex =
+ getRelocationWithOffset(is, (is.getSize() - sizeOfDirectJmpInsn - 4));
+ if (rbIndex == is.relocations.size())
+ return 0;
+
+ Relocation &rB = is.relocations[rbIndex];
+
+ const uint8_t *jmpInsnB = secContents + rB.offset - 1;
+ JmpInsnOpcode jmpOpcodeB = getJmpInsnType(jmpInsnB - 1, jmpInsnB);
+ if (jmpOpcodeB == J_UNKNOWN)
+ return false;
+
+ if (!isFallThruRelocation(is, file, nextIS, rB))
+ return false;
+
+ // jmpCC jumps to the fall thru block, the branch can be flipped and the
+ // jmp can be deleted.
+ JmpInsnOpcode jInvert = invertJmpOpcode(jmpOpcodeB);
+ if (jInvert == J_UNKNOWN)
+ return false;
+ is.jumpInstrMods.push_back({jInvert, (rB.offset - 1), 4});
+ // Move R's values to rB except the offset.
+ rB = {r.expr, r.type, rB.offset, r.addend, r.sym};
+ // Cancel R
+ r.expr = R_NONE;
+ r.offset = 0;
+ is.drop_back(sizeOfDirectJmpInsn);
+ is.nopFiller = true;
+ return true;
+}
+
RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
if (type == R_X86_64_GOTTPOFF)
@@ -177,8 +412,9 @@ RelType X86_64::getDynRel(RelType type) const {
return R_X86_64_NONE;
}
-void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_X86_64_TLSGD) {
+void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
// leaq x@tlsgd(%rip), %rdi
@@ -201,7 +437,7 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// lea x@tlsgd(%rip), %rax
// call *(%rax)
// to the following two instructions.
- assert(type == R_X86_64_GOTPC32_TLSDESC);
+ assert(rel.type == R_X86_64_GOTPC32_TLSDESC);
if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
"in callq *x@tlsdesc(%rip), %rax");
@@ -217,8 +453,9 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
}
}
-void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_X86_64_TLSGD) {
+void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
// leaq x@tlsgd(%rip), %rdi
@@ -241,7 +478,7 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// lea x@tlsgd(%rip), %rax
// call *(%rax)
// to the following two instructions.
- assert(type == R_X86_64_GOTPC32_TLSDESC);
+ assert(rel.type == R_X86_64_GOTPC32_TLSDESC);
if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
"in callq *x@tlsdesc(%rip), %rax");
@@ -258,7 +495,8 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
-void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &,
+ uint64_t val) const {
uint8_t *inst = loc - 3;
uint8_t reg = loc[-1] >> 3;
uint8_t *regSlot = loc - 1;
@@ -299,12 +537,13 @@ void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
write32le(loc, val + 4);
}
-void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_X86_64_DTPOFF64) {
+void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_X86_64_DTPOFF64) {
write64le(loc, val);
return;
}
- if (type == R_X86_64_DTPOFF32) {
+ if (rel.type == R_X86_64_DTPOFF32) {
write32le(loc, val);
return;
}
@@ -347,26 +586,114 @@ void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
"expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD");
}
-void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+// A JumpInstrMod at a specific offset indicates that the jump instruction
+// opcode at that offset must be modified. This is specifically used to relax
+// jump instructions with basic block sections. This function looks at the
+// JumpMod and effects the change.
+void X86_64::applyJumpInstrMod(uint8_t *loc, JumpModType type,
+ unsigned size) const {
switch (type) {
+ case J_JMP_32:
+ if (size == 4)
+ *loc = 0xe9;
+ else
+ *loc = 0xeb;
+ break;
+ case J_JE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x84;
+ } else
+ *loc = 0x74;
+ break;
+ case J_JNE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x85;
+ } else
+ *loc = 0x75;
+ break;
+ case J_JG_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8f;
+ } else
+ *loc = 0x7f;
+ break;
+ case J_JGE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8d;
+ } else
+ *loc = 0x7d;
+ break;
+ case J_JB_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x82;
+ } else
+ *loc = 0x72;
+ break;
+ case J_JBE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x86;
+ } else
+ *loc = 0x76;
+ break;
+ case J_JL_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8c;
+ } else
+ *loc = 0x7c;
+ break;
+ case J_JLE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8e;
+ } else
+ *loc = 0x7e;
+ break;
+ case J_JA_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x87;
+ } else
+ *loc = 0x77;
+ break;
+ case J_JAE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x83;
+ } else
+ *loc = 0x73;
+ break;
+ case J_UNKNOWN:
+ llvm_unreachable("Unknown Jump Relocation");
+ }
+}
+
+void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_X86_64_8:
- checkIntUInt(loc, val, 8, type);
+ checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_X86_64_PC8:
- checkInt(loc, val, 8, type);
+ checkInt(loc, val, 8, rel);
*loc = val;
break;
case R_X86_64_16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_X86_64_PC16:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_X86_64_32:
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_X86_64_32S:
@@ -384,7 +711,7 @@ void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_X86_64_64:
@@ -495,7 +822,7 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op,
write32le(loc, val);
}
-void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+void X86_64::relaxGot(uint8_t *loc, const Relocation &, uint64_t val) const {
const uint8_t op = loc[-2];
const uint8_t modRm = loc[-1];
@@ -758,7 +1085,4 @@ static TargetInfo *getTargetInfo() {
return &t;
}
-TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); }
-
-} // namespace elf
-} // namespace lld
+TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
diff --git a/contrib/llvm-project/lld/ELF/CallGraphSort.cpp b/contrib/llvm-project/lld/ELF/CallGraphSort.cpp
index 6dad7c965f1a..21c641b5161f 100644
--- a/contrib/llvm-project/lld/ELF/CallGraphSort.cpp
+++ b/contrib/llvm-project/lld/ELF/CallGraphSort.cpp
@@ -48,9 +48,8 @@
#include <numeric>
using namespace llvm;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
struct Edge {
@@ -263,11 +262,8 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
// Sort sections by the profile data provided by -callgraph-profile-file
//
// This first builds a call graph based on the profile data then merges sections
-// according to the C³ huristic. 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.
-DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder() {
+DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
return CallGraphSort().run();
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Config.h b/contrib/llvm-project/lld/ELF/Config.h
index ef1edbcd1992..e74a4a0c5b22 100644
--- a/contrib/llvm-project/lld/ELF/Config.h
+++ b/contrib/llvm-project/lld/ELF/Config.h
@@ -17,6 +17,7 @@
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/GlobPattern.h"
#include <atomic>
#include <vector>
@@ -90,6 +91,7 @@ struct Configuration {
uint32_t andFeatures = 0;
llvm::CachePruningPolicy thinLTOCachePolicy;
llvm::StringMap<uint64_t> sectionStartMap;
+ llvm::StringRef bfdname;
llvm::StringRef chroot;
llvm::StringRef dynamicLinker;
llvm::StringRef dwoDir;
@@ -108,11 +110,13 @@ struct Configuration {
llvm::StringRef optRemarksPasses;
llvm::StringRef optRemarksFormat;
llvm::StringRef progName;
+ llvm::StringRef printArchiveStats;
llvm::StringRef printSymbolOrder;
llvm::StringRef soName;
llvm::StringRef sysroot;
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOIndexOnlyArg;
+ llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
std::string rpath;
@@ -121,6 +125,7 @@ struct Configuration {
std::vector<llvm::StringRef> filterList;
std::vector<llvm::StringRef> searchPaths;
std::vector<llvm::StringRef> symbolOrderingFile;
+ std::vector<llvm::StringRef> thinLTOModulesToCompile;
std::vector<llvm::StringRef> undefined;
std::vector<SymbolVersion> dynamicList;
std::vector<uint8_t> buildIdVector;
@@ -140,6 +145,7 @@ struct Configuration {
bool checkSections;
bool compressDebugSections;
bool cref;
+ std::vector<std::pair<llvm::GlobPattern, uint64_t>> deadRelocInNonAlloc;
bool defineCommon;
bool demangle = true;
bool dependentLibraries;
@@ -152,19 +158,20 @@ struct Configuration {
bool exportDynamic;
bool fixCortexA53Errata843419;
bool fixCortexA8;
- bool forceBTI;
bool formatBinary = false;
bool gcSections;
bool gdbIndex;
bool gnuHash = false;
bool gnuUnique;
- bool hasDynamicList = false;
bool hasDynSymTab;
bool ignoreDataAddressEquality;
bool ignoreFunctionAddressEquality;
bool ltoCSProfileGenerate;
bool ltoDebugPassManager;
+ bool ltoEmitAsm;
bool ltoNewPassManager;
+ bool ltoUniqueBasicBlockSectionNames;
+ bool ltoWholeProgramVisibility;
bool mergeArmExidx;
bool mipsN32Abi = false;
bool mmapOutputFile;
@@ -174,8 +181,8 @@ struct Configuration {
bool nostdlib;
bool oFormatBinary;
bool omagic;
+ bool optimizeBBJumps;
bool optRemarksWithHotness;
- bool pacPlt;
bool picThunk;
bool pie;
bool printGcSections;
@@ -183,18 +190,23 @@ struct Configuration {
bool relocatable;
bool relrPackDynRelocs;
bool saveTemps;
+ llvm::Optional<uint32_t> shuffleSectionSeed;
bool singleRoRx;
bool shared;
+ bool symbolic;
bool isStatic = false;
bool sysvHash = false;
bool target1Rel;
bool trace;
bool thinLTOEmitImportsFiles;
bool thinLTOIndexOnly;
+ bool timeTraceEnabled;
bool tocOptimize;
bool undefinedVersion;
+ bool unique;
bool useAndroidRelrTags = false;
bool warnBackrefs;
+ std::vector<llvm::GlobPattern> warnBackrefsExclude;
bool warnCommon;
bool warnIfuncTextrel;
bool warnMissingEntry;
@@ -202,6 +214,7 @@ struct Configuration {
bool writeAddends;
bool zCombreloc;
bool zCopyreloc;
+ bool zForceBti;
bool zForceIbt;
bool zGlobal;
bool zHazardplt;
@@ -214,9 +227,11 @@ struct Configuration {
bool zNodlopen;
bool zNow;
bool zOrigin;
+ bool zPacPlt;
bool zRelro;
bool zRodynamic;
bool zShstk;
+ uint8_t zStartStopVisibility;
bool zText;
bool zRetpolineplt;
bool zWxneeded;
@@ -241,7 +256,8 @@ struct Configuration {
unsigned ltoPartitions;
unsigned ltoo;
unsigned optimize;
- unsigned thinLTOJobs;
+ StringRef thinLTOJobs;
+ unsigned timeTraceGranularity;
int32_t splitStackAdjustSize;
// The following config options do not directly correspond to any
diff --git a/contrib/llvm-project/lld/ELF/DWARF.cpp b/contrib/llvm-project/lld/ELF/DWARF.cpp
index a00189a0e3a2..24c44730bf64 100644
--- a/contrib/llvm-project/lld/ELF/DWARF.cpp
+++ b/contrib/llvm-project/lld/ELF/DWARF.cpp
@@ -22,9 +22,9 @@
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
for (InputSectionBase *sec : obj->getSections()) {
if (!sec)
@@ -36,6 +36,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
.Case(".debug_gnu_pubnames", &gnuPubnamesSection)
.Case(".debug_gnu_pubtypes", &gnuPubtypesSection)
.Case(".debug_info", &infoSection)
+ .Case(".debug_loclists", &loclistsSection)
.Case(".debug_ranges", &rangesSection)
.Case(".debug_rnglists", &rnglistsSection)
.Case(".debug_str_offsets", &strOffsetsSection)
@@ -99,15 +100,9 @@ LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos,
// its zero value will terminate the decoding of .debug_ranges prematurely.
Symbol &s = file->getRelocTargetSym(rel);
uint64_t val = 0;
- if (auto *dr = dyn_cast<Defined>(&s)) {
+ if (auto *dr = dyn_cast<Defined>(&s))
val = dr->value;
- // FIXME: We should be consistent about always adding the file
- // offset or not.
- if (dr->section->flags & ELF::SHF_ALLOC)
- val += cast<InputSection>(dr->section)->getOffsetInFile();
- }
-
DataRefImpl d;
d.p = getAddend<ELFT>(rel);
return RelocAddrEntry{secIndex, RelocationRef(d, nullptr),
@@ -124,10 +119,7 @@ Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s,
return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
}
-template class LLDDwarfObj<ELF32LE>;
-template class LLDDwarfObj<ELF32BE>;
-template class LLDDwarfObj<ELF64LE>;
-template class LLDDwarfObj<ELF64BE>;
-
-} // namespace elf
-} // namespace lld
+template class elf::LLDDwarfObj<ELF32LE>;
+template class elf::LLDDwarfObj<ELF32BE>;
+template class elf::LLDDwarfObj<ELF64LE>;
+template class elf::LLDDwarfObj<ELF64BE>;
diff --git a/contrib/llvm-project/lld/ELF/DWARF.h b/contrib/llvm-project/lld/ELF/DWARF.h
index 51ec9092f172..a12dae6e9960 100644
--- a/contrib/llvm-project/lld/ELF/DWARF.h
+++ b/contrib/llvm-project/lld/ELF/DWARF.h
@@ -32,6 +32,10 @@ public:
f(infoSection);
}
+ const llvm::DWARFSection &getLoclistsSection() const override {
+ return loclistsSection;
+ }
+
const llvm::DWARFSection &getRangesSection() const override {
return rangesSection;
}
@@ -52,11 +56,11 @@ public:
return addrSection;
}
- const llvm::DWARFSection &getGnuPubnamesSection() const override {
+ const LLDDWARFSection &getGnuPubnamesSection() const override {
return gnuPubnamesSection;
}
- const llvm::DWARFSection &getGnuPubtypesSection() const override {
+ const LLDDWARFSection &getGnuPubtypesSection() const override {
return gnuPubtypesSection;
}
@@ -81,6 +85,7 @@ private:
LLDDWARFSection gnuPubnamesSection;
LLDDWARFSection gnuPubtypesSection;
LLDDWARFSection infoSection;
+ LLDDWARFSection loclistsSection;
LLDDWARFSection rangesSection;
LLDDWARFSection rnglistsSection;
LLDDWARFSection strOffsetsSection;
diff --git a/contrib/llvm-project/lld/ELF/Driver.cpp b/contrib/llvm-project/lld/ELF/Driver.cpp
index 6de9698bb2c8..4637a3b306da 100644
--- a/contrib/llvm-project/lld/ELF/Driver.cpp
+++ b/contrib/llvm-project/lld/ELF/Driver.cpp
@@ -43,7 +43,6 @@
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h"
@@ -53,9 +52,11 @@
#include "llvm/Support/Compression.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <utility>
@@ -65,18 +66,17 @@ using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys;
using namespace llvm::support;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
-
-Configuration *config;
-LinkerDriver *driver;
+Configuration *elf::config;
+LinkerDriver *elf::driver;
static void setConfigs(opt::InputArgList &args);
static void readConfigs(opt::InputArgList &args);
-bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
- raw_ostream &stderrOS) {
+bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
+ raw_ostream &stdoutOS, raw_ostream &stderrOS) {
lld::stdoutOS = &stdoutOS;
lld::stderrOS = &stderrOS;
@@ -89,10 +89,13 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
inputSections.clear();
outputSections.clear();
+ archiveFiles.clear();
binaryFiles.clear();
bitcodeFiles.clear();
+ lazyObjFiles.clear();
objectFiles.clear();
sharedFiles.clear();
+ backwardReferences.clear();
config = make<Configuration>();
driver = make<LinkerDriver>();
@@ -147,6 +150,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) {
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
.Case("elf_i386", {ELF32LEKind, EM_386})
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
+ .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9})
.Default({ELFNoneKind, EM_NONE});
if (ret.first == ELFNoneKind)
@@ -350,9 +354,9 @@ static void checkOptions() {
error("-z force-ibt may not be used with -z retpolineplt");
if (config->emachine != EM_AARCH64) {
- if (config->pacPlt)
+ if (config->zPacPlt)
error("-z pac-plt only supported on AArch64");
- if (config->forceBTI)
+ if (config->zForceBti)
error("-z force-bti only supported on AArch64");
}
}
@@ -407,6 +411,24 @@ static GnuStackKind getZGnuStack(opt::InputArgList &args) {
return GnuStackKind::NoExec;
}
+static uint8_t getZStartStopVisibility(opt::InputArgList &args) {
+ for (auto *arg : args.filtered_reverse(OPT_z)) {
+ std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+ if (kv.first == "start-stop-visibility") {
+ if (kv.second == "default")
+ return STV_DEFAULT;
+ else if (kv.second == "internal")
+ return STV_INTERNAL;
+ else if (kv.second == "hidden")
+ return STV_HIDDEN;
+ else if (kv.second == "protected")
+ return STV_PROTECTED;
+ error("unknown -z start-stop-visibility= value: " + StringRef(kv.second));
+ }
+ }
+ return STV_PROTECTED;
+}
+
static bool isKnownZFlag(StringRef s) {
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
s == "execstack" || s == "force-bti" || s == "force-ibt" ||
@@ -418,11 +440,13 @@ static bool isKnownZFlag(StringRef s) {
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
s == "nognustack" || s == "nokeep-text-section-prefix" ||
s == "norelro" || s == "noseparate-code" || s == "notext" ||
- s == "now" || s == "origin" || s == "pac-plt" || s == "relro" ||
- s == "retpolineplt" || s == "rodynamic" || s == "shstk" ||
- s == "text" || s == "undefs" || s == "wxneeded" ||
- s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
- s.startswith("stack-size=");
+ s == "now" || s == "origin" || s == "pac-plt" || s == "rel" ||
+ s == "rela" || s == "relro" || s == "retpolineplt" ||
+ s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
+ s == "wxneeded" || s.startswith("common-page-size=") ||
+ s.startswith("dead-reloc-in-nonalloc=") ||
+ s.startswith("max-page-size=") || s.startswith("stack-size=") ||
+ s.startswith("start-stop-visibility=");
}
// Report an error for an unknown -z option.
@@ -487,37 +511,57 @@ void LinkerDriver::main(ArrayRef<const char *> argsArr) {
if (args.hasArg(OPT_version))
return;
- initLLVM();
- createFiles(args);
- if (errorCount())
- return;
+ // Initialize time trace profiler.
+ if (config->timeTraceEnabled)
+ timeTraceProfilerInitialize(config->timeTraceGranularity, config->progName);
- inferMachineType();
- setConfigs(args);
- checkOptions();
- if (errorCount())
- return;
+ {
+ llvm::TimeTraceScope timeScope("ExecuteLinker");
- // The Target instance handles target-specific stuff, such as applying
- // relocations or writing a PLT section. It also contains target-dependent
- // values such as a default image base address.
- target = getTarget();
+ initLLVM();
+ createFiles(args);
+ if (errorCount())
+ return;
- switch (config->ekind) {
- case ELF32LEKind:
- link<ELF32LE>(args);
- return;
- case ELF32BEKind:
- link<ELF32BE>(args);
- return;
- case ELF64LEKind:
- link<ELF64LE>(args);
- return;
- case ELF64BEKind:
- link<ELF64BE>(args);
- return;
- default:
- llvm_unreachable("unknown Config->EKind");
+ inferMachineType();
+ setConfigs(args);
+ checkOptions();
+ if (errorCount())
+ return;
+
+ // The Target instance handles target-specific stuff, such as applying
+ // relocations or writing a PLT section. It also contains target-dependent
+ // values such as a default image base address.
+ target = getTarget();
+
+ switch (config->ekind) {
+ case ELF32LEKind:
+ link<ELF32LE>(args);
+ break;
+ case ELF32BEKind:
+ link<ELF32BE>(args);
+ break;
+ case ELF64LEKind:
+ link<ELF64LE>(args);
+ break;
+ case ELF64BEKind:
+ link<ELF64BE>(args);
+ break;
+ default:
+ llvm_unreachable("unknown Config->EKind");
+ }
+ }
+
+ if (config->timeTraceEnabled) {
+ if (auto E = timeTraceProfilerWrite(args.getLastArgValue(OPT_time_trace_file_eq).str(),
+ config->outputFile)) {
+ handleAllErrors(std::move(E), [&](const StringError &SE) {
+ error(SE.getMessage());
+ });
+ return;
+ }
+
+ timeTraceProfilerCleanup();
}
}
@@ -586,9 +630,6 @@ static bool isOutputFormatBinary(opt::InputArgList &args) {
}
static DiscardPolicy getDiscard(opt::InputArgList &args) {
- if (args.hasArg(OPT_relocatable))
- return DiscardPolicy::None;
-
auto *arg =
args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
if (!arg)
@@ -821,6 +862,22 @@ static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef mb) {
return names.takeVector();
}
+static bool getIsRela(opt::InputArgList &args) {
+ // If -z rel or -z rela is specified, use the last option.
+ for (auto *arg : args.filtered_reverse(OPT_z)) {
+ StringRef s(arg->getValue());
+ if (s == "rel")
+ return false;
+ if (s == "rela")
+ return true;
+ }
+
+ // Otherwise use the psABI defined relocation entry format.
+ uint16_t m = config->emachine;
+ return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC ||
+ m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64;
+}
+
static void parseClangOption(StringRef opt, const Twine &msg) {
std::string err;
raw_string_ostream os(err);
@@ -839,7 +896,6 @@ static void readConfigs(opt::InputArgList &args) {
args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
errorHandler().vsDiagnostics =
args.hasArg(OPT_visual_studio_diagnostics_format, false);
- threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true);
config->allowMultipleDefinition =
args.hasFlag(OPT_allow_multiple_definition,
@@ -858,6 +914,8 @@ static void readConfigs(opt::InputArgList &args) {
config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false);
config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common,
!args.hasArg(OPT_relocatable));
+ config->optimizeBBJumps =
+ args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false);
config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true);
config->disableVerify = args.hasArg(OPT_disable_verify);
@@ -879,9 +937,10 @@ static void readConfigs(opt::InputArgList &args) {
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
config->filterList = args::getStrings(args, OPT_filter);
config->fini = args.getLastArgValue(OPT_fini, "_fini");
- config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419);
- config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8);
- config->forceBTI = hasZOption(args, "force-bti");
+ config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) &&
+ !args.hasArg(OPT_relocatable);
+ config->fixCortexA8 =
+ args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable);
config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true);
config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
@@ -895,12 +954,20 @@ static void readConfigs(opt::InputArgList &args) {
config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate);
config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file);
config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager);
+ config->ltoEmitAsm = args.hasArg(OPT_lto_emit_asm);
config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
+ config->ltoWholeProgramVisibility =
+ args.hasArg(OPT_lto_whole_program_visibility);
config->ltoo = args::getInteger(args, OPT_lto_O, 2);
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
+ config->ltoBasicBlockSections =
+ args.getLastArgValue(OPT_lto_basicblock_sections);
+ config->ltoUniqueBasicBlockSectionNames =
+ args.hasFlag(OPT_lto_unique_bb_section_names,
+ OPT_no_lto_unique_bb_section_names, false);
config->mapFile = args.getLastArgValue(OPT_Map);
config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0);
config->mergeArmExidx =
@@ -919,21 +986,23 @@ static void readConfigs(opt::InputArgList &args) {
config->optimize = args::getInteger(args, OPT_O, 1);
config->orphanHandling = getOrphanHandling(args);
config->outputFile = args.getLastArgValue(OPT_o);
- config->pacPlt = hasZOption(args, "pac-plt");
config->pie = args.hasFlag(OPT_pie, OPT_no_pie, false);
config->printIcfSections =
args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
config->printGcSections =
args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
+ config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats);
config->printSymbolOrder =
args.getLastArgValue(OPT_print_symbol_order);
config->rpath = getRpath(args);
config->relocatable = args.hasArg(OPT_relocatable);
config->saveTemps = args.hasArg(OPT_save_temps);
+ if (args.hasArg(OPT_shuffle_sections))
+ config->shuffleSectionSeed = args::getInteger(args, OPT_shuffle_sections, 0);
config->searchPaths = args::getStrings(args, OPT_library_path);
config->sectionStartMap = getSectionStartMap(args);
config->shared = args.hasArg(OPT_shared);
- config->singleRoRx = args.hasArg(OPT_no_rosegment);
+ config->singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true);
config->soName = args.getLastArgValue(OPT_soname);
config->sortSection = getSortSection(args);
config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384);
@@ -949,15 +1018,20 @@ static void readConfigs(opt::InputArgList &args) {
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
- config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
config->thinLTOPrefixReplace =
getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
+ config->thinLTOModulesToCompile =
+ args::getStrings(args, OPT_thinlto_single_module_eq);
+ config->timeTraceEnabled = args.hasArg(OPT_time_trace);
+ config->timeTraceGranularity =
+ args::getInteger(args, OPT_time_trace_granularity, 500);
config->trace = args.hasArg(OPT_trace);
config->undefined = args::getStrings(args, OPT_undefined);
config->undefinedVersion =
args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true);
+ config->unique = args.hasArg(OPT_unique);
config->useAndroidRelrTags = args.hasFlag(
OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false);
config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
@@ -970,6 +1044,7 @@ static void readConfigs(opt::InputArgList &args) {
args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
+ config->zForceBti = hasZOption(args, "force-bti");
config->zForceIbt = hasZOption(args, "force-ibt");
config->zGlobal = hasZOption(args, "global");
config->zGnustack = getZGnuStack(args);
@@ -984,33 +1059,78 @@ static void readConfigs(opt::InputArgList &args) {
config->zNodlopen = hasZOption(args, "nodlopen");
config->zNow = getZFlag(args, "now", "lazy", false);
config->zOrigin = hasZOption(args, "origin");
+ config->zPacPlt = hasZOption(args, "pac-plt");
config->zRelro = getZFlag(args, "relro", "norelro", true);
config->zRetpolineplt = hasZOption(args, "retpolineplt");
config->zRodynamic = hasZOption(args, "rodynamic");
config->zSeparate = getZSeparate(args);
config->zShstk = hasZOption(args, "shstk");
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
+ config->zStartStopVisibility = getZStartStopVisibility(args);
config->zText = getZFlag(args, "text", "notext", true);
config->zWxneeded = hasZOption(args, "wxneeded");
+ for (opt::Arg *arg : args.filtered(OPT_z)) {
+ std::pair<StringRef, StringRef> option =
+ StringRef(arg->getValue()).split('=');
+ if (option.first != "dead-reloc-in-nonalloc")
+ continue;
+ constexpr StringRef errPrefix = "-z dead-reloc-in-nonalloc=: ";
+ std::pair<StringRef, StringRef> kv = option.second.split('=');
+ if (kv.first.empty() || kv.second.empty()) {
+ error(errPrefix + "expected <section_glob>=<value>");
+ continue;
+ }
+ uint64_t v;
+ if (!to_integer(kv.second, v))
+ error(errPrefix + "expected a non-negative integer, but got '" +
+ kv.second + "'");
+ else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
+ config->deadRelocInNonAlloc.emplace_back(std::move(*pat), v);
+ else
+ error(errPrefix + toString(pat.takeError()));
+ }
+
// Parse LTO options.
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
arg->getSpelling());
- for (auto *arg : args.filtered(OPT_plugin_opt))
- parseClangOption(arg->getValue(), arg->getSpelling());
+ for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq_minus))
+ parseClangOption(std::string("-") + arg->getValue(), arg->getSpelling());
+
+ // GCC collect2 passes -plugin-opt=path/to/lto-wrapper with an absolute or
+ // relative path. Just ignore. If not ended with "lto-wrapper", consider it an
+ // unsupported LLVMgold.so option and error.
+ for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq))
+ if (!StringRef(arg->getValue()).endswith("lto-wrapper"))
+ error(arg->getSpelling() + ": unknown plugin option '" + arg->getValue() +
+ "'");
// Parse -mllvm options.
for (auto *arg : args.filtered(OPT_mllvm))
parseClangOption(arg->getValue(), arg->getSpelling());
+ // --threads= takes a positive integer and provides the default value for
+ // --thinlto-jobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v;
+ }
+ if (auto *arg = args.getLastArg(OPT_thinlto_jobs))
+ config->thinLTOJobs = arg->getValue();
+
if (config->ltoo > 3)
error("invalid optimization level for LTO: " + Twine(config->ltoo));
if (config->ltoPartitions == 0)
error("--lto-partitions: number of threads must be > 0");
- if (config->thinLTOJobs == 0)
- error("--thinlto-jobs: number of threads must be > 0");
+ if (!get_threadpool_strategy(config->thinLTOJobs))
+ error("--thinlto-jobs: invalid job count: " + config->thinLTOJobs);
if (config->splitStackAdjustSize < 0)
error("--split-stack-adjust-size: size must be >= 0");
@@ -1088,25 +1208,30 @@ static void readConfigs(opt::InputArgList &args) {
{s, /*isExternCpp=*/false, /*hasWildcard=*/false});
}
- // Parses -dynamic-list and -export-dynamic-symbol. They make some
- // symbols private. Note that -export-dynamic takes precedence over them
- // as it says all symbols should be exported.
- if (!config->exportDynamic) {
- for (auto *arg : args.filtered(OPT_dynamic_list))
- if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
- readDynamicList(*buffer);
-
- for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
- config->dynamicList.push_back(
- {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false});
+ for (opt::Arg *arg : args.filtered(OPT_warn_backrefs_exclude)) {
+ StringRef pattern(arg->getValue());
+ if (Expected<GlobPattern> pat = GlobPattern::create(pattern))
+ config->warnBackrefsExclude.push_back(std::move(*pat));
+ else
+ error(arg->getSpelling() + ": " + toString(pat.takeError()));
}
- // If --export-dynamic-symbol=foo is given and symbol foo is defined in
- // an object file in an archive file, that object file should be pulled
- // out and linked. (It doesn't have to behave like that from technical
- // point of view, but this is needed for compatibility with GNU.)
+ // When producing an executable, --dynamic-list specifies non-local defined
+ // symbols whith are required to be exported. When producing a shared object,
+ // symbols not specified by --dynamic-list are non-preemptible.
+ config->symbolic =
+ args.hasArg(OPT_Bsymbolic) || args.hasArg(OPT_dynamic_list);
+ for (auto *arg : args.filtered(OPT_dynamic_list))
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ readDynamicList(*buffer);
+
+ // --export-dynamic-symbol specifies additional --dynamic-list symbols if any
+ // other option expresses a symbolic intention: -no-pie, -pie, -Bsymbolic,
+ // -Bsymbolic-functions (if STT_FUNC), --dynamic-list.
for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
- config->undefined.push_back(arg->getValue());
+ config->dynamicList.push_back(
+ {arg->getValue(), /*isExternCpp=*/false,
+ /*hasWildcard=*/hasWildcard(arg->getValue())});
for (auto *arg : args.filtered(OPT_version_script))
if (Optional<std::string> path = searchScript(arg->getValue())) {
@@ -1136,20 +1261,19 @@ static void setConfigs(opt::InputArgList &args) {
// ELF defines two different ways to store relocation addends as shown below:
//
- // Rel: Addends are stored to the location where relocations are applied.
+ // Rel: Addends are stored to the location where relocations are applied. It
+ // cannot pack the full range of addend values for all relocation types, but
+ // this only affects relocation types that we don't support emitting as
+ // dynamic relocations (see getDynRel).
// Rela: Addends are stored as part of relocation entry.
//
// In other words, Rela makes it easy to read addends at the price of extra
- // 4 or 8 byte for each relocation entry. We don't know why ELF defined two
- // different mechanisms in the first place, but this is how the spec is
- // defined.
+ // 4 or 8 byte for each relocation entry.
//
- // You cannot choose which one, Rel or Rela, you want to use. Instead each
- // ABI defines which one you need to use. The following expression expresses
- // that.
- config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON ||
- m == EM_PPC || m == EM_PPC64 || m == EM_RISCV ||
- m == EM_X86_64;
+ // We pick the format for dynamic relocations according to the psABI for each
+ // processor, but a contrary choice can be made if the dynamic loader
+ // supports.
+ config->isRela = getIsRela(args);
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations
@@ -1391,7 +1515,7 @@ static void excludeLibs(opt::InputArgList &args) {
visit(file);
}
-// Force Sym to be entered in the output. Used for -u or equivalent.
+// Force Sym to be entered in the output.
static void handleUndefined(Symbol *sym) {
// Since a symbol may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
@@ -1592,6 +1716,12 @@ static Symbol *addUndefined(StringRef name) {
Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0});
}
+static Symbol *addUnusedUndefined(StringRef name) {
+ Undefined sym{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0};
+ sym.isUsedInRegularObj = false;
+ return symtab->addSymbol(sym);
+}
+
// This function is where all the optimizations of link-time
// optimization takes place. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
@@ -1600,6 +1730,7 @@ static Symbol *addUndefined(StringRef name) {
// Because all bitcode files that the program consists of are passed to
// the compiler at once, it can do a whole-program optimization.
template <class ELFT> void LinkerDriver::compileBitcodeFiles() {
+ llvm::TimeTraceScope timeScope("LTO");
// Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler);
for (BitcodeFile *file : bitcodeFiles)
@@ -1608,8 +1739,11 @@ template <class ELFT> void LinkerDriver::compileBitcodeFiles() {
for (InputFile *file : lto->compile()) {
auto *obj = cast<ObjFile<ELFT>>(file);
obj->parse(/*ignoreComdats=*/true);
- for (Symbol *sym : obj->getGlobalSymbols())
- sym->parseSymbolVersion();
+
+ // Parse '@' in symbol names for non-relocatable output.
+ if (!config->relocatable)
+ for (Symbol *sym : obj->getGlobalSymbols())
+ sym->parseSymbolVersion();
objectFiles.push_back(file);
}
}
@@ -1703,8 +1837,9 @@ template <class ELFT> static uint32_t getAndFeatures() {
uint32_t ret = -1;
for (InputFile *f : objectFiles) {
uint32_t features = cast<ObjFile<ELFT>>(f)->andFeatures;
- if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
- warn(toString(f) + ": -z force-bti: file does not have BTI property");
+ if (config->zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
+ warn(toString(f) + ": -z force-bti: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
} else if (config->zForceIbt &&
!(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
@@ -1712,13 +1847,14 @@ template <class ELFT> static uint32_t getAndFeatures() {
"GNU_PROPERTY_X86_FEATURE_1_IBT property");
features |= GNU_PROPERTY_X86_FEATURE_1_IBT;
}
+ if (config->zPacPlt && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
+ warn(toString(f) + ": -z pac-plt: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_PAC property");
+ features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+ }
ret &= features;
}
- // Force enable pointer authentication Plt, we don't warn in this case as
- // this does not require support in the object for correctness.
- if (config->pacPlt)
- ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
// Force enable Shadow Stack.
if (config->zShstk)
ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
@@ -1729,6 +1865,7 @@ template <class ELFT> static uint32_t getAndFeatures() {
// Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
+ llvm::TimeTraceScope timeScope("Link", StringRef("LinkerDriver::Link"));
// If a -hash-style option was not given, set to a default value,
// which varies depending on the target.
if (!args.hasArg(OPT_hash_style)) {
@@ -1764,12 +1901,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
for (auto *arg : args.filtered(OPT_trace_symbol))
symtab->insert(arg->getValue())->traced = true;
+ // Handle -u/--undefined before input files. If both a.a and b.so define foo,
+ // -u foo a.a b.so will fetch a.a.
+ for (StringRef name : config->undefined)
+ addUnusedUndefined(name);
+
// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table. This process might
// add files to the link, via autolinking, these files are always
// appended to the Files vector.
- for (size_t i = 0; i < files.size(); ++i)
- parseFile(files[i]);
+ {
+ llvm::TimeTraceScope timeScope("Parse input files");
+ for (size_t i = 0; i < files.size(); ++i)
+ parseFile(files[i]);
+ }
// Now that we have every file, we can decide if we will need a
// dynamic symbol table.
@@ -1785,10 +1930,10 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
for (StringRef name : script->referencedSymbols)
addUndefined(name);
- // Handle the `--undefined <sym>` options.
- for (StringRef arg : config->undefined)
- if (Symbol *sym = symtab->find(arg))
- handleUndefined(sym);
+ // Prevent LTO from removing any definition referenced by -u.
+ for (StringRef name : config->undefined)
+ if (Defined *sym = dyn_cast_or_null<Defined>(symtab->find(name)))
+ sym->isUsedInRegularObj = true;
// If an entry symbol is in a static archive, pull out that file now.
if (Symbol *sym = symtab->find(config->entry))
@@ -1799,9 +1944,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
handleUndefinedGlob(pat);
// Mark -init and -fini symbols so that the LTO doesn't eliminate them.
- if (Symbol *sym = symtab->find(config->init))
+ if (Symbol *sym = dyn_cast_or_null<Defined>(symtab->find(config->init)))
sym->isUsedInRegularObj = true;
- if (Symbol *sym = symtab->find(config->fini))
+ if (Symbol *sym = dyn_cast_or_null<Defined>(symtab->find(config->fini)))
sym->isUsedInRegularObj = true;
// If any of our inputs are bitcode files, the LTO code generator may create
@@ -1829,10 +1974,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
if (errorCount())
return;
- // Now when we read all script files, we want to finalize order of linker
- // script commands, which can be not yet final because of INSERT commands.
- script->processInsertCommands();
-
// We want to declare linker script's symbols early,
// so that we can version them.
// They also might be exported if referenced by DSOs.
@@ -1868,19 +2009,22 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// With this the symbol table should be complete. After this, no new names
// except a few linker-synthesized ones will be added to the symbol table.
compileBitcodeFiles<ELFT>();
+
+ // Symbol resolution finished. Report backward reference problems.
+ reportBackrefs();
if (errorCount())
return;
// If -thinlto-index-only is given, we should create only "index
// files" and not object files. Index file creation is already done
// in addCombinedLTOObject, so we are done if that's the case.
- if (config->thinLTOIndexOnly)
- return;
-
- // Likewise, --plugin-opt=emit-llvm is an option to make LTO create
- // an output file in bitcode and exit, so that you can just get a
- // combined bitcode file.
- if (config->emitLLVM)
+ // Likewise, --plugin-opt=emit-llvm and --plugin-opt=emit-asm are the
+ // options to create output files in bitcode or assembly code
+ // repsectively. No object files are generated.
+ // Also bail out here when only certain thinLTO modules are specified for
+ // compilation. The intermediate object file are the expected output.
+ if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm ||
+ !config->thinLTOModulesToCompile.empty())
return;
// Apply symbol renames for -wrap.
@@ -2020,6 +2164,3 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// Write the result to the file.
writeResult<ELFT>();
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/DriverUtils.cpp b/contrib/llvm-project/lld/ELF/DriverUtils.cpp
index 9fcb36e81676..e33b07c0c9c9 100644
--- a/contrib/llvm-project/lld/ELF/DriverUtils.cpp
+++ b/contrib/llvm-project/lld/ELF/DriverUtils.cpp
@@ -23,15 +23,15 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
using namespace llvm;
using namespace llvm::sys;
using namespace llvm::opt;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Create OptTable
@@ -82,7 +82,7 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
- if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32)
+ if (Triple(sys::getProcessTriple()).isOSWindows())
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
@@ -143,7 +143,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
return args;
}
-void printHelp() {
+void elf::printHelp() {
ELFOptTable().PrintHelp(
lld::outs(), (config->progName + " [options] file...").str().c_str(),
"lld", false /*ShowHidden*/, true /*ShowAllAliases*/);
@@ -160,12 +160,12 @@ void printHelp() {
static std::string rewritePath(StringRef s) {
if (fs::exists(s))
return relativeToRoot(s);
- return s;
+ return std::string(s);
}
// Reconstructs command line arguments so that so that you can re-run
// the same command with the same inputs. This is for --reproduce.
-std::string createResponseFile(const opt::InputArgList &args) {
+std::string elf::createResponseFile(const opt::InputArgList &args) {
SmallString<0> data;
raw_svector_ostream os(data);
os << "--chroot .\n";
@@ -199,7 +199,7 @@ std::string createResponseFile(const opt::InputArgList &args) {
os << toString(*arg) << "\n";
}
}
- return data.str();
+ return std::string(data.str());
}
// Find a file by concatenating given paths. If a resulting path
@@ -212,11 +212,11 @@ static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
path::append(s, path1, path2);
if (fs::exists(s))
- return s.str().str();
+ return std::string(s);
return None;
}
-Optional<std::string> findFromSearchPaths(StringRef path) {
+Optional<std::string> elf::findFromSearchPaths(StringRef path) {
for (StringRef dir : config->searchPaths)
if (Optional<std::string> s = findFile(dir, path))
return s;
@@ -225,7 +225,7 @@ Optional<std::string> findFromSearchPaths(StringRef path) {
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
// search paths.
-Optional<std::string> searchLibraryBaseName(StringRef name) {
+Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
for (StringRef dir : config->searchPaths) {
if (!config->isStatic)
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
@@ -237,7 +237,7 @@ Optional<std::string> searchLibraryBaseName(StringRef name) {
}
// This is for -l<namespec>.
-Optional<std::string> searchLibrary(StringRef name) {
+Optional<std::string> elf::searchLibrary(StringRef name) {
if (name.startswith(":"))
return findFromSearchPaths(name.substr(1));
return searchLibraryBaseName(name);
@@ -246,11 +246,8 @@ Optional<std::string> searchLibrary(StringRef name) {
// If a linker/version script doesn't exist in the current directory, we also
// look for the script in the '-L' search paths. This matches the behaviour of
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
-Optional<std::string> searchScript(StringRef name) {
+Optional<std::string> elf::searchScript(StringRef name) {
if (fs::exists(name))
return name.str();
return findFromSearchPaths(name);
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/EhFrame.cpp b/contrib/llvm-project/lld/ELF/EhFrame.cpp
index a9c66f29446c..f97e3b604eb7 100644
--- a/contrib/llvm-project/lld/ELF/EhFrame.cpp
+++ b/contrib/llvm-project/lld/ELF/EhFrame.cpp
@@ -29,9 +29,9 @@ using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::dwarf;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
class EhReader {
public:
@@ -56,7 +56,7 @@ private:
};
}
-size_t readEhRecordSize(InputSectionBase *s, size_t off) {
+size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) {
return EhReader(s, s->data().slice(off)).readEhRecordSize();
}
@@ -148,7 +148,7 @@ void EhReader::skipAugP() {
d = d.slice(size);
}
-uint8_t getFdeEncoding(EhSectionPiece *p) {
+uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
return EhReader(p->sec, p->data()).getFdeEncoding();
}
@@ -194,6 +194,3 @@ uint8_t EhReader::getFdeEncoding() {
}
return DW_EH_PE_absptr;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/ICF.cpp b/contrib/llvm-project/lld/ELF/ICF.cpp
index 8992b6564a8a..ecf0a282420d 100644
--- a/contrib/llvm-project/lld/ELF/ICF.cpp
+++ b/contrib/llvm-project/lld/ELF/ICF.cpp
@@ -80,10 +80,11 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Writer.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/xxhash.h"
#include <algorithm>
#include <atomic>
@@ -91,9 +92,9 @@
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
template <class ELFT> class ICF {
public:
@@ -399,7 +400,7 @@ template <class ELFT>
void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
// If threading is disabled or the number of sections are
// too small to use threading, call Fn sequentially.
- if (!threadsEnabled || sections.size() < 1024) {
+ if (parallel::strategy.ThreadsRequested == 1 || sections.size() < 1024) {
forEachClassRange(0, sections.size(), fn);
++cnt;
return;
@@ -466,9 +467,8 @@ template <class ELFT> void ICF<ELFT>::run() {
}
// Initially, we use hash values to partition sections.
- parallelForEach(sections, [&](InputSection *s) {
- s->eqClass[0] = xxHash64(s->data());
- });
+ parallelForEach(
+ sections, [&](InputSection *s) { s->eqClass[0] = xxHash64(s->data()); });
for (unsigned cnt = 0; cnt != 2; ++cnt) {
parallelForEach(sections, [&](InputSection *s) {
@@ -525,12 +525,12 @@ template <class ELFT> void ICF<ELFT>::run() {
}
// ICF entry point function.
-template <class ELFT> void doIcf() { ICF<ELFT>().run(); }
-
-template void doIcf<ELF32LE>();
-template void doIcf<ELF32BE>();
-template void doIcf<ELF64LE>();
-template void doIcf<ELF64BE>();
+template <class ELFT> void elf::doIcf() {
+ llvm::TimeTraceScope timeScope("ICF");
+ ICF<ELFT>().run();
+}
-} // namespace elf
-} // namespace lld
+template void elf::doIcf<ELF32LE>();
+template void elf::doIcf<ELF32BE>();
+template void elf::doIcf<ELF64LE>();
+template void elf::doIcf<ELF64BE>();
diff --git a/contrib/llvm-project/lld/ELF/InputFiles.cpp b/contrib/llvm-project/lld/ELF/InputFiles.cpp
index 43978cd66c61..c2f1830a981b 100644
--- a/contrib/llvm-project/lld/ELF/InputFiles.cpp
+++ b/contrib/llvm-project/lld/ELF/InputFiles.cpp
@@ -36,33 +36,35 @@ using namespace llvm::object;
using namespace llvm::sys;
using namespace llvm::sys::fs;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
+
+bool InputFile::isInGroup;
+uint32_t InputFile::nextGroupId;
+
+std::vector<ArchiveFile *> elf::archiveFiles;
+std::vector<BinaryFile *> elf::binaryFiles;
+std::vector<BitcodeFile *> elf::bitcodeFiles;
+std::vector<LazyObjFile *> elf::lazyObjFiles;
+std::vector<InputFile *> elf::objectFiles;
+std::vector<SharedFile *> elf::sharedFiles;
+
+std::unique_ptr<TarWriter> elf::tar;
-namespace lld {
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
-std::string toString(const elf::InputFile *f) {
+std::string lld::toString(const InputFile *f) {
if (!f)
return "<internal>";
if (f->toStringCache.empty()) {
if (f->archiveName.empty())
- f->toStringCache = f->getName();
+ f->toStringCache = std::string(f->getName());
else
f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
}
return f->toStringCache;
}
-namespace elf {
-bool InputFile::isInGroup;
-uint32_t InputFile::nextGroupId;
-std::vector<BinaryFile *> binaryFiles;
-std::vector<BitcodeFile *> bitcodeFiles;
-std::vector<LazyObjFile *> lazyObjFiles;
-std::vector<InputFile *> objectFiles;
-std::vector<SharedFile *> sharedFiles;
-
-std::unique_ptr<TarWriter> tar;
-
static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
unsigned char size;
unsigned char endian;
@@ -101,7 +103,7 @@ InputFile::InputFile(Kind k, MemoryBufferRef m)
++nextGroupId;
}
-Optional<MemoryBufferRef> readFile(StringRef path) {
+Optional<MemoryBufferRef> elf::readFile(StringRef path) {
// The --chroot option changes our virtual root directory.
// This is useful when you are dealing with files created by --reproduce.
if (!config->chroot.empty() && path.startswith("/"))
@@ -138,8 +140,10 @@ static bool isCompatible(InputFile *file) {
return true;
}
- if (!config->emulation.empty()) {
- error(toString(file) + " is incompatible with " + config->emulation);
+ StringRef target =
+ !config->bfdname.empty() ? config->bfdname : config->emulation;
+ if (!target.empty()) {
+ error(toString(file) + " is incompatible with " + target);
return false;
}
@@ -148,8 +152,11 @@ static bool isCompatible(InputFile *file) {
existing = objectFiles[0];
else if (!sharedFiles.empty())
existing = sharedFiles[0];
- else
+ else if (!bitcodeFiles.empty())
existing = bitcodeFiles[0];
+ else
+ llvm_unreachable("Must have -m, OUTPUT_FORMAT or existing input file to "
+ "determine target emulation");
error(toString(file) + " is incompatible with " + toString(existing));
return false;
@@ -168,6 +175,7 @@ template <class ELFT> static void doParseFile(InputFile *file) {
// .a file
if (auto *f = dyn_cast<ArchiveFile>(file)) {
+ archiveFiles.push_back(f);
f->parse();
return;
}
@@ -201,7 +209,7 @@ template <class ELFT> static void doParseFile(InputFile *file) {
}
// Add symbols in File to the symbol table.
-void parseFile(InputFile *file) {
+void elf::parseFile(InputFile *file) {
switch (config->ekind) {
case ELF32LEKind:
doParseFile<ELF32LE>(file);
@@ -222,7 +230,7 @@ void parseFile(InputFile *file) {
// Concatenates arguments to construct a string representing an error location.
static std::string createFileLineMsg(StringRef path, unsigned line) {
- std::string filename = path::filename(path);
+ std::string filename = std::string(path::filename(path));
std::string lineno = ":" + std::to_string(line);
if (filename == path)
return filename + lineno;
@@ -243,7 +251,7 @@ static std::string getSrcMsgAux(ObjFile<ELFT> &file, const Symbol &sym,
return createFileLineMsg(fileLine->first, fileLine->second);
// File.sourceFile contains STT_FILE symbol, and that is a last resort.
- return file.sourceFile;
+ return std::string(file.sourceFile);
}
std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
@@ -264,9 +272,17 @@ std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
}
}
-template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
- dwarf = make<DWARFCache>(std::make_unique<DWARFContext>(
- std::make_unique<LLDDwarfObj<ELFT>>(this)));
+template <class ELFT> DWARFCache *ObjFile<ELFT>::getDwarf() {
+ llvm::call_once(initDwarf, [this]() {
+ dwarf = std::make_unique<DWARFCache>(std::make_unique<DWARFContext>(
+ std::make_unique<LLDDwarfObj<ELFT>>(this), "",
+ [&](Error err) { warn(getName() + ": " + toString(std::move(err))); },
+ [&](Error warning) {
+ warn(getName() + ": " + toString(std::move(warning)));
+ }));
+ });
+
+ return dwarf.get();
}
// Returns the pair of file name and line number describing location of data
@@ -274,9 +290,7 @@ template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
template <class ELFT>
Optional<std::pair<std::string, unsigned>>
ObjFile<ELFT>::getVariableLoc(StringRef name) {
- llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
-
- return dwarf->getVariableLoc(name);
+ return getDwarf()->getVariableLoc(name);
}
// Returns source line information for a given offset
@@ -284,8 +298,6 @@ ObjFile<ELFT>::getVariableLoc(StringRef name) {
template <class ELFT>
Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
uint64_t offset) {
- llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
-
// Detect SectionIndex for specified section.
uint64_t sectionIndex = object::SectionedAddress::UndefSection;
ArrayRef<InputSectionBase *> sections = s->file->getSections();
@@ -296,9 +308,7 @@ Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
}
}
- // Use fake address calculated by adding section file offset and offset in
- // section. See comments for ObjectInfo class.
- return dwarf->getDILineInfo(s->getOffsetInFile() + offset, sectionIndex);
+ return getDwarf()->getDILineInfo(offset, sectionIndex);
}
ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
@@ -417,6 +427,9 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
template <class ELFT>
bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec, StringRef name) {
+ if (!(sec.sh_flags & SHF_MERGE))
+ return false;
+
// On a regular link we don't merge sections if -O0 (default is -O1). This
// sometimes makes the linker significantly faster, although the output will
// be bigger.
@@ -452,10 +465,7 @@ bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec, StringRef name) {
Twine(sec.sh_size) + ") must be a multiple of sh_entsize (" +
Twine(entSize) + ")");
- uint64_t flags = sec.sh_flags;
- if (!(flags & SHF_MERGE))
- return false;
- if (flags & SHF_WRITE)
+ if (sec.sh_flags & SHF_WRITE)
fatal(toString(this) + ":(" + name +
"): writable SHF_MERGE section is not supported");
@@ -622,6 +632,8 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
break;
case SHT_SYMTAB:
case SHT_STRTAB:
+ case SHT_REL:
+ case SHT_RELA:
case SHT_NULL:
break;
default:
@@ -629,11 +641,21 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
}
}
- // This block handles SHF_LINK_ORDER.
+ // We have a second loop. It is used to:
+ // 1) handle SHF_LINK_ORDER sections.
+ // 2) create SHT_REL[A] sections. In some cases the section header index of a
+ // relocation section may be smaller than that of the relocated section. In
+ // such cases, the relocation section would attempt to reference a target
+ // section that has not yet been created. For simplicity, delay creation of
+ // relocation sections until now.
for (size_t i = 0, e = objSections.size(); i < e; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
const Elf_Shdr &sec = objSections[i];
+
+ if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA)
+ this->sections[i] = createInputSection(sec);
+
if (!(sec.sh_flags & SHF_LINK_ORDER))
continue;
@@ -662,7 +684,9 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
// the input objects have been compiled.
static void updateARMVFPArgs(const ARMAttributeParser &attributes,
const InputFile *f) {
- if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
+ Optional<unsigned> attr =
+ attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ if (!attr.hasValue())
// If an ABI tag isn't present then it is implicitly given the value of 0
// which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files,
// including some in glibc that don't use FP args (and should have value 3)
@@ -670,7 +694,7 @@ static void updateARMVFPArgs(const ARMAttributeParser &attributes,
// as a clash.
return;
- unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ unsigned vfpArgs = attr.getValue();
ARMVFPArgKind arg;
switch (vfpArgs) {
case ARMBuildAttrs::BaseAAPCS:
@@ -707,9 +731,11 @@ static void updateARMVFPArgs(const ARMAttributeParser &attributes,
// is compiled with an architecture that supports these features then lld is
// permitted to use them.
static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
- if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
+ Optional<unsigned> attr =
+ attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ if (!attr.hasValue())
return;
- auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ auto arch = attr.getValue();
switch (arch) {
case ARMBuildAttrs::Pre_v4:
case ARMBuildAttrs::v4:
@@ -842,7 +868,13 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
break;
ARMAttributeParser attributes;
ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec));
- attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind);
+ if (Error e = attributes.parse(contents, config->ekind == ELF32LEKind
+ ? support::little
+ : support::big)) {
+ auto *isec = make<InputSection>(*this, sec, name);
+ warn(toString(isec) + ": " + llvm::toString(std::move(e)));
+ break;
+ }
updateSupportedARMFeatures(attributes);
updateARMVFPArgs(attributes, this);
@@ -1029,51 +1061,68 @@ template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
this->symbols.resize(eSyms.size());
- // Our symbol table may have already been partially initialized
+ // Fill in InputFile::symbols. Some entries have been initialized
// because of LazyObjFile.
- for (size_t i = 0, end = eSyms.size(); i != end; ++i)
- if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL)
- this->symbols[i] =
- symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
-
- // Fill this->Symbols. A symbol is either local or global.
for (size_t i = 0, end = eSyms.size(); i != end; ++i) {
+ if (this->symbols[i])
+ continue;
const Elf_Sym &eSym = eSyms[i];
-
- // Read symbol attributes.
uint32_t secIdx = getSectionIndex(eSym);
if (secIdx >= this->sections.size())
fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
+ if (eSym.getBinding() != STB_LOCAL) {
+ if (i < firstGlobal)
+ error(toString(this) + ": non-local symbol (" + Twine(i) +
+ ") found at index < .symtab's sh_info (" + Twine(firstGlobal) +
+ ")");
+ this->symbols[i] =
+ symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
+ continue;
+ }
+
+ // Handle local symbols. Local symbols are not added to the symbol
+ // table because they are not visible from other object files. We
+ // allocate symbol instances and add their pointers to symbols.
+ if (i >= firstGlobal)
+ errorOrWarn(toString(this) + ": STB_LOCAL symbol (" + Twine(i) +
+ ") found at index >= .symtab's sh_info (" +
+ Twine(firstGlobal) + ")");
InputSectionBase *sec = this->sections[secIdx];
+ uint8_t type = eSym.getType();
+ if (type == STT_FILE)
+ sourceFile = CHECK(eSym.getName(this->stringTable), this);
+ if (this->stringTable.size() <= eSym.st_name)
+ fatal(toString(this) + ": invalid symbol name offset");
+ StringRefZ name = this->stringTable.data() + eSym.st_name;
+
+ if (eSym.st_shndx == SHN_UNDEF)
+ this->symbols[i] =
+ make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type);
+ else if (sec == &InputSection::discarded)
+ this->symbols[i] =
+ make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type,
+ /*discardedSecIdx=*/secIdx);
+ else
+ this->symbols[i] = make<Defined>(this, name, STB_LOCAL, eSym.st_other,
+ type, eSym.st_value, eSym.st_size, sec);
+ }
+
+ // Symbol resolution of non-local symbols.
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
+ const Elf_Sym &eSym = eSyms[i];
uint8_t binding = eSym.getBinding();
+ if (binding == STB_LOCAL)
+ continue; // Errored above.
+
+ uint32_t secIdx = getSectionIndex(eSym);
+ InputSectionBase *sec = this->sections[secIdx];
uint8_t stOther = eSym.st_other;
uint8_t type = eSym.getType();
uint64_t value = eSym.st_value;
uint64_t size = eSym.st_size;
StringRefZ name = this->stringTable.data() + eSym.st_name;
- // Handle local symbols. Local symbols are not added to the symbol
- // table because they are not visible from other object files. We
- // allocate symbol instances and add their pointers to Symbols.
- if (binding == STB_LOCAL) {
- if (eSym.getType() == STT_FILE)
- sourceFile = CHECK(eSym.getName(this->stringTable), this);
-
- if (this->stringTable.size() <= eSym.st_name)
- fatal(toString(this) + ": invalid symbol name offset");
-
- if (eSym.st_shndx == SHN_UNDEF)
- this->symbols[i] = make<Undefined>(this, name, binding, stOther, type);
- else if (sec == &InputSection::discarded)
- this->symbols[i] = make<Undefined>(this, name, binding, stOther, type,
- /*DiscardedSecIdx=*/secIdx);
- else
- this->symbols[i] =
- make<Defined>(this, name, binding, stOther, type, value, size, sec);
- continue;
- }
-
// Handle global undefined symbols.
if (eSym.st_shndx == SHN_UNDEF) {
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
@@ -1097,8 +1146,20 @@ template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
// COMDAT member sections, and if a comdat group is discarded, some
// defined symbol in a .eh_frame becomes dangling symbols.
if (sec == &InputSection::discarded) {
- this->symbols[i]->resolve(
- Undefined{this, name, binding, stOther, type, secIdx});
+ Undefined und{this, name, binding, stOther, type, secIdx};
+ Symbol *sym = this->symbols[i];
+ // !ArchiveFile::parsed or LazyObjFile::fetched means that the file
+ // containing this object has not finished processing, i.e. this symbol is
+ // a result of a lazy symbol fetch. We should demote the lazy symbol to an
+ // Undefined so that any relocations outside of the group to it will
+ // trigger a discarded section error.
+ if ((sym->symbolKind == Symbol::LazyArchiveKind &&
+ !cast<ArchiveFile>(sym->file)->parsed) ||
+ (sym->symbolKind == Symbol::LazyObjectKind &&
+ cast<LazyObjFile>(sym->file)->fetched))
+ sym->replace(und);
+ else
+ sym->resolve(und);
continue;
}
@@ -1121,6 +1182,10 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&file)
void ArchiveFile::parse() {
for (const Archive::Symbol &sym : file->symbols())
symtab->addSymbol(LazyArchive{*this, sym});
+
+ // Inform a future invocation of ObjFile<ELFT>::initializeSymbols() that this
+ // archive has been processed.
+ parsed = true;
}
// Returns a buffer pointing to a member file containing a given symbol.
@@ -1142,12 +1207,24 @@ void ArchiveFile::fetch(const Archive::Symbol &sym) {
if (tar && c.getParent()->isThin())
tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer());
- InputFile *file = createObjectFile(
- mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset());
+ InputFile *file = createObjectFile(mb, getName(), c.getChildOffset());
file->groupId = groupId;
parseFile(file);
}
+size_t ArchiveFile::getMemberCount() const {
+ size_t count = 0;
+ Error err = Error::success();
+ for (const Archive::Child &c : file->children(err)) {
+ (void)c;
+ ++count;
+ }
+ // This function is used by --print-archive-stats=, where an error does not
+ // really matter.
+ consumeError(std::move(err));
+ return count;
+}
+
unsigned SharedFile::vernauxNum;
// Parse the version definitions in the object file if present, and return a
@@ -1179,6 +1256,40 @@ static std::vector<const void *> parseVerdefs(const uint8_t *base,
return verdefs;
}
+// Parse SHT_GNU_verneed to properly set the name of a versioned undefined
+// symbol. We detect fatal issues which would cause vulnerabilities, but do not
+// implement sophisticated error checking like in llvm-readobj because the value
+// of such diagnostics is low.
+template <typename ELFT>
+std::vector<uint32_t> SharedFile::parseVerneed(const ELFFile<ELFT> &obj,
+ const typename ELFT::Shdr *sec) {
+ if (!sec)
+ return {};
+ std::vector<uint32_t> verneeds;
+ ArrayRef<uint8_t> data = CHECK(obj.getSectionContents(sec), this);
+ const uint8_t *verneedBuf = data.begin();
+ for (unsigned i = 0; i != sec->sh_info; ++i) {
+ if (verneedBuf + sizeof(typename ELFT::Verneed) > data.end())
+ fatal(toString(this) + " has an invalid Verneed");
+ auto *vn = reinterpret_cast<const typename ELFT::Verneed *>(verneedBuf);
+ const uint8_t *vernauxBuf = verneedBuf + vn->vn_aux;
+ for (unsigned j = 0; j != vn->vn_cnt; ++j) {
+ if (vernauxBuf + sizeof(typename ELFT::Vernaux) > data.end())
+ fatal(toString(this) + " has an invalid Vernaux");
+ auto *aux = reinterpret_cast<const typename ELFT::Vernaux *>(vernauxBuf);
+ if (aux->vna_name >= this->stringTable.size())
+ fatal(toString(this) + " has a Vernaux with an invalid vna_name");
+ uint16_t version = aux->vna_other & VERSYM_VERSION;
+ if (version >= verneeds.size())
+ verneeds.resize(version + 1);
+ verneeds[version] = aux->vna_name;
+ vernauxBuf += aux->vna_next;
+ }
+ verneedBuf += vn->vn_next;
+ }
+ return verneeds;
+}
+
// We do not usually care about alignments of data in shared object
// files because the loader takes care of it. However, if we promote a
// DSO symbol to point to .bss due to copy relocation, we need to keep
@@ -1222,6 +1333,7 @@ template <class ELFT> void SharedFile::parse() {
const Elf_Shdr *versymSec = nullptr;
const Elf_Shdr *verdefSec = nullptr;
+ const Elf_Shdr *verneedSec = nullptr;
// Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
for (const Elf_Shdr &sec : sections) {
@@ -1238,6 +1350,9 @@ template <class ELFT> void SharedFile::parse() {
case SHT_GNU_verdef:
verdefSec = &sec;
break;
+ case SHT_GNU_verneed:
+ verneedSec = &sec;
+ break;
}
}
@@ -1277,12 +1392,13 @@ template <class ELFT> void SharedFile::parse() {
sharedFiles.push_back(this);
verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec);
+ std::vector<uint32_t> verneeds = parseVerneed<ELFT>(obj, verneedSec);
// Parse ".gnu.version" section which is a parallel array for the symbol
// table. If a given file doesn't have a ".gnu.version" section, we use
// VER_NDX_GLOBAL.
size_t size = numELFSyms - firstGlobal;
- std::vector<uint32_t> versyms(size, VER_NDX_GLOBAL);
+ std::vector<uint16_t> versyms(size, VER_NDX_GLOBAL);
if (versymSec) {
ArrayRef<Elf_Versym> versym =
CHECK(obj.template getSectionContentsAsArray<Elf_Versym>(versymSec),
@@ -1313,7 +1429,22 @@ template <class ELFT> void SharedFile::parse() {
continue;
}
+ uint16_t idx = versyms[i] & ~VERSYM_HIDDEN;
if (sym.isUndefined()) {
+ // For unversioned undefined symbols, VER_NDX_GLOBAL makes more sense but
+ // as of binutils 2.34, GNU ld produces VER_NDX_LOCAL.
+ if (idx != VER_NDX_LOCAL && idx != VER_NDX_GLOBAL) {
+ if (idx >= verneeds.size()) {
+ error("corrupt input file: version need index " + Twine(idx) +
+ " for symbol " + name + " is out of bounds\n>>> defined in " +
+ toString(this));
+ continue;
+ }
+ StringRef verName = this->stringTable.data() + verneeds[idx];
+ versionedNameBuffer.clear();
+ name =
+ saver.save((name + "@" + verName).toStringRef(versionedNameBuffer));
+ }
Symbol *s = symtab->addSymbol(
Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
s->exportDynamic = true;
@@ -1323,7 +1454,6 @@ template <class ELFT> void SharedFile::parse() {
// MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
// assigns VER_NDX_LOCAL to this section global symbol. Here is a
// workaround for this bug.
- uint32_t idx = versyms[i] & ~VERSYM_HIDDEN;
if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL &&
name == "_gp_disp")
continue;
@@ -1405,7 +1535,7 @@ static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) {
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive)
: InputFile(BitcodeKind, mb) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly)
@@ -1417,10 +1547,11 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
// into consideration at LTO time (which very likely causes undefined
// symbols later in the link stage). So we append file offset to make
// filename unique.
- StringRef name = archiveName.empty()
- ? saver.save(path)
- : saver.save(archiveName + "(" + path + " at " +
- utostr(offsetInArchive) + ")");
+ StringRef name =
+ archiveName.empty()
+ ? saver.save(path)
+ : saver.save(archiveName + "(" + path::filename(path) + " at " +
+ utostr(offsetInArchive) + ")");
MemoryBufferRef mbref(mb.getBuffer(), name);
obj = CHECK(lto::InputFile::create(mbref), this);
@@ -1509,8 +1640,8 @@ void BinaryFile::parse() {
STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
}
-InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive) {
+InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive) {
if (isBitcode(mb))
return make<BitcodeFile>(mb, archiveName, offsetInArchive);
@@ -1529,14 +1660,13 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
}
void LazyObjFile::fetch() {
- if (mb.getBuffer().empty())
+ if (fetched)
return;
+ fetched = true;
InputFile *file = createObjectFile(mb, archiveName, offsetInArchive);
file->groupId = groupId;
- mb = {};
-
// Copy symbol vector so that the new InputFile doesn't have to
// insert the same defined symbols to the symbol table again.
file->symbols = std::move(symbols);
@@ -1593,21 +1723,22 @@ template <class ELFT> void LazyObjFile::parse() {
continue;
sym->resolve(LazyObject{*this, sym->getName()});
- // MemoryBuffer is emptied if this file is instantiated as ObjFile.
- if (mb.getBuffer().empty())
+ // If fetched, stop iterating because this->symbols has been transferred
+ // to the instantiated ObjFile.
+ if (fetched)
return;
}
return;
}
}
-std::string replaceThinLTOSuffix(StringRef path) {
+std::string elf::replaceThinLTOSuffix(StringRef path) {
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
StringRef repl = config->thinLTOObjectSuffixReplace.second;
if (path.consume_back(suffix))
return (path + repl).str();
- return path;
+ return std::string(path);
}
template void BitcodeFile::parse<ELF32LE>();
@@ -1620,15 +1751,12 @@ template void LazyObjFile::parse<ELF32BE>();
template void LazyObjFile::parse<ELF64LE>();
template void LazyObjFile::parse<ELF64BE>();
-template class ObjFile<ELF32LE>;
-template class ObjFile<ELF32BE>;
-template class ObjFile<ELF64LE>;
-template class ObjFile<ELF64BE>;
+template class elf::ObjFile<ELF32LE>;
+template class elf::ObjFile<ELF32BE>;
+template class elf::ObjFile<ELF64LE>;
+template class elf::ObjFile<ELF64BE>;
template void SharedFile::parse<ELF32LE>();
template void SharedFile::parse<ELF32BE>();
template void SharedFile::parse<ELF64LE>();
template void SharedFile::parse<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/InputFiles.h b/contrib/llvm-project/lld/ELF/InputFiles.h
index a310ba551bd4..7af85e417ca5 100644
--- a/contrib/llvm-project/lld/ELF/InputFiles.h
+++ b/contrib/llvm-project/lld/ELF/InputFiles.h
@@ -38,8 +38,6 @@ class DWARFCache;
std::string toString(const elf::InputFile *f);
namespace elf {
-class InputFile;
-class InputSectionBase;
using llvm::object::Archive;
@@ -200,7 +198,7 @@ public:
ArrayRef<Symbol *> getGlobalSymbols();
ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
}
void parse(bool ignoreComdats = false);
@@ -250,11 +248,14 @@ public:
// SHT_LLVM_CALL_GRAPH_PROFILE table
ArrayRef<Elf_CGProfile> cgProfile;
+ // Get cached DWARF information.
+ DWARFCache *getDwarf();
+
private:
void initializeSections(bool ignoreComdats);
void initializeSymbols();
void initializeJustSymbols();
- void initializeDwarf();
+
InputSectionBase *getRelocTarget(const Elf_Shdr &sec);
InputSectionBase *createInputSection(const Elf_Shdr &sec);
StringRef getSectionName(const Elf_Shdr &sec);
@@ -282,8 +283,8 @@ private:
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
// parse it only once for each object file we link.
- DWARFCache *dwarf;
- llvm::once_flag initDwarfLine;
+ std::unique_ptr<DWARFCache> dwarf;
+ llvm::once_flag initDwarf;
};
// LazyObjFile is analogous to ArchiveFile in the sense that
@@ -298,7 +299,7 @@ public:
LazyObjFile(MemoryBufferRef m, StringRef archiveName,
uint64_t offsetInArchive)
: InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
}
static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; }
@@ -306,6 +307,8 @@ public:
template <class ELFT> void parse();
void fetch();
+ bool fetched = false;
+
private:
uint64_t offsetInArchive;
};
@@ -323,6 +326,11 @@ public:
// more than once.)
void fetch(const Archive::Symbol &sym);
+ size_t getMemberCount() const;
+ size_t getFetchedMemberCount() const { return seen.size(); }
+
+ bool parsed = false;
+
private:
std::unique_ptr<Archive> file;
llvm::DenseSet<uint64_t> seen;
@@ -341,7 +349,7 @@ public:
class SharedFile : public ELFFileBase {
public:
SharedFile(MemoryBufferRef m, StringRef defaultSoName)
- : ELFFileBase(SharedKind, m), soName(defaultSoName),
+ : ELFFileBase(SharedKind, m), soName(std::string(defaultSoName)),
isNeeded(!config->asNeeded) {}
// This is actually a vector of Elf_Verdef pointers.
@@ -366,6 +374,11 @@ public:
// Used for --as-needed
bool isNeeded;
+
+private:
+ template <typename ELFT>
+ std::vector<uint32_t> parseVerneed(const llvm::object::ELFFile<ELFT> &obj,
+ const typename ELFT::Shdr *sec);
};
class BinaryFile : public InputFile {
@@ -384,6 +397,7 @@ inline bool isBitcode(MemoryBufferRef mb) {
std::string replaceThinLTOSuffix(StringRef path);
+extern std::vector<ArchiveFile *> archiveFiles;
extern std::vector<BinaryFile *> binaryFiles;
extern std::vector<BitcodeFile *> bitcodeFiles;
extern std::vector<LazyObjFile *> lazyObjFiles;
diff --git a/contrib/llvm-project/lld/ELF/InputSection.cpp b/contrib/llvm-project/lld/ELF/InputSection.cpp
index 8613e0d68e67..7a7ebd974909 100644
--- a/contrib/llvm-project/lld/ELF/InputSection.cpp
+++ b/contrib/llvm-project/lld/ELF/InputSection.cpp
@@ -36,16 +36,17 @@ using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
using namespace llvm::sys;
+using namespace lld;
+using namespace lld::elf;
+
+std::vector<InputSectionBase *> elf::inputSections;
+DenseSet<std::pair<const Symbol *, uint64_t>> elf::ppc64noTocRelax;
-namespace lld {
// Returns a string to construct an error message.
-std::string toString(const elf::InputSectionBase *sec) {
+std::string lld::toString(const InputSectionBase *sec) {
return (toString(sec->file) + ":(" + sec->name + ")").str();
}
-namespace elf {
-std::vector<InputSectionBase *> inputSections;
-
template <class ELFT>
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
const typename ELFT::Shdr &hdr) {
@@ -138,7 +139,7 @@ size_t InputSectionBase::getSize() const {
return s->getSize();
if (uncompressedSize >= 0)
return uncompressedSize;
- return rawData.size();
+ return rawData.size() - bytesDropped;
}
void InputSectionBase::uncompress() const {
@@ -307,7 +308,7 @@ std::string InputSectionBase::getLocation(uint64_t offset) {
// File->sourceFile contains STT_FILE symbol that contains a
// source file name. If it's missing, we use an object file name.
- std::string srcFile = getFile<ELFT>()->sourceFile;
+ std::string srcFile = std::string(getFile<ELFT>()->sourceFile);
if (srcFile.empty())
srcFile = toString(file);
@@ -338,7 +339,7 @@ std::string InputSectionBase::getSrcMsg(const Symbol &sym, uint64_t offset) {
//
// path/to/foo.o:(function bar) in archive path/to/bar.a
std::string InputSectionBase::getObjMsg(uint64_t off) {
- std::string filename = file->getName();
+ std::string filename = std::string(file->getName());
std::string archive;
if (!file->archiveName.empty())
@@ -465,7 +466,7 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
if (!RelTy::IsRela)
addend = target->getImplicitAddend(bufLoc, type);
- if (config->emachine == EM_MIPS && config->relocatable &&
+ if (config->emachine == EM_MIPS &&
target->getRelExpr(type, sym, bufLoc) == R_MIPS_GOTREL) {
// Some MIPS relocations depend on "gp" value. By default,
// this value has 0x7ff0 offset from a .got section. But
@@ -526,9 +527,14 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType type, uint32_t a,
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVT_PREL:
case R_ARM_REL32:
+ case R_ARM_THM_ALU_PREL_11_0:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
+ case R_ARM_THM_PC12:
return p + a;
+ // p + a is unrepresentable as negative immediates can't be encoded.
+ case R_ARM_THM_PC8:
+ return p;
}
llvm_unreachable("ARM pc-relative relocation expected\n");
}
@@ -550,6 +556,7 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t type, uint64_t a,
case R_AARCH64_PREL64:
case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_LD_PREL_LO19:
+ case R_AARCH64_PLT32:
return p + a;
}
llvm_unreachable("AArch64 pc-relative relocation expected\n");
@@ -645,6 +652,7 @@ static int64_t getTlsTpOffset(const Symbol &s) {
// Variant 2.
case EM_HEXAGON:
+ case EM_SPARCV9:
case EM_386:
case EM_X86_64:
return s.getVA(0) - tls->p_memsz -
@@ -654,8 +662,9 @@ static int64_t getTlsTpOffset(const Symbol &s) {
}
}
-static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
- uint64_t p, const Symbol &sym, RelExpr expr) {
+uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
+ int64_t a, uint64_t p,
+ const Symbol &sym, RelExpr expr) {
switch (expr) {
case R_ABS:
case R_DTPREL:
@@ -703,7 +712,7 @@ static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
// microMIPS variants of these relocations use slightly different
// expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi()
- // to correctly handle less-sugnificant bit of the microMIPS symbol.
+ // to correctly handle less-significant bit of the microMIPS symbol.
uint64_t v = in.mipsGot->getGp(file) + a - p;
if (type == R_MIPS_LO16 || type == R_MICROMIPS_LO16)
v += 4;
@@ -740,8 +749,12 @@ static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
*hiRel->sym, hiRel->expr);
return 0;
}
- case R_PC: {
+ case R_PC:
+ case R_ARM_PCA: {
uint64_t dest;
+ if (expr == R_ARM_PCA)
+ // Some PC relative ARM (Thumb) relocations align down the place.
+ p = p & 0xfffffffc;
if (sym.isUndefWeak()) {
// On ARM and AArch64 a branch to an undefined weak resolves to the
// next instruction, otherwise the place.
@@ -796,7 +809,7 @@ static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
// --noinhibit-exec, even a non-weak undefined reference may reach here.
// Just return A, which matches R_ABS, and the behavior of some dynamic
// loaders.
- if (sym.isUndefined())
+ if (sym.isUndefined() || sym.isLazy())
return a;
return getTlsTpOffset(sym) + a;
case R_RELAX_TLS_GD_TO_LE_NEG:
@@ -840,6 +853,16 @@ static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
template <class ELFT, class RelTy>
void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
const unsigned bits = sizeof(typename ELFT::uint) * 8;
+ const bool isDebug = isDebugSection(*this);
+ const bool isDebugLocOrRanges =
+ isDebug && (name == ".debug_loc" || name == ".debug_ranges");
+ const bool isDebugLine = isDebug && name == ".debug_line";
+ Optional<uint64_t> tombstone;
+ for (const auto &patAndValue : llvm::reverse(config->deadRelocInNonAlloc))
+ if (patAndValue.first.match(this->name)) {
+ tombstone = patAndValue.second;
+ break;
+ }
for (const RelTy &rel : rels) {
RelType type = rel.getType(config->isMips64EL);
@@ -862,11 +885,17 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
if (expr == R_NONE)
continue;
+ if (expr == R_SIZE) {
+ target->relocateNoSym(bufLoc, type,
+ SignExtend64<bits>(sym.getSize() + addend));
+ continue;
+ }
+
if (expr != R_ABS && expr != R_DTPREL && expr != R_RISCV_ADD) {
std::string msg = getLocation<ELFT>(offset) +
": has non-ABS relocation " + toString(type) +
" against symbol '" + toString(sym) + "'";
- if (expr != R_PC) {
+ if (expr != R_PC && expr != R_ARM_PCA) {
error(msg);
return;
}
@@ -879,15 +908,49 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
// address 0. For bug-compatibilty, we accept them with warnings. We
// know Steel Bank Common Lisp as of 2018 have this bug.
warn(msg);
- target->relocateOne(bufLoc, type,
- SignExtend64<bits>(sym.getVA(addend - offset)));
+ target->relocateNoSym(bufLoc, type,
+ SignExtend64<bits>(sym.getVA(addend - offset)));
continue;
}
- if (sym.isTls() && !Out::tlsPhdr)
- target->relocateOne(bufLoc, type, 0);
- else
- target->relocateOne(bufLoc, type, SignExtend64<bits>(sym.getVA(addend)));
+ if (tombstone ||
+ (isDebug && (type == target->symbolicRel || expr == R_DTPREL))) {
+ // Resolve relocations in .debug_* referencing (discarded symbols or ICF
+ // folded section symbols) to a tombstone value. Resolving to addend is
+ // unsatisfactory because the result address range may collide with a
+ // valid range of low address, or leave multiple CUs claiming ownership of
+ // the same range of code, which may confuse consumers.
+ //
+ // To address the problems, we use -1 as a tombstone value for most
+ // .debug_* sections. We have to ignore the addend because we don't want
+ // to resolve an address attribute (which may have a non-zero addend) to
+ // -1+addend (wrap around to a low address).
+ //
+ // R_DTPREL type relocations represent an offset into the dynamic thread
+ // vector. The computed value is st_value plus a non-negative offset.
+ // Negative values are invalid, so -1 can be used as the tombstone value.
+ //
+ // If the referenced symbol is discarded (made Undefined), or the
+ // section defining the referenced symbol is garbage collected,
+ // sym.getOutputSection() is nullptr. `ds->section->repl != ds->section`
+ // catches the ICF folded case. However, resolving a relocation in
+ // .debug_line to -1 would stop debugger users from setting breakpoints on
+ // the folded-in function, so exclude .debug_line.
+ //
+ // For pre-DWARF-v5 .debug_loc and .debug_ranges, -1 is a reserved value
+ // (base address selection entry), so -2 is used.
+ auto *ds = dyn_cast<Defined>(&sym);
+ if (!sym.getOutputSection() ||
+ (ds && ds->section->repl != ds->section && !isDebugLine)) {
+ // If -z dead-reloc-in-nonalloc= is specified, respect it.
+ const uint64_t value =
+ tombstone ? SignExtend64<bits>(*tombstone)
+ : (isDebugLocOrRanges ? UINT64_MAX - 1 : UINT64_MAX);
+ target->relocateNoSym(bufLoc, type, value);
+ continue;
+ }
+ }
+ target->relocateNoSym(bufLoc, type, SignExtend64<bits>(sym.getVA(addend)));
}
}
@@ -904,7 +967,7 @@ static void relocateNonAllocForRelocatable(InputSection *sec, uint8_t *buf) {
assert(rel.expr == R_ABS);
uint8_t *bufLoc = buf + rel.offset + sec->outSecOff;
uint64_t targetVA = SignExtend64(rel.sym->getVA(rel.addend), bits);
- target->relocateOne(bufLoc, rel.type, targetVA);
+ target->relocate(bufLoc, rel, targetVA);
}
}
@@ -932,6 +995,8 @@ void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
const unsigned bits = config->wordsize * 8;
for (const Relocation &rel : relocations) {
+ if (rel.expr == R_NONE)
+ continue;
uint64_t offset = rel.offset;
if (auto *sec = dyn_cast<InputSection>(this))
offset += sec->outSecOff;
@@ -947,29 +1012,35 @@ void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
switch (expr) {
case R_RELAX_GOT_PC:
case R_RELAX_GOT_PC_NOPIC:
- target->relaxGot(bufLoc, type, targetVA);
+ target->relaxGot(bufLoc, rel, targetVA);
break;
case R_PPC64_RELAX_TOC:
- if (!tryRelaxPPC64TocIndirection(type, rel, bufLoc))
- target->relocateOne(bufLoc, type, targetVA);
+ // rel.sym refers to the STT_SECTION symbol associated to the .toc input
+ // section. If an R_PPC64_TOC16_LO (.toc + addend) references the TOC
+ // entry, there may be R_PPC64_TOC16_HA not paired with
+ // R_PPC64_TOC16_LO_DS. Don't relax. This loses some relaxation
+ // opportunities but is safe.
+ if (ppc64noTocRelax.count({rel.sym, rel.addend}) ||
+ !tryRelaxPPC64TocIndirection(rel, bufLoc))
+ target->relocate(bufLoc, rel, targetVA);
break;
case R_RELAX_TLS_IE_TO_LE:
- target->relaxTlsIeToLe(bufLoc, type, targetVA);
+ target->relaxTlsIeToLe(bufLoc, rel, targetVA);
break;
case R_RELAX_TLS_LD_TO_LE:
case R_RELAX_TLS_LD_TO_LE_ABS:
- target->relaxTlsLdToLe(bufLoc, type, targetVA);
+ target->relaxTlsLdToLe(bufLoc, rel, targetVA);
break;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_GD_TO_LE_NEG:
- target->relaxTlsGdToLe(bufLoc, type, targetVA);
+ target->relaxTlsGdToLe(bufLoc, rel, targetVA);
break;
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE:
case R_RELAX_TLS_GD_TO_IE_ABS:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
case R_RELAX_TLS_GD_TO_IE_GOTPLT:
- target->relaxTlsGdToIe(bufLoc, type, targetVA);
+ target->relaxTlsGdToIe(bufLoc, rel, targetVA);
break;
case R_PPC64_CALL:
// If this is a call to __tls_get_addr, it may be part of a TLS
@@ -994,13 +1065,25 @@ void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
}
write32(bufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
}
- target->relocateOne(bufLoc, type, targetVA);
+ target->relocate(bufLoc, rel, targetVA);
break;
default:
- target->relocateOne(bufLoc, type, targetVA);
+ target->relocate(bufLoc, rel, targetVA);
break;
}
}
+
+ // Apply jumpInstrMods. jumpInstrMods are created when the opcode of
+ // a jmp insn must be modified to shrink the jmp insn or to flip the jmp
+ // insn. This is primarily used to relax and optimize jumps created with
+ // basic block sections.
+ if (auto *sec = dyn_cast<InputSection>(this)) {
+ for (const JumpInstrMod &jumpMod : jumpInstrMods) {
+ uint64_t offset = jumpMod.offset + sec->outSecOff;
+ uint8_t *bufLoc = buf + offset;
+ target->applyJumpInstrMod(bufLoc, jumpMod.original, jumpMod.size);
+ }
+ }
}
// For each function-defining prologue, find any calls to __morestack,
@@ -1098,7 +1181,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
end, f->stOther))
continue;
if (!getFile<ELFT>()->someNoSplitStack)
- error(toString(this) + ": " + f->getName() +
+ error(lld::toString(this) + ": " + f->getName() +
" (with -fsplit-stack) calls " + rel.sym->getName() +
" (without -fsplit-stack), but couldn't adjust its prologue");
}
@@ -1361,6 +1444,3 @@ template void EhInputSection::split<ELF32LE>();
template void EhInputSection::split<ELF32BE>();
template void EhInputSection::split<ELF64LE>();
template void EhInputSection::split<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/InputSection.h b/contrib/llvm-project/lld/ELF/InputSection.h
index fe2c3c516a96..112c6ab49a38 100644
--- a/contrib/llvm-project/lld/ELF/InputSection.h
+++ b/contrib/llvm-project/lld/ELF/InputSection.h
@@ -128,6 +128,26 @@ public:
return cast_or_null<ObjFile<ELFT>>(file);
}
+ // If basic block sections are enabled, many code sections could end up with
+ // one or two jump instructions at the end that could be relaxed to a smaller
+ // instruction. The members below help trimming the trailing jump instruction
+ // and shrinking a section.
+ unsigned bytesDropped = 0;
+
+ void drop_back(uint64_t num) { bytesDropped += num; }
+
+ void push_back(uint64_t num) {
+ assert(bytesDropped >= num);
+ bytesDropped -= num;
+ }
+
+ void trim() {
+ if (bytesDropped) {
+ rawData = rawData.drop_back(bytesDropped);
+ bytesDropped = 0;
+ }
+ }
+
ArrayRef<uint8_t> data() const {
if (uncompressedSize >= 0)
uncompress();
@@ -183,12 +203,25 @@ public:
// the mmap'ed output buffer.
template <class ELFT> void relocate(uint8_t *buf, uint8_t *bufEnd);
void relocateAlloc(uint8_t *buf, uint8_t *bufEnd);
+ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type,
+ int64_t A, uint64_t P, const Symbol &Sym,
+ RelExpr Expr);
// The native ELF reloc data type is not very convenient to handle.
// So we convert ELF reloc records to our own records in Relocations.cpp.
// This vector contains such "cooked" relocations.
std::vector<Relocation> relocations;
+ // Indicates that this section needs to be padded with a NOP filler if set to
+ // true.
+ bool nopFiller = false;
+
+ // These are modifiers to jump instructions that are necessary when basic
+ // block sections are enabled. Basic block sections creates opportunities to
+ // relax jump instructions at basic block boundaries after reordering the
+ // basic blocks.
+ std::vector<JumpInstrMod> jumpInstrMods;
+
// A function compiled with -fsplit-stack calling a function
// compiled without -fsplit-stack needs its prologue adjusted. Find
// such functions and adjust their prologues. This is very similar
@@ -364,6 +397,11 @@ inline bool isDebugSection(const InputSectionBase &sec) {
// The list of all input sections.
extern std::vector<InputSectionBase *> inputSections;
+// The set of TOC entries (.toc + addend) for which we should not apply
+// toc-indirect to toc-relative relaxation. const Symbol * refers to the
+// STT_SECTION symbol associated to the .toc input section.
+extern llvm::DenseSet<std::pair<const Symbol *, uint64_t>> ppc64noTocRelax;
+
} // namespace elf
std::string toString(const elf::InputSectionBase *);
diff --git a/contrib/llvm-project/lld/ELF/LTO.cpp b/contrib/llvm-project/lld/ELF/LTO.cpp
index 2148ac500291..b8041afed6c9 100644
--- a/contrib/llvm-project/lld/ELF/LTO.cpp
+++ b/contrib/llvm-project/lld/ELF/LTO.cpp
@@ -41,9 +41,8 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Creates an empty file to store a list of object files for final
// linking of distributed ThinLTO.
@@ -59,9 +58,9 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
}
static std::string getThinLTOOutputFile(StringRef modulePath) {
- return lto::getThinLTOOutputFile(modulePath,
- config->thinLTOPrefixReplace.first,
- config->thinLTOPrefixReplace.second);
+ return lto::getThinLTOOutputFile(
+ std::string(modulePath), std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second));
}
static lto::Config createConfig() {
@@ -76,6 +75,33 @@ static lto::Config createConfig() {
c.Options.FunctionSections = true;
c.Options.DataSections = true;
+ // Check if basic block sections must be used.
+ // Allowed values for --lto-basicblock-sections are "all", "labels",
+ // "<file name specifying basic block ids>", or none. This is the equivalent
+ // of -fbasic-block-sections= flag in clang.
+ if (!config->ltoBasicBlockSections.empty()) {
+ if (config->ltoBasicBlockSections == "all") {
+ c.Options.BBSections = BasicBlockSection::All;
+ } else if (config->ltoBasicBlockSections == "labels") {
+ c.Options.BBSections = BasicBlockSection::Labels;
+ } else if (config->ltoBasicBlockSections == "none") {
+ c.Options.BBSections = BasicBlockSection::None;
+ } else {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+ MemoryBuffer::getFile(config->ltoBasicBlockSections.str());
+ if (!MBOrErr) {
+ error("cannot open " + config->ltoBasicBlockSections + ":" +
+ MBOrErr.getError().message());
+ } else {
+ c.Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
+ }
+ c.Options.BBSections = BasicBlockSection::List;
+ }
+ }
+
+ c.Options.UniqueBasicBlockSectionNames =
+ config->ltoUniqueBasicBlockSectionNames;
+
if (auto relocModel = getRelocModelFromCMModel())
c.RelocModel = *relocModel;
else if (config->relocatable)
@@ -97,21 +123,30 @@ static lto::Config createConfig() {
c.PTO.SLPVectorization = c.OptLevel > 1;
// Set up a custom pipeline if we've been asked to.
- c.OptPipeline = config->ltoNewPmPasses;
- c.AAPipeline = config->ltoAAPipeline;
+ c.OptPipeline = std::string(config->ltoNewPmPasses);
+ c.AAPipeline = std::string(config->ltoAAPipeline);
// Set up optimization remarks if we've been asked to.
- c.RemarksFilename = config->optRemarksFilename;
- c.RemarksPasses = config->optRemarksPasses;
+ c.RemarksFilename = std::string(config->optRemarksFilename);
+ c.RemarksPasses = std::string(config->optRemarksPasses);
c.RemarksWithHotness = config->optRemarksWithHotness;
- c.RemarksFormat = config->optRemarksFormat;
+ c.RemarksFormat = std::string(config->optRemarksFormat);
- c.SampleProfile = config->ltoSampleProfile;
+ c.SampleProfile = std::string(config->ltoSampleProfile);
c.UseNewPM = config->ltoNewPassManager;
c.DebugPassManager = config->ltoDebugPassManager;
- c.DwoDir = config->dwoDir;
+ c.DwoDir = std::string(config->dwoDir);
- c.CSIRProfile = config->ltoCSProfileFile;
+ c.HasWholeProgramVisibility = config->ltoWholeProgramVisibility;
+ c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
+
+ for (const llvm::StringRef &name : config->thinLTOModulesToCompile)
+ c.ThinLTOModulesToCompile.emplace_back(name);
+
+ c.TimeTraceEnabled = config->timeTraceEnabled;
+ c.TimeTraceGranularity = config->timeTraceGranularity;
+
+ c.CSIRProfile = std::string(config->ltoCSProfileFile);
c.RunCSIRInstr = config->ltoCSProfileGenerate;
if (config->emitLLVM) {
@@ -122,6 +157,9 @@ static lto::Config createConfig() {
};
}
+ if (config->ltoEmitAsm)
+ c.CGFileType = CGFT_AssemblyFile;
+
if (config->saveTemps)
checkError(c.addSaveTemps(config->outputFile.str() + ".",
/*UseInputModulePath*/ true));
@@ -138,10 +176,12 @@ BitcodeCompiler::BitcodeCompiler() {
if (config->thinLTOIndexOnly) {
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
backend = lto::createWriteIndexesThinBackend(
- config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second),
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
- } else if (config->thinLTOJobs != -1U) {
- backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ } else {
+ backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
@@ -218,7 +258,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
// distributed build system that depends on that behavior.
static void thinLTOCreateEmptyIndexFiles() {
for (LazyObjFile *f : lazyObjFiles) {
- if (!isBitcode(f->mb))
+ if (f->fetched || !isBitcode(f->mb))
continue;
std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName()));
std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
@@ -259,12 +299,14 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
},
cache));
- // Emit empty index files for non-indexed files
- for (StringRef s : thinIndices) {
- std::string path = getThinLTOOutputFile(s);
- openFile(path + ".thinlto.bc");
- if (config->thinLTOEmitImportsFiles)
- openFile(path + ".imports");
+ // Emit empty index files for non-indexed files but not in single-module mode.
+ if (config->thinLTOModulesToCompile.empty()) {
+ for (StringRef s : thinIndices) {
+ std::string path = getThinLTOOutputFile(s);
+ openFile(path + ".thinlto.bc");
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
+ }
}
if (config->thinLTOIndexOnly) {
@@ -291,11 +333,19 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
}
if (config->saveTemps) {
- saveBuffer(buf[0], config->outputFile + ".lto.o");
+ if (!buf[0].empty())
+ saveBuffer(buf[0], config->outputFile + ".lto.o");
for (unsigned i = 1; i != maxTasks; ++i)
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
}
+ if (config->ltoEmitAsm) {
+ saveBuffer(buf[0], config->outputFile);
+ for (unsigned i = 1; i != maxTasks; ++i)
+ saveBuffer(buf[i], config->outputFile + Twine(i));
+ return {};
+ }
+
std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i)
if (!buf[i].empty())
@@ -306,6 +356,3 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
ret.push_back(createObjectFile(*file));
return ret;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/LinkerScript.cpp b/contrib/llvm-project/lld/ELF/LinkerScript.cpp
index 57e0e1e8acbf..72e2ebff9b8c 100644
--- a/contrib/llvm-project/lld/ELF/LinkerScript.cpp
+++ b/contrib/llvm-project/lld/ELF/LinkerScript.cpp
@@ -21,7 +21,6 @@
#include "Writer.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -29,6 +28,7 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include <algorithm>
#include <cassert>
@@ -43,10 +43,10 @@ using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
-LinkerScript *script;
+LinkerScript *elf::script;
static uint64_t getOutputSectionVA(SectionBase *sec) {
OutputSection *os = sec->getOutputSection();
@@ -88,7 +88,7 @@ OutputSection *LinkerScript::createOutputSection(StringRef name,
if (!secRef)
secRef = sec;
}
- sec->location = location;
+ sec->location = std::string(location);
return sec;
}
@@ -103,10 +103,11 @@ OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) {
static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size,
StringRef regionName, StringRef secName) {
memRegion->curPos += size;
- uint64_t newSize = memRegion->curPos - memRegion->origin;
- if (newSize > memRegion->length)
+ uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue();
+ uint64_t length = (memRegion->length)().getValue();
+ if (newSize > length)
error("section '" + secName + "' will not fit in region '" + regionName +
- "': overflowed by " + Twine(newSize - memRegion->length) + " bytes");
+ "': overflowed by " + Twine(newSize - length) + " bytes");
}
void LinkerScript::expandMemoryRegions(uint64_t size) {
@@ -246,32 +247,30 @@ getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
return changed;
}
-// This method is used to handle INSERT AFTER statement. Here we rebuild
-// the list of script commands to mix sections inserted into.
+// Process INSERT [AFTER|BEFORE] commands. For each command, we move the
+// specified output section to the designated place.
void LinkerScript::processInsertCommands() {
- std::vector<BaseCommand *> v;
- auto insert = [&](std::vector<BaseCommand *> &from) {
- v.insert(v.end(), from.begin(), from.end());
- from.clear();
- };
-
- for (BaseCommand *base : sectionCommands) {
- if (auto *os = dyn_cast<OutputSection>(base)) {
- insert(insertBeforeCommands[os->name]);
- v.push_back(base);
- insert(insertAfterCommands[os->name]);
+ for (const InsertCommand &cmd : insertCommands) {
+ // If cmd.os is empty, it may have been discarded by
+ // adjustSectionsBeforeSorting(). We do not handle such output sections.
+ auto from = llvm::find(sectionCommands, cmd.os);
+ if (from == sectionCommands.end())
continue;
+ sectionCommands.erase(from);
+
+ auto insertPos = llvm::find_if(sectionCommands, [&cmd](BaseCommand *base) {
+ auto *to = dyn_cast<OutputSection>(base);
+ return to != nullptr && to->name == cmd.where;
+ });
+ if (insertPos == sectionCommands.end()) {
+ error("unable to insert " + cmd.os->name +
+ (cmd.isAfter ? " after " : " before ") + cmd.where);
+ } else {
+ if (cmd.isAfter)
+ ++insertPos;
+ sectionCommands.insert(insertPos, cmd.os);
}
- v.push_back(base);
}
-
- for (auto &cmds : {insertBeforeCommands, insertAfterCommands})
- for (const std::pair<StringRef, std::vector<BaseCommand *>> &p : cmds)
- if (!p.second.empty())
- error("unable to INSERT AFTER/BEFORE " + p.first +
- ": section not defined");
-
- sectionCommands = std::move(v);
}
// Symbols defined in script should not be inlined by LTO. At the same time
@@ -324,8 +323,8 @@ static std::string getFilename(InputFile *file) {
if (!file)
return "";
if (file->archiveName.empty())
- return file->getName();
- return (file->archiveName + "(" + file->getName() + ")").str();
+ return std::string(file->getName());
+ return (file->archiveName + ':' + file->getName()).str();
}
bool LinkerScript::shouldKeep(InputSectionBase *s) {
@@ -335,7 +334,9 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) {
for (InputSectionDescription *id : keptSections)
if (id->filePat.match(filename))
for (SectionPattern &p : id->sectionPatterns)
- if (p.sectionPat.match(s->name))
+ if (p.sectionPat.match(s->name) &&
+ (s->flags & id->withFlags) == id->withFlags &&
+ (s->flags & id->withoutFlags) == 0)
return true;
return false;
}
@@ -406,14 +407,15 @@ static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSectionBase *>
-LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
+LinkerScript::computeInputSections(const InputSectionDescription *cmd,
+ ArrayRef<InputSectionBase *> sections) {
std::vector<InputSectionBase *> ret;
// Collects all sections that satisfy constraints of Cmd.
for (const SectionPattern &pat : cmd->sectionPatterns) {
size_t sizeBefore = ret.size();
- for (InputSectionBase *sec : inputSections) {
+ for (InputSectionBase *sec : sections) {
if (!sec->isLive() || sec->parent)
continue;
@@ -426,10 +428,15 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
cast<InputSection>(sec)->getRelocatedSection())
continue;
+ // Check the name early to improve performance in the common case.
+ if (!pat.sectionPat.match(sec->name))
+ continue;
+
std::string filename = getFilename(sec->file);
if (!cmd->filePat.match(filename) ||
pat.excludedFilePat.match(filename) ||
- !pat.sectionPat.match(sec->name))
+ (sec->flags & cmd->withFlags) != cmd->withFlags ||
+ (sec->flags & cmd->withoutFlags) != 0)
continue;
ret.push_back(sec);
@@ -459,13 +466,29 @@ void LinkerScript::discard(InputSectionBase *s) {
discard(ds);
}
+void LinkerScript::discardSynthetic(OutputSection &outCmd) {
+ for (Partition &part : partitions) {
+ if (!part.armExidx || !part.armExidx->isLive())
+ continue;
+ std::vector<InputSectionBase *> secs(part.armExidx->exidxSections.begin(),
+ part.armExidx->exidxSections.end());
+ for (BaseCommand *base : outCmd.sectionCommands)
+ if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
+ std::vector<InputSectionBase *> matches =
+ computeInputSections(cmd, secs);
+ for (InputSectionBase *s : matches)
+ discard(s);
+ }
+ }
+}
+
std::vector<InputSectionBase *>
LinkerScript::createInputSectionList(OutputSection &outCmd) {
std::vector<InputSectionBase *> ret;
for (BaseCommand *base : outCmd.sectionCommands) {
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
- cmd->sectionBases = computeInputSections(cmd);
+ cmd->sectionBases = computeInputSections(cmd, inputSections);
for (InputSectionBase *s : cmd->sectionBases)
s->parent = &outCmd;
ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
@@ -486,6 +509,7 @@ void LinkerScript::processSectionCommands() {
if (sec->name == "/DISCARD/") {
for (InputSectionBase *s : v)
discard(s);
+ discardSynthetic(*sec);
sec->sectionCommands.clear();
continue;
}
@@ -676,14 +700,12 @@ void LinkerScript::addOrphanSections() {
std::function<void(InputSectionBase *)> add;
add = [&](InputSectionBase *s) {
if (s->isLive() && !s->parent) {
- StringRef name = getOutputSectionName(s);
-
- if (config->orphanHandling == OrphanHandlingPolicy::Error)
- error(toString(s) + " is being placed in '" + name + "'");
- else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
- warn(toString(s) + " is being placed in '" + name + "'");
+ orphanSections.push_back(s);
- if (OutputSection *sec = findByName(sectionCommands, name)) {
+ StringRef name = getOutputSectionName(s);
+ if (config->unique) {
+ v.push_back(createSection(s, name));
+ } else if (OutputSection *sec = findByName(sectionCommands, name)) {
sec->recordSection(s);
} else {
if (OutputSection *os = addInputSec(map, s, name))
@@ -727,6 +749,22 @@ void LinkerScript::addOrphanSections() {
sectionCommands.insert(sectionCommands.begin(), v.begin(), v.end());
}
+void LinkerScript::diagnoseOrphanHandling() const {
+ for (const InputSectionBase *sec : orphanSections) {
+ // Input SHT_REL[A] retained by --emit-relocs are ignored by
+ // computeInputSections(). Don't warn/error.
+ if (isa<InputSection>(sec) &&
+ cast<InputSection>(sec)->getRelocatedSection())
+ continue;
+
+ StringRef name = getOutputSectionName(sec);
+ if (config->orphanHandling == OrphanHandlingPolicy::Error)
+ error(toString(sec) + " is being placed in '" + name + "'");
+ else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
+ warn(toString(sec) + " is being placed in '" + name + "'");
+ }
+}
+
uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) {
bool isTbss =
(ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS;
@@ -756,9 +794,16 @@ void LinkerScript::output(InputSection *s) {
void LinkerScript::switchTo(OutputSection *sec) {
ctx->outSec = sec;
- uint64_t before = advance(0, 1);
- ctx->outSec->addr = advance(0, ctx->outSec->alignment);
- expandMemoryRegions(ctx->outSec->addr - before);
+ uint64_t pos = advance(0, 1);
+ if (sec->addrExpr && script->hasSectionsCommand) {
+ // The alignment is ignored.
+ ctx->outSec->addr = pos;
+ } else {
+ // ctx->outSec->alignment is the max of ALIGN and the maximum of input
+ // section alignments.
+ ctx->outSec->addr = advance(0, ctx->outSec->alignment);
+ expandMemoryRegions(ctx->outSec->addr - pos);
+ }
}
// This function searches for a memory region to place the given output
@@ -806,6 +851,8 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
if (!(sec->flags & SHF_ALLOC))
dot = 0;
+ const bool sameMemRegion = ctx->memRegion == sec->memRegion;
+ const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr;
ctx->memRegion = sec->memRegion;
ctx->lmaRegion = sec->lmaRegion;
if (ctx->memRegion)
@@ -824,18 +871,20 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
switchTo(sec);
+ // ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or
+ // AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA
+ // region is the default, and the two sections are in the same memory region,
+ // reuse previous lmaOffset; otherwise, reset lmaOffset to 0. This emulates
+ // heuristics described in
+ // https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html
if (sec->lmaExpr)
ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
+ else if (MemoryRegion *mr = sec->lmaRegion)
+ ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot;
+ else if (!sameMemRegion || !prevLMARegionIsDefault)
+ ctx->lmaOffset = 0;
- if (MemoryRegion *mr = sec->lmaRegion)
- ctx->lmaOffset = mr->curPos - dot;
-
- // If neither AT nor AT> is specified for an allocatable section, the linker
- // will set the LMA such that the difference between VMA and LMA for the
- // section is the same as the preceding output section in the same region
- // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
- // This, however, should only be done by the first "non-header" section
- // in the segment.
+ // Propagate ctx->lmaOffset to the first "non-header" section.
if (PhdrEntry *l = ctx->outSec->ptLoad)
if (sec == findFirstSection(l))
l->lmaOffset = ctx->lmaOffset;
@@ -946,7 +995,7 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// We do not want to keep any special flags for output section
// in case it is empty.
- bool isEmpty = getInputSections(sec).empty();
+ bool isEmpty = (getFirstInputSection(sec) == nullptr);
if (isEmpty)
sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) |
SHF_WRITE | SHF_EXECINSTR);
@@ -1068,7 +1117,7 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
LinkerScript::AddressState::AddressState() {
for (auto &mri : script->memoryRegions) {
MemoryRegion *mr = mri.second;
- mr->curPos = mr->origin;
+ mr->curPos = (mr->origin)().getValue();
}
}
@@ -1195,11 +1244,8 @@ std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
if (Optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
ret.push_back(*idx);
else if (s != "NONE")
- error(cmd->location + ": section header '" + s +
+ error(cmd->location + ": program header '" + s +
"' is not listed in PHDRS");
}
return ret;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/LinkerScript.h b/contrib/llvm-project/lld/ELF/LinkerScript.h
index 25a14e08dade..ec4fc22db486 100644
--- a/contrib/llvm-project/lld/ELF/LinkerScript.h
+++ b/contrib/llvm-project/lld/ELF/LinkerScript.h
@@ -109,11 +109,11 @@ struct SymbolAssignment : BaseCommand {
std::string commandString;
// Address of this assignment command.
- unsigned addr;
+ uint64_t addr;
// Size of this assignment command. This is usually 0, but if
// you move '.' this may be greater than 0.
- unsigned size;
+ uint64_t size;
};
// Linker scripts allow additional constraints to be put on output sections.
@@ -126,14 +126,14 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
// target memory. Instances of the struct are created by parsing the
// MEMORY command.
struct MemoryRegion {
- MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags,
+ MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags,
uint32_t negFlags)
- : name(name), origin(origin), length(length), flags(flags),
+ : name(std::string(name)), origin(origin), length(length), flags(flags),
negFlags(negFlags) {}
std::string name;
- uint64_t origin;
- uint64_t length;
+ Expr origin;
+ Expr length;
uint32_t flags;
uint32_t negFlags;
uint64_t curPos = 0;
@@ -155,14 +155,16 @@ struct SectionPattern {
};
struct InputSectionDescription : BaseCommand {
- InputSectionDescription(StringRef filePattern)
- : BaseCommand(InputSectionKind), filePat(filePattern) {}
+ InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0,
+ uint64_t withoutFlags = 0)
+ : BaseCommand(InputSectionKind), filePat(filePattern),
+ withFlags(withFlags), withoutFlags(withoutFlags) {}
static bool classof(const BaseCommand *c) {
return c->kind == InputSectionKind;
}
- StringMatcher filePat;
+ SingleStringMatcher filePat;
// Input sections that matches at least one of SectionPatterns
// will be associated with this InputSectionDescription.
@@ -180,6 +182,10 @@ struct InputSectionDescription : BaseCommand {
// they were created in. This is used to insert newly created ThunkSections
// into Sections at the end of a createThunks() pass.
std::vector<std::pair<ThunkSection *, uint32_t>> thunkSections;
+
+ // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command.
+ uint64_t withFlags;
+ uint64_t withoutFlags;
};
// Represents BYTE(), SHORT(), LONG(), or QUAD().
@@ -202,6 +208,12 @@ struct ByteCommand : BaseCommand {
unsigned size;
};
+struct InsertCommand {
+ OutputSection *os;
+ bool isAfter;
+ StringRef where;
+};
+
struct PhdrsCommand {
StringRef name;
unsigned type = llvm::ELF::PT_NULL;
@@ -233,10 +245,13 @@ class LinkerScript final {
void expandMemoryRegions(uint64_t size);
std::vector<InputSectionBase *>
- computeInputSections(const InputSectionDescription *);
+ computeInputSections(const InputSectionDescription *,
+ ArrayRef<InputSectionBase *>);
std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
+ void discardSynthetic(OutputSection &);
+
std::vector<size_t> getPhdrIndices(OutputSection *sec);
MemoryRegion *findMemoryRegion(OutputSection *sec);
@@ -270,6 +285,7 @@ public:
ExprValue getSymbolValue(StringRef name, const Twine &loc);
void addOrphanSections();
+ void diagnoseOrphanHandling() const;
void adjustSectionsBeforeSorting();
void adjustSectionsAfterSorting();
@@ -305,10 +321,12 @@ public:
// A list of symbols referenced by the script.
std::vector<llvm::StringRef> referencedSymbols;
- // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
- // to be inserted into SECTIONS commands list.
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertAfterCommands;
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertBeforeCommands;
+ // Used to implement INSERT [AFTER|BEFORE]. Contains output sections that need
+ // to be reordered.
+ std::vector<InsertCommand> insertCommands;
+
+ // Sections that will be warned/errored by --orphan-handling.
+ std::vector<const InputSectionBase *> orphanSections;
};
extern LinkerScript *script;
diff --git a/contrib/llvm-project/lld/ELF/MapFile.cpp b/contrib/llvm-project/lld/ELF/MapFile.cpp
index e5f5c4f4ff23..12cffead1f80 100644
--- a/contrib/llvm-project/lld/ELF/MapFile.cpp
+++ b/contrib/llvm-project/lld/ELF/MapFile.cpp
@@ -26,16 +26,16 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
static constexpr char indent8[] = " "; // 8 spaces
@@ -138,7 +138,7 @@ static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
}
}
-void writeMapFile() {
+void elf::writeMapFile() {
if (config->mapFile.empty())
return;
@@ -227,7 +227,7 @@ static void print(StringRef a, StringRef b) {
//
// In this case, strlen is defined by libc.so.6 and used by other two
// files.
-void writeCrossReferenceTable() {
+void elf::writeCrossReferenceTable() {
if (!config->cref)
return;
@@ -259,5 +259,20 @@ void writeCrossReferenceTable() {
}
}
-} // namespace elf
-} // namespace lld
+void elf::writeArchiveStats() {
+ if (config->printArchiveStats.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None);
+ if (ec) {
+ error("--print-archive-stats=: cannot open " + config->printArchiveStats +
+ ": " + ec.message());
+ return;
+ }
+
+ os << "members\tfetched\tarchive\n";
+ for (const ArchiveFile *f : archiveFiles)
+ os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t'
+ << f->getName() << '\n';
+}
diff --git a/contrib/llvm-project/lld/ELF/MapFile.h b/contrib/llvm-project/lld/ELF/MapFile.h
index 7e7938919edf..c4da18f8ad7f 100644
--- a/contrib/llvm-project/lld/ELF/MapFile.h
+++ b/contrib/llvm-project/lld/ELF/MapFile.h
@@ -13,6 +13,7 @@ namespace lld {
namespace elf {
void writeMapFile();
void writeCrossReferenceTable();
+void writeArchiveStats();
} // namespace elf
} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/MarkLive.cpp b/contrib/llvm-project/lld/ELF/MarkLive.cpp
index bb0105c28928..28e13e8c1234 100644
--- a/contrib/llvm-project/lld/ELF/MarkLive.cpp
+++ b/contrib/llvm-project/lld/ELF/MarkLive.cpp
@@ -31,17 +31,17 @@
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
+#include "llvm/Support/TimeProfiler.h"
#include <functional>
#include <vector>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace endian = llvm::support::endian;
-
-namespace lld {
-namespace elf {
namespace {
template <class ELFT> class MarkLive {
public:
@@ -141,7 +141,7 @@ void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
if (firstRelI == (unsigned)-1)
continue;
- if (endian::read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
+ if (read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
// This is a CIE, we only need to worry about the first relocation. It is
// known to point to the personality function.
resolveReloc(eh, rels[firstRelI], false);
@@ -322,7 +322,8 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
// Before calling this function, Live bits are off for all
// input sections. This function make some or all of them on
// so that they are emitted to the output file.
-template <class ELFT> void markLive() {
+template <class ELFT> void elf::markLive() {
+ llvm::TimeTraceScope timeScope("markLive");
// If -gc-sections is not given, no sections are removed.
if (!config->gcSections) {
for (InputSectionBase *sec : inputSections)
@@ -390,10 +391,7 @@ template <class ELFT> void markLive() {
message("removing unused section " + toString(sec));
}
-template void markLive<ELF32LE>();
-template void markLive<ELF32BE>();
-template void markLive<ELF64LE>();
-template void markLive<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
+template void elf::markLive<ELF32LE>();
+template void elf::markLive<ELF32BE>();
+template void elf::markLive<ELF64LE>();
+template void elf::markLive<ELF64BE>();
diff --git a/contrib/llvm-project/lld/ELF/Options.td b/contrib/llvm-project/lld/ELF/Options.td
index ea78a3526211..c3c1309aca1a 100644
--- a/contrib/llvm-project/lld/ELF/Options.td
+++ b/contrib/llvm-project/lld/ELF/Options.td
@@ -1,5 +1,23 @@
include "llvm/Option/OptParser.td"
+// Convenience classes for long options which only accept two dashes. For lld
+// specific or newer long options, we prefer two dashes to avoid collision with
+// short options. For many others, we have to accept both forms to be compatible
+// with GNU ld.
+class FF<string name> : Flag<["--"], name>;
+class JJ<string name>: Joined<["--"], name>;
+
+multiclass EEq<string name, string help> {
+ def NAME: Separate<["--"], name>;
+ def NAME # _eq: Joined<["--"], name # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+}
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
// For options whose names are multiple letters, either one dash or
// two can precede the option name except those that start with 'o'.
class F<string name>: Flag<["--", "-"], name>;
@@ -42,6 +60,10 @@ defm compress_debug_sections:
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
+defm optimize_bb_jumps: BB<"optimize-bb-jumps",
+ "Remove direct jumps at the end to the next basic block",
+ "Do not remove any direct jumps at the end to the next basic block (default)">;
+
defm split_stack_adjust_size
: Eq<"split-stack-adjust-size",
"Specify adjustment to stack size when a split-stack function calls a "
@@ -69,11 +91,11 @@ defm allow_shlib_undefined: B<"allow-shlib-undefined",
"Allow unresolved references in shared libraries (default when linking a shared library)",
"Do not allow unresolved references in shared libraries (default when linking an executable)">;
-defm apply_dynamic_relocs: B<"apply-dynamic-relocs",
+defm apply_dynamic_relocs: BB<"apply-dynamic-relocs",
"Apply link-time values for dynamic relocations",
"Do not apply link-time values for dynamic relocations (default)">;
-defm dependent_libraries: B<"dependent-libraries",
+defm dependent_libraries: BB<"dependent-libraries",
"Process dependent library specifiers from input files (default)",
"Ignore dependent library specifiers from input files">;
@@ -84,7 +106,7 @@ defm as_needed: B<"as-needed",
defm call_graph_ordering_file:
Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
-defm call_graph_profile_sort: B<"call-graph-profile-sort",
+defm call_graph_profile_sort: BB<"call-graph-profile-sort",
"Reorder sections with call graph profile (default)",
"Do not reorder sections with call graph profile">;
@@ -123,7 +145,12 @@ def discard_none: F<"discard-none">,
defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
-defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">;
+defm dynamic_list : Eq<"dynamic-list",
+ "Read a list of dynamic symbols. (executable) Put matched non-local defined"
+ "symbols to the dynamic symbol table. (shared object) References to matched"
+ "non-local STV_DEFAULT symbols shouldn't be bound to definitions within the "
+ "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">,
+ MetaVarName<"<file>">;
defm eh_frame_hdr: B<"eh-frame-hdr",
"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
@@ -151,7 +178,7 @@ def error_unresolved_symbols: F<"error-unresolved-symbols">,
defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
-defm execute_only: B<"execute-only",
+defm execute_only: BB<"execute-only",
"Mark executable sections unreadable",
"Mark executable sections readable (default)">;
@@ -159,8 +186,12 @@ defm export_dynamic: B<"export-dynamic",
"Put symbols in the dynamic symbol table",
"Do not put symbols in the dynamic symbol table (default)">;
-defm export_dynamic_symbol:
- Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">;
+defm export_dynamic_symbol : EEq<"export-dynamic-symbol",
+ "(executable) Put matched symbols in the dynamic symbol table. "
+ "(shared object) References to matched non-local STV_DEFAULT symbols "
+ "shouldn't be bound to definitions within the shared object. "
+ "Does not imply -Bsymbolic.">,
+ MetaVarName<"glob">;
defm fatal_warnings: B<"fatal-warnings",
"Treat warnings as errors",
@@ -183,11 +214,11 @@ defm gc_sections: B<"gc-sections",
"Enable garbage collection of unused sections",
"Disable garbage collection of unused sections (default)">;
-defm gdb_index: B<"gdb-index",
+defm gdb_index: BB<"gdb-index",
"Generate .gdb_index section",
"Do not generate .gdb_index section (default)">;
-defm gnu_unique: B<"gnu-unique",
+defm gnu_unique: BB<"gnu-unique",
"Enable STB_GNU_UNIQUE symbol binding (default)",
"Disable STB_GNU_UNIQUE symbol binding">;
@@ -227,7 +258,7 @@ defm merge_exidx_entries: B<"merge-exidx-entries",
"Enable merging .ARM.exidx entries (default)",
"Disable merging .ARM.exidx entries">;
-defm mmap_output_file: B<"mmap-output-file",
+defm mmap_output_file: BB<"mmap-output-file",
"Mmap the output file for writing (default)",
"Do not mmap the output file for writing">;
@@ -252,9 +283,6 @@ def no_nmagic: F<"no-nmagic">, MetaVarName<"<magic>">,
def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
HelpText<"Do not set the text data sections to be writable, page align sections (default)">;
-def no_rosegment: F<"no-rosegment">,
- HelpText<"Do not put read-only non-executable sections in their own segment">;
-
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
@@ -264,7 +292,7 @@ def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
HelpText<"Specify the binary format for the output object file">;
-def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
+def omagic: FF<"omagic">, MetaVarName<"<magic>">,
HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">;
defm orphan_handling:
@@ -274,7 +302,7 @@ defm pack_dyn_relocs:
Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
MetaVarName<"[none,android,relr,android+relr]">;
-defm use_android_relr_tags: B<"use-android-relr-tags",
+defm use_android_relr_tags: BB<"use-android-relr-tags",
"Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
"Use SHT_RELR / DT_RELR* tags (default)">;
@@ -293,6 +321,10 @@ defm print_icf_sections: B<"print-icf-sections",
"List identical folded sections",
"Do not list identical folded sections (default)">;
+def print_archive_stats: J<"print-archive-stats=">,
+ HelpText<"Write archive usage statistics to the specified file. "
+ "Print the numbers of members and fetched members for each archive">;
+
defm print_symbol_order: Eq<"print-symbol-order",
"Print a symbol order specified by --call-graph-ordering-file into the specified file">;
@@ -307,6 +339,10 @@ def print_map: F<"print-map">,
defm reproduce: Eq<"reproduce", "Write a tar file containing input files and command line options to reproduce link">;
+defm rosegment: BB<"rosegment",
+ "Put read-only non-executable sections in their own segment (default)",
+ "Do not put read-only non-executable sections in their own segment">;
+
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
@@ -350,9 +386,16 @@ defm target2:
Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
MetaVarName<"<type>">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+defm threads
+ : Eq<"threads",
+ "Number of threads. '1' disables multi-threading. By default all "
+ "available hardware threads are used">;
+
+def time_trace: F<"time-trace">, HelpText<"Record time trace">;
+def time_trace_file_eq: J<"time-trace-file=">, HelpText<"Specify time trace output file">;
+
+defm time_trace_granularity: Eq<"time-trace-granularity",
+ "Minimum time granularity (in microseconds) traced by time profiler">;
defm toc_optimize : B<"toc-optimize",
"(PowerPC64) Enable TOC related optimizations (default)",
@@ -368,6 +411,8 @@ defm undefined: Eq<"undefined", "Force undefined symbol during linking">,
defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">,
MetaVarName<"<pattern>">;
+def unique: F<"unique">, HelpText<"Creates a separate output section for every orphan input section">;
+
defm unresolved_symbols:
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
@@ -386,19 +431,25 @@ def version: F<"version">, HelpText<"Display the version number and exit">;
defm version_script: Eq<"version-script", "Read a version script">;
-defm warn_backrefs: B<"warn-backrefs",
+defm warn_backrefs: BB<"warn-backrefs",
"Warn about backward symbol references to fetch archive members",
"Do not warn about backward symbol references to fetch archive members (default)">;
+defm warn_backrefs_exclude
+ : EEq<"warn-backrefs-exclude",
+ "Glob describing an archive (or an object file within --start-lib) "
+ "which should be ignored for --warn-backrefs.">,
+ MetaVarName<"<glob>">;
+
defm warn_common: B<"warn-common",
"Warn about duplicate common symbols",
"Do not warn about duplicate common symbols (default)">;
-defm warn_ifunc_textrel: B<"warn-ifunc-textrel",
+defm warn_ifunc_textrel: BB<"warn-ifunc-textrel",
"Warn about using ifunc symbols with text relocations",
"Do not warn about using ifunc symbols with text relocations (default)">;
-defm warn_symbol_ordering: B<"warn-symbol-ordering",
+defm warn_symbol_ordering: BB<"warn-symbol-ordering",
"Warn about problems with the symbol ordering file (default)",
"Do not warn about problems with the symbol ordering file">;
@@ -460,84 +511,99 @@ def: JoinedOrSeparate<["-"], "u">, Alias<undefined>, HelpText<"Alias for --undef
def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">;
// LTO-related options.
-def lto_aa_pipeline: J<"lto-aa-pipeline=">,
+def lto_aa_pipeline: JJ<"lto-aa-pipeline=">,
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
-def lto_debug_pass_manager: F<"lto-debug-pass-manager">,
+def lto_debug_pass_manager: FF<"lto-debug-pass-manager">,
HelpText<"Debug new pass manager">;
-def lto_new_pass_manager: F<"lto-new-pass-manager">,
+def lto_emit_asm: FF<"lto-emit-asm">,
+ HelpText<"Emit assembly code">;
+def lto_new_pass_manager: FF<"lto-new-pass-manager">,
HelpText<"Use new pass manager">;
-def lto_newpm_passes: J<"lto-newpm-passes=">,
+def lto_newpm_passes: JJ<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
-def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
+def lto_O: JJ<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
-def lto_partitions: J<"lto-partitions=">,
+def lto_partitions: JJ<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
-def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
+def lto_cs_profile_generate: FF<"lto-cs-profile-generate">,
HelpText<"Perform context sensitive PGO instrumentation">;
-def lto_cs_profile_file: J<"lto-cs-profile-file=">,
+def lto_cs_profile_file: JJ<"lto-cs-profile-file=">,
HelpText<"Context sensitive profile file path">;
-def lto_obj_path_eq: J<"lto-obj-path=">;
-def lto_sample_profile: J<"lto-sample-profile=">,
+def lto_obj_path_eq: JJ<"lto-obj-path=">;
+def lto_sample_profile: JJ<"lto-sample-profile=">,
HelpText<"Sample profile file path">;
+def lto_whole_program_visibility: FF<"lto-whole-program-visibility">,
+ HelpText<"Asserts that the LTO link has whole program visibility">;
def disable_verify: F<"disable-verify">;
defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
HelpText<"YAML output file for optimization remarks">;
def opt_remarks_passes: Separate<["--"], "opt-remarks-passes">,
HelpText<"Regex for the passes that need to be serialized to the output file">;
-def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
+def opt_remarks_with_hotness: FF<"opt-remarks-with-hotness">,
HelpText<"Include hotness information in the optimization remarks file">;
def opt_remarks_format: Separate<["--"], "opt-remarks-format">,
HelpText<"The format used for serializing remarks (default: YAML)">;
-defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">;
def save_temps: F<"save-temps">;
-def thinlto_cache_dir: J<"thinlto-cache-dir=">,
+def lto_basicblock_sections: JJ<"lto-basicblock-sections=">,
+ HelpText<"Enable basic block sections for LTO">;
+defm lto_unique_bb_section_names: BB<"lto-unique-bb-section-names",
+ "Give unique names to every basic block section for LTO",
+ "Do not give unique names to every basic block section for LTO (default)">;
+def shuffle_sections: JJ<"shuffle-sections=">, MetaVarName<"<seed>">,
+ HelpText<"Shuffle input sections using the given seed. If 0, use a random seed">;
+def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
-defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
-def thinlto_emit_imports_files: F<"thinlto-emit-imports-files">;
-def thinlto_index_only: F<"thinlto-index-only">;
-def thinlto_index_only_eq: J<"thinlto-index-only=">;
-def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
-def thinlto_object_suffix_replace_eq: J<"thinlto-object-suffix-replace=">;
-def thinlto_prefix_replace_eq: J<"thinlto-prefix-replace=">;
-
-def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
+defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
+def thinlto_emit_imports_files: FF<"thinlto-emit-imports-files">;
+def thinlto_index_only: FF<"thinlto-index-only">;
+def thinlto_index_only_eq: JJ<"thinlto-index-only=">;
+def thinlto_jobs: JJ<"thinlto-jobs=">,
+ HelpText<"Number of ThinLTO jobs. Default to --threads=">;
+def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">;
+def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">;
+def thinlto_single_module_eq: JJ<"thinlto-single-module=">,
+ HelpText<"Specific a single module to compile in ThinLTO mode, for debugging only">;
+
+def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for --lto-O">;
def: F<"plugin-opt=debug-pass-manager">,
- Alias<lto_debug_pass_manager>, HelpText<"Alias for -lto-debug-pass-manager">;
-def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">;
+ Alias<lto_debug_pass_manager>, HelpText<"Alias for --lto-debug-pass-manager">;
+def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for --disable-verify">;
def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">,
HelpText<"Directory to store .dwo files when LTO and debug fission are used">;
+def plugin_opt_emit_asm: F<"plugin-opt=emit-asm">,
+ Alias<lto_emit_asm>, HelpText<"Alias for --lto-emit-asm">;
def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">;
-def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">;
-def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">;
+def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for --thinlto-jobs">;
+def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for --lto-partitions">;
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
def: F<"plugin-opt=new-pass-manager">,
- Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
+ Alias<lto_new_pass_manager>, HelpText<"Alias for --lto-new-pass-manager">;
def: F<"plugin-opt=cs-profile-generate">,
- Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
+ Alias<lto_cs_profile_generate>, HelpText<"Alias for --lto-cs-profile-generate">;
def: J<"plugin-opt=cs-profile-path=">,
- Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
+ Alias<lto_cs_profile_file>, HelpText<"Alias for --lto-cs-profile-file">;
def: J<"plugin-opt=obj-path=">,
Alias<lto_obj_path_eq>,
- HelpText<"Alias for -lto-obj-path=">;
+ HelpText<"Alias for --lto-obj-path=">;
def: J<"plugin-opt=sample-profile=">,
- Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
-def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
+ Alias<lto_sample_profile>, HelpText<"Alias for --lto-sample-profile">;
+def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for --save-temps">;
def: F<"plugin-opt=thinlto-emit-imports-files">,
Alias<thinlto_emit_imports_files>,
- HelpText<"Alias for -thinlto-emit-imports-files">;
+ HelpText<"Alias for --thinlto-emit-imports-files">;
def: F<"plugin-opt=thinlto-index-only">,
Alias<thinlto_index_only>,
- HelpText<"Alias for -thinlto-index-only">;
+ HelpText<"Alias for --thinlto-index-only">;
def: J<"plugin-opt=thinlto-index-only=">,
Alias<thinlto_index_only_eq>,
- HelpText<"Alias for -thinlto-index-only=">;
+ HelpText<"Alias for --thinlto-index-only=">;
def: J<"plugin-opt=thinlto-object-suffix-replace=">,
Alias<thinlto_object_suffix_replace_eq>,
- HelpText<"Alias for -thinlto-object-suffix-replace=">;
+ HelpText<"Alias for --thinlto-object-suffix-replace=">;
def: J<"plugin-opt=thinlto-prefix-replace=">,
Alias<thinlto_prefix_replace_eq>,
- HelpText<"Alias for -thinlto-prefix-replace=">;
+ HelpText<"Alias for --thinlto-prefix-replace=">;
// Ignore LTO plugin-related options.
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
@@ -548,10 +614,17 @@ def: J<"plugin-opt=thinlto-prefix-replace=">,
// --version output.
defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">;
-def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">;
-def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">;
-def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
-def plugin_opt_slash: J<"plugin-opt=/">;
+def plugin_opt_eq_minus: J<"plugin-opt=-">,
+ HelpText<"Specify an LLVM option for compatibility with LLVMgold.so">;
+def: J<"plugin-opt=thinlto">;
+
+// Ignore GCC collect2 LTO plugin related options. Note that we don't support
+// GCC LTO, but GCC collect2 passes these options even in non-LTO mode.
+def: J<"plugin-opt=-fresolution=">;
+def: J<"plugin-opt=-pass-through=">;
+// This may be either an unhandled LLVMgold.so feature or GCC passed
+// -plugin-opt=path/to/{liblto_plugin.so,lto-wrapper}
+def plugin_opt_eq : J<"plugin-opt=">;
// Options listed below are silently ignored for now for compatibility.
def: F<"detect-odr-violations">;
@@ -562,6 +635,7 @@ def: F<"no-copy-dt-needed-entries">;
def: F<"no-ctors-in-init-array">;
def: F<"no-keep-memory">;
def: F<"no-pipeline-knowledge">;
+def: F<"no-relax">;
def: F<"no-warn-mismatch">;
def: Flag<["-"], "p">;
def: Separate<["--", "-"], "rpath-link">;
diff --git a/contrib/llvm-project/lld/ELF/OutputSections.cpp b/contrib/llvm-project/lld/ELF/OutputSections.cpp
index b609878be319..7e9e76b070ec 100644
--- a/contrib/llvm-project/lld/ELF/OutputSections.cpp
+++ b/contrib/llvm-project/lld/ELF/OutputSections.cpp
@@ -14,11 +14,11 @@
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/SHA1.h"
#include <regex>
@@ -27,9 +27,9 @@ using namespace llvm::dwarf;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
uint8_t *Out::bufferStart;
uint8_t Out::first;
PhdrEntry *Out::tlsPhdr;
@@ -39,7 +39,7 @@ OutputSection *Out::preinitArray;
OutputSection *Out::initArray;
OutputSection *Out::finiArray;
-std::vector<OutputSection *> outputSections;
+std::vector<OutputSection *> elf::outputSections;
uint32_t OutputSection::getPhdrFlags() const {
uint32_t ret = 0;
@@ -225,7 +225,7 @@ static void sortByOrder(MutableArrayRef<InputSection *> in,
in[i] = v[i].second;
}
-uint64_t getHeaderSize() {
+uint64_t elf::getHeaderSize() {
if (config->oFormatBinary)
return 0;
return Out::elfHeader->size + Out::programHeaders->size;
@@ -242,6 +242,25 @@ void OutputSection::sort(llvm::function_ref<int(InputSectionBase *s)> order) {
sortByOrder(isd->sections, order);
}
+static void nopInstrFill(uint8_t *buf, size_t size) {
+ if (size == 0)
+ return;
+ unsigned i = 0;
+ if (size == 0)
+ return;
+ std::vector<std::vector<uint8_t>> nopFiller = *target->nopInstrs;
+ unsigned num = size / nopFiller.back().size();
+ for (unsigned c = 0; c < num; ++c) {
+ memcpy(buf + i, nopFiller.back().data(), nopFiller.back().size());
+ i += nopFiller.back().size();
+ }
+ unsigned remaining = size - i;
+ if (!remaining)
+ return;
+ assert(nopFiller[remaining - 1].size() == remaining);
+ memcpy(buf + i, nopFiller[remaining - 1].data(), remaining);
+}
+
// Fill [Buf, Buf + Size) with Filler.
// This is used for linker script "=fillexp" command.
static void fill(uint8_t *buf, size_t size,
@@ -330,7 +349,11 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *buf) {
end = buf + size;
else
end = buf + sections[i + 1]->outSecOff;
- fill(start, end - start, filler);
+ if (isec->nopFiller) {
+ assert(target->nopInstrs);
+ nopInstrFill(start, end - start);
+ } else
+ fill(start, end - start, filler);
}
});
@@ -356,8 +379,7 @@ static void finalizeShtGroup(OutputSection *os,
}
void OutputSection::finalize() {
- std::vector<InputSection *> v = getInputSections(this);
- InputSection *first = v.empty() ? nullptr : v[0];
+ InputSection *first = getFirstInputSection(this);
if (flags & SHF_LINK_ORDER) {
// We must preserve the link order dependency of sections with the
@@ -456,7 +478,7 @@ void OutputSection::sortCtorsDtors() {
// If an input string is in the form of "foo.N" where N is a number,
// return N. Otherwise, returns 65536, which is one greater than the
// lowest priority.
-int getPriority(StringRef s) {
+int elf::getPriority(StringRef s) {
size_t pos = s.rfind('.');
if (pos == StringRef::npos)
return 65536;
@@ -466,7 +488,15 @@ int getPriority(StringRef s) {
return v;
}
-std::vector<InputSection *> getInputSections(OutputSection *os) {
+InputSection *elf::getFirstInputSection(const OutputSection *os) {
+ for (BaseCommand *base : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ if (!isd->sections.empty())
+ return isd->sections[0];
+ return nullptr;
+}
+
+std::vector<InputSection *> elf::getInputSections(const OutputSection *os) {
std::vector<InputSection *> ret;
for (BaseCommand *base : os->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(base))
@@ -507,6 +537,3 @@ template void OutputSection::maybeCompress<ELF32LE>();
template void OutputSection::maybeCompress<ELF32BE>();
template void OutputSection::maybeCompress<ELF64LE>();
template void OutputSection::maybeCompress<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/OutputSections.h b/contrib/llvm-project/lld/ELF/OutputSections.h
index a24294eedf35..d5686f11ec8e 100644
--- a/contrib/llvm-project/lld/ELF/OutputSections.h
+++ b/contrib/llvm-project/lld/ELF/OutputSections.h
@@ -118,7 +118,8 @@ private:
int getPriority(StringRef s);
-std::vector<InputSection *> getInputSections(OutputSection* os);
+InputSection *getFirstInputSection(const OutputSection *os);
+std::vector<InputSection *> getInputSections(const OutputSection *os);
// All output sections that are handled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
diff --git a/contrib/llvm-project/lld/ELF/Relocations.cpp b/contrib/llvm-project/lld/ELF/Relocations.cpp
index c90990ac2752..751ded397768 100644
--- a/contrib/llvm-project/lld/ELF/Relocations.cpp
+++ b/contrib/llvm-project/lld/ELF/Relocations.cpp
@@ -62,9 +62,9 @@ using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
for (BaseCommand *base : script->sectionCommands)
if (auto *cmd = dyn_cast<SymbolAssignment>(base))
@@ -73,6 +73,15 @@ static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
return None;
}
+static std::string getDefinedLocation(const Symbol &sym) {
+ std::string msg = "\n>>> defined in ";
+ if (sym.file)
+ msg += toString(sym.file);
+ else if (Optional<std::string> loc = getLinkerScriptLocation(sym))
+ msg += *loc;
+ return msg;
+}
+
// Construct a message in the following format.
//
// >>> defined in /home/alice/src/foo.o
@@ -80,19 +89,30 @@ static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
// >>> /home/alice/src/bar.o:(.text+0x1)
static std::string getLocation(InputSectionBase &s, const Symbol &sym,
uint64_t off) {
- std::string msg = "\n>>> defined in ";
- if (sym.file)
- msg += toString(sym.file);
- else if (Optional<std::string> loc = getLinkerScriptLocation(sym))
- msg += *loc;
-
- msg += "\n>>> referenced by ";
+ std::string msg = getDefinedLocation(sym) + "\n>>> referenced by ";
std::string src = s.getSrcMsg(sym, off);
if (!src.empty())
msg += src + "\n>>> ";
return msg + s.getObjMsg(off);
}
+void elf::reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v,
+ int64_t min, uint64_t max) {
+ ErrorPlace errPlace = getErrorPlace(loc);
+ std::string hint;
+ if (rel.sym && !rel.sym->isLocal())
+ hint = "; references " + lld::toString(*rel.sym) +
+ getDefinedLocation(*rel.sym);
+
+ if (errPlace.isec && errPlace.isec->name.startswith(".debug"))
+ hint += "; consider recompiling with -fdebug-types-section to reduce size "
+ "of debug sections";
+
+ errorOrWarn(errPlace.loc + "relocation " + lld::toString(rel.type) +
+ " out of range: " + v.str() + " is not in [" + Twine(min).str() +
+ ", " + Twine(max).str() + "]" + hint);
+}
+
namespace {
// Build a bitmask with one bit set for each RelExpr.
//
@@ -177,9 +197,9 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
return 1;
}
- bool canRelax = config->emachine != EM_ARM &&
- config->emachine != EM_HEXAGON &&
- config->emachine != EM_RISCV;
+ bool toExecRelax = !config->shared && config->emachine != EM_ARM &&
+ config->emachine != EM_HEXAGON &&
+ config->emachine != EM_RISCV;
// If we are producing an executable and the symbol is non-preemptable, it
// must be defined and the code sequence can be relaxed to use Local-Exec.
@@ -197,7 +217,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(
expr)) {
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (canRelax && !config->shared) {
+ if (toExecRelax) {
c.relocations.push_back(
{target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
offset, addend, &sym});
@@ -218,7 +238,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (expr == R_DTPREL && !config->shared) {
+ if (expr == R_DTPREL && toExecRelax) {
c.relocations.push_back(
{target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
offset, addend, &sym});
@@ -240,7 +260,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
- if (!canRelax || config->shared) {
+ if (!toExecRelax) {
if (in.got->addDynTlsEntry(sym)) {
uint64_t off = in.got->getGlobalDynOffset(sym);
@@ -288,7 +308,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
// defined.
if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
R_TLSIE_HINT>(expr) &&
- canRelax && isLocalInExecutable) {
+ toExecRelax && isLocalInExecutable) {
c.relocations.push_back({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
return 1;
}
@@ -761,7 +781,7 @@ static const Symbol *getAlternativeSpelling(const Undefined &sym,
break;
// Substitute name[i].
- newName = name;
+ newName = std::string(name);
for (char c = '0'; c <= 'z'; ++c) {
newName[i] = c;
if (const Symbol *s = suggest(newName))
@@ -850,7 +870,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
if (msg.empty())
msg = "undefined " + visibility() + "symbol: " + toString(sym);
- const size_t maxUndefReferences = 10;
+ const size_t maxUndefReferences = 3;
size_t i = 0;
for (UndefinedDiag::Loc l : undef.locs) {
if (i >= maxUndefReferences)
@@ -881,8 +901,9 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
}
if (sym.getName().startswith("_ZTV"))
- msg += "\nthe vtable symbol may be undefined because the class is missing "
- "its key function (see https://lld.llvm.org/missingkeyfunction)";
+ msg +=
+ "\n>>> the vtable symbol may be undefined because the class is missing "
+ "its key function (see https://lld.llvm.org/missingkeyfunction)";
if (undef.isWarning)
warn(msg);
@@ -890,7 +911,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
error(msg);
}
-template <class ELFT> void reportUndefinedSymbols() {
+template <class ELFT> void elf::reportUndefinedSymbols() {
// Find the first "undefined symbol" diagnostic for each diagnostic, and
// collect all "referenced from" lines at the first diagnostic.
DenseMap<Symbol *, UndefinedDiag *> firstRef;
@@ -1273,17 +1294,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
if (expr == R_NONE)
return;
- // We can separate the small code model relocations into 2 categories:
- // 1) Those that access the compiler generated .toc sections.
- // 2) Those that access the linker allocated got entries.
- // lld allocates got entries to symbols on demand. Since we don't try to sort
- // the got entries in any way, we don't have to track which objects have
- // got-based small code model relocs. The .toc sections get placed after the
- // end of the linker allocated .got section and we do sort those so sections
- // addressed with small code model relocations come first.
- if (config->emachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(type))
- sec.file->ppc64SmallCodeModelTocRelocs = true;
-
if (sym.isGnuIFunc() && !config->zText && config->warnIfuncTextrel) {
warn("using ifunc symbols when text relocations are allowed may produce "
"a binary that will segfault, if the object file is linked with "
@@ -1297,6 +1307,25 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// Read an addend.
int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
+ if (config->emachine == EM_PPC64) {
+ // We can separate the small code model relocations into 2 categories:
+ // 1) Those that access the compiler generated .toc sections.
+ // 2) Those that access the linker allocated got entries.
+ // lld allocates got entries to symbols on demand. Since we don't try to
+ // sort the got entries in any way, we don't have to track which objects
+ // have got-based small code model relocs. The .toc sections get placed
+ // after the end of the linker allocated .got section and we do sort those
+ // so sections addressed with small code model relocations come first.
+ if (isPPC64SmallCodeModelTocReloc(type))
+ sec.file->ppc64SmallCodeModelTocRelocs = true;
+
+ // Record the TOC entry (.toc + addend) as not relaxable. See the comment in
+ // InputSectionBase::relocateAlloc().
+ if (type == R_PPC64_TOC16_LO && sym.isSection() && isa<Defined>(sym) &&
+ cast<Defined>(sym).section->name == ".toc")
+ ppc64noTocRelax.insert({&sym, addend});
+ }
+
// Relax relocations.
//
// If we know that a PLT entry will be resolved within the same ELF module, we
@@ -1313,6 +1342,12 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// stub type. It should be ignored if optimized to R_PC.
if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL)
addend &= ~0x8000;
+ // R_HEX_GD_PLT_B22_PCREL (call a@GDPLT) is transformed into
+ // call __tls_get_addr even if the symbol is non-preemptible.
+ if (!(config->emachine == EM_HEXAGON &&
+ (type == R_HEX_GD_PLT_B22_PCREL ||
+ type == R_HEX_GD_PLT_B22_PCREL_X ||
+ type == R_HEX_GD_PLT_B32_PCREL_X)))
expr = fromPlt(expr);
}
}
@@ -1479,7 +1514,7 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
});
}
-template <class ELFT> void scanRelocations(InputSectionBase &s) {
+template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
if (s.areRelocsRela)
scanRelocs<ELFT>(s, s.relas<ELFT>());
else
@@ -1960,14 +1995,49 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
return addressesChanged;
}
-template void scanRelocations<ELF32LE>(InputSectionBase &);
-template void scanRelocations<ELF32BE>(InputSectionBase &);
-template void scanRelocations<ELF64LE>(InputSectionBase &);
-template void scanRelocations<ELF64BE>(InputSectionBase &);
-template void reportUndefinedSymbols<ELF32LE>();
-template void reportUndefinedSymbols<ELF32BE>();
-template void reportUndefinedSymbols<ELF64LE>();
-template void reportUndefinedSymbols<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
+// The following aid in the conversion of call x@GDPLT to call __tls_get_addr
+// hexagonNeedsTLSSymbol scans for relocations would require a call to
+// __tls_get_addr.
+// hexagonTLSSymbolUpdate rebinds the relocation to __tls_get_addr.
+bool elf::hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections) {
+ bool needTlsSymbol = false;
+ forEachInputSectionDescription(
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ for (InputSection *isec : isd->sections)
+ for (Relocation &rel : isec->relocations)
+ if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
+ needTlsSymbol = true;
+ return;
+ }
+ });
+ return needTlsSymbol;
+}
+
+void elf::hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections) {
+ Symbol *sym = symtab->find("__tls_get_addr");
+ if (!sym)
+ return;
+ bool needEntry = true;
+ forEachInputSectionDescription(
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ for (InputSection *isec : isd->sections)
+ for (Relocation &rel : isec->relocations)
+ if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
+ if (needEntry) {
+ addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel,
+ *sym);
+ needEntry = false;
+ }
+ rel.sym = sym;
+ }
+ });
+}
+
+template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
+template void elf::scanRelocations<ELF32BE>(InputSectionBase &);
+template void elf::scanRelocations<ELF64LE>(InputSectionBase &);
+template void elf::scanRelocations<ELF64BE>(InputSectionBase &);
+template void elf::reportUndefinedSymbols<ELF32LE>();
+template void elf::reportUndefinedSymbols<ELF32BE>();
+template void elf::reportUndefinedSymbols<ELF64LE>();
+template void elf::reportUndefinedSymbols<ELF64BE>();
diff --git a/contrib/llvm-project/lld/ELF/Relocations.h b/contrib/llvm-project/lld/ELF/Relocations.h
index bfec1e628851..ec59c63410d0 100644
--- a/contrib/llvm-project/lld/ELF/Relocations.h
+++ b/contrib/llvm-project/lld/ELF/Relocations.h
@@ -24,6 +24,7 @@ class SectionBase;
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
using RelType = uint32_t;
+using JumpModType = uint32_t;
// List of target-independent relocation types. Relocations read
// from files are converted to these types so that the main code
@@ -80,6 +81,7 @@ enum RelExpr {
R_AARCH64_PAGE_PC,
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
R_AARCH64_TLSDESC_PAGE,
+ R_ARM_PCA,
R_ARM_SBREL,
R_MIPS_GOTREL,
R_MIPS_GOT_GP,
@@ -107,6 +109,15 @@ struct Relocation {
Symbol *sym;
};
+// Manipulate jump instructions with these modifiers. These are used to relax
+// jump instruction opcodes at basic block boundaries and are particularly
+// useful when basic block sections are enabled.
+struct JumpInstrMod {
+ JumpModType original;
+ uint64_t offset;
+ unsigned size;
+};
+
// This function writes undefined symbol diagnostics to an internal buffer.
// Call reportUndefinedSymbols() after calling scanRelocations() to emit
// the diagnostics.
@@ -114,6 +125,9 @@ template <class ELFT> void scanRelocations(InputSectionBase &);
template <class ELFT> void reportUndefinedSymbols();
+void hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections);
+bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);
+
class ThunkSection;
class Thunk;
struct InputSectionDescription;
diff --git a/contrib/llvm-project/lld/ELF/ScriptLexer.cpp b/contrib/llvm-project/lld/ELF/ScriptLexer.cpp
index 1fed3d06227e..9ac8447eef0e 100644
--- a/contrib/llvm-project/lld/ELF/ScriptLexer.cpp
+++ b/contrib/llvm-project/lld/ELF/ScriptLexer.cpp
@@ -36,9 +36,9 @@
#include "llvm/ADT/Twine.h"
using namespace llvm;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
// Returns a whole line containing the current token.
StringRef ScriptLexer::getLine() {
StringRef s = getCurrentMB().getBuffer();
@@ -66,7 +66,7 @@ size_t ScriptLexer::getColumnNumber() {
}
std::string ScriptLexer::getCurrentLocation() {
- std::string filename = getCurrentMB().getBufferIdentifier();
+ std::string filename = std::string(getCurrentMB().getBufferIdentifier());
return (filename + ":" + Twine(getLineNumber())).str();
}
@@ -189,7 +189,7 @@ static std::vector<StringRef> tokenizeExpr(StringRef s) {
break;
}
- // Get a token before the opreator.
+ // Get a token before the operator.
if (e != 0)
ret.push_back(s.substr(0, e));
@@ -302,6 +302,3 @@ MemoryBufferRef ScriptLexer::getCurrentMB() {
return mb;
llvm_unreachable("getCurrentMB: failed to find a token");
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/ScriptLexer.h b/contrib/llvm-project/lld/ELF/ScriptLexer.h
index 98e4cac95a73..306d428e98fe 100644
--- a/contrib/llvm-project/lld/ELF/ScriptLexer.h
+++ b/contrib/llvm-project/lld/ELF/ScriptLexer.h
@@ -40,13 +40,14 @@ public:
bool inExpr = false;
size_t pos = 0;
+protected:
+ MemoryBufferRef getCurrentMB();
+
private:
void maybeSplitExpr();
StringRef getLine();
size_t getLineNumber();
size_t getColumnNumber();
-
- MemoryBufferRef getCurrentMB();
};
} // namespace elf
diff --git a/contrib/llvm-project/lld/ELF/ScriptParser.cpp b/contrib/llvm-project/lld/ELF/ScriptParser.cpp
index 80ec8b655b04..fea6b7a274e7 100644
--- a/contrib/llvm-project/lld/ELF/ScriptParser.cpp
+++ b/contrib/llvm-project/lld/ELF/ScriptParser.cpp
@@ -30,6 +30,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/ScopedPrinter.h"
#include <cassert>
#include <limits>
#include <vector>
@@ -37,9 +38,9 @@
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
class ScriptParser final : ScriptLexer {
public:
@@ -91,10 +92,13 @@ private:
OutputSection *readOutputSectionDescription(StringRef outSec);
std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
+ std::pair<uint64_t, uint64_t> readInputSectionFlags();
InputSectionDescription *readInputSectionDescription(StringRef tok);
StringMatcher readFilePatterns();
std::vector<SectionPattern> readInputSectionsList();
- InputSectionDescription *readInputSectionRules(StringRef filePattern);
+ InputSectionDescription *readInputSectionRules(StringRef filePattern,
+ uint64_t withFlags,
+ uint64_t withoutFlags);
unsigned readPhdrType();
SortSectionPolicy readSortKind();
SymbolAssignment *readProvideHidden(bool provide, bool hidden);
@@ -104,7 +108,7 @@ private:
Expr readConstant();
Expr getPageSize();
- uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
+ Expr readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
Expr combine(StringRef op, Expr l, Expr r);
@@ -171,7 +175,6 @@ static ExprValue bitOr(ExprValue a, ExprValue b) {
}
void ScriptParser::readDynamicList() {
- config->hasDynamicList = true;
expect("{");
std::vector<SymbolVersion> locals;
std::vector<SymbolVersion> globals;
@@ -286,22 +289,40 @@ void ScriptParser::addFile(StringRef s) {
}
if (s.startswith("/")) {
+ // Case 1: s is an absolute path. Just open it.
driver->addFile(s, /*withLOption=*/false);
} else if (s.startswith("=")) {
+ // Case 2: relative to the sysroot.
if (config->sysroot.empty())
driver->addFile(s.substr(1), /*withLOption=*/false);
else
driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)),
/*withLOption=*/false);
} else if (s.startswith("-l")) {
+ // Case 3: search in the list of library paths.
driver->addLibrary(s.substr(2));
- } else if (sys::fs::exists(s)) {
- driver->addFile(s, /*withLOption=*/false);
} else {
- if (Optional<std::string> path = findFromSearchPaths(s))
- driver->addFile(saver.save(*path), /*withLOption=*/true);
- else
- setError("unable to find " + s);
+ // Case 4: s is a relative path. Search in the directory of the script file.
+ std::string filename = std::string(getCurrentMB().getBufferIdentifier());
+ StringRef directory = sys::path::parent_path(filename);
+ if (!directory.empty()) {
+ SmallString<0> path(directory);
+ sys::path::append(path, s);
+ if (sys::fs::exists(path)) {
+ driver->addFile(path, /*withLOption=*/false);
+ return;
+ }
+ }
+ // Then search in the current working directory.
+ if (sys::fs::exists(s)) {
+ driver->addFile(s, /*withLOption=*/false);
+ } else {
+ // Finally, search in the list of library paths.
+ if (Optional<std::string> path = findFromSearchPaths(s))
+ driver->addFile(saver.save(*path), /*withLOption=*/true);
+ else
+ setError("unable to find " + s);
+ }
}
}
@@ -400,6 +421,7 @@ static std::pair<ELFKind, uint16_t> parseBfdName(StringRef s) {
.Case("elf64-tradlittlemips", {ELF64LEKind, EM_MIPS})
.Case("elf32-littleriscv", {ELF32LEKind, EM_RISCV})
.Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV})
+ .Case("elf64-sparc", {ELF64BEKind, EM_SPARCV9})
.Default({ELFNoneKind, EM_NONE});
}
@@ -408,14 +430,14 @@ static std::pair<ELFKind, uint16_t> parseBfdName(StringRef s) {
void ScriptParser::readOutputFormat() {
expect("(");
- StringRef name = unquote(next());
- StringRef s = name;
+ config->bfdname = unquote(next());
+ StringRef s = config->bfdname;
if (s.consume_back("-freebsd"))
config->osabi = ELFOSABI_FREEBSD;
std::tie(config->ekind, config->emachine) = parseBfdName(s);
if (config->emachine == EM_NONE)
- setError("unknown output format name: " + name);
+ setError("unknown output format name: " + config->bfdname);
if (s == "elf32-ntradlittlemips" || s == "elf32-ntradbigmips")
config->mipsN32Abi = true;
@@ -519,13 +541,6 @@ std::vector<BaseCommand *> ScriptParser::readOverlay() {
}
void ScriptParser::readSections() {
- script->hasSectionsCommand = true;
-
- // -no-rosegment is used to avoid placing read only non-executable sections in
- // their own segment. We do the same if SECTIONS command is present in linker
- // script. See comment for computeFlags().
- config->singleRoRx = true;
-
expect("{");
std::vector<BaseCommand *> v;
while (!errorCount() && !consume("}")) {
@@ -544,22 +559,23 @@ void ScriptParser::readSections() {
else
v.push_back(readOutputSectionDescription(tok));
}
+ script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
+ v.end());
- if (!atEOF() && consume("INSERT")) {
- std::vector<BaseCommand *> *dest = nullptr;
- if (consume("AFTER"))
- dest = &script->insertAfterCommands[next()];
- else if (consume("BEFORE"))
- dest = &script->insertBeforeCommands[next()];
- else
- setError("expected AFTER/BEFORE, but got '" + next() + "'");
- if (dest)
- dest->insert(dest->end(), v.begin(), v.end());
+ if (atEOF() || !consume("INSERT")) {
+ script->hasSectionsCommand = true;
return;
}
- script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
- v.end());
+ bool isAfter = false;
+ if (consume("AFTER"))
+ isAfter = true;
+ else if (!consume("BEFORE"))
+ setError("expected AFTER/BEFORE, but got '" + next() + "'");
+ StringRef where = next();
+ for (BaseCommand *cmd : v)
+ if (auto *os = dyn_cast<OutputSection>(cmd))
+ script->insertCommands.push_back({os, isAfter, where});
}
void ScriptParser::readTarget() {
@@ -593,10 +609,11 @@ static int precedence(StringRef op) {
}
StringMatcher ScriptParser::readFilePatterns() {
- std::vector<StringRef> v;
+ StringMatcher Matcher;
+
while (!errorCount() && !consume(")"))
- v.push_back(next());
- return StringMatcher(v);
+ Matcher.addPattern(SingleStringMatcher(next()));
+ return Matcher;
}
SortSectionPolicy ScriptParser::readSortKind() {
@@ -633,12 +650,12 @@ std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
excludeFilePat = readFilePatterns();
}
- std::vector<StringRef> v;
+ StringMatcher SectionMatcher;
while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE")
- v.push_back(unquote(next()));
+ SectionMatcher.addPattern(unquote(next()));
- if (!v.empty())
- ret.push_back({std::move(excludeFilePat), StringMatcher(v)});
+ if (!SectionMatcher.empty())
+ ret.push_back({std::move(excludeFilePat), std::move(SectionMatcher)});
else
setError("section pattern is expected");
}
@@ -657,8 +674,10 @@ std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
//
// <section-list> is parsed by readInputSectionsList().
InputSectionDescription *
-ScriptParser::readInputSectionRules(StringRef filePattern) {
- auto *cmd = make<InputSectionDescription>(filePattern);
+ScriptParser::readInputSectionRules(StringRef filePattern, uint64_t withFlags,
+ uint64_t withoutFlags) {
+ auto *cmd =
+ make<InputSectionDescription>(filePattern, withFlags, withoutFlags);
expect("(");
while (!errorCount() && !consume(")")) {
@@ -694,15 +713,23 @@ InputSectionDescription *
ScriptParser::readInputSectionDescription(StringRef tok) {
// Input section wildcard can be surrounded by KEEP.
// https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep
+ uint64_t withFlags = 0;
+ uint64_t withoutFlags = 0;
if (tok == "KEEP") {
expect("(");
- StringRef filePattern = next();
- InputSectionDescription *cmd = readInputSectionRules(filePattern);
+ if (consume("INPUT_SECTION_FLAGS"))
+ std::tie(withFlags, withoutFlags) = readInputSectionFlags();
+ InputSectionDescription *cmd =
+ readInputSectionRules(next(), withFlags, withoutFlags);
expect(")");
script->keptSections.push_back(cmd);
return cmd;
}
- return readInputSectionRules(tok);
+ if (tok == "INPUT_SECTION_FLAGS") {
+ std::tie(withFlags, withoutFlags) = readInputSectionFlags();
+ tok = next();
+ }
+ return readInputSectionRules(tok, withFlags, withoutFlags);
}
void ScriptParser::readSort() {
@@ -782,9 +809,14 @@ OutputSection *ScriptParser::readOverlaySectionDescription() {
script->createOutputSection(next(), getCurrentLocation());
cmd->inOverlay = true;
expect("{");
- while (!errorCount() && !consume("}"))
- cmd->sectionCommands.push_back(readInputSectionRules(next()));
- cmd->phdrs = readOutputSectionPhdrs();
+ while (!errorCount() && !consume("}")) {
+ uint64_t withFlags = 0;
+ uint64_t withoutFlags = 0;
+ if (consume("INPUT_SECTION_FLAGS"))
+ std::tie(withFlags, withoutFlags) = readInputSectionFlags();
+ cmd->sectionCommands.push_back(
+ readInputSectionRules(next(), withFlags, withoutFlags));
+ }
return cmd;
}
@@ -829,9 +861,9 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
// We handle the FILL command as an alias for =fillexp section attribute,
// which is different from what GNU linkers do.
// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
- expect("(");
+ if (peek() != "(")
+ setError("( expected, but got " + peek());
cmd->filler = readFill();
- expect(")");
} else if (tok == "SORT") {
readSort();
} else if (tok == "INCLUDE") {
@@ -842,18 +874,21 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
// We have a file name and no input sections description. It is not a
// commonly used syntax, but still acceptable. In that case, all sections
// from the file will be included.
+ // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not
+ // handle this case here as it will already have been matched by the
+ // case above.
auto *isd = make<InputSectionDescription>(tok);
- isd->sectionPatterns.push_back({{}, StringMatcher({"*"})});
+ isd->sectionPatterns.push_back({{}, StringMatcher("*")});
cmd->sectionCommands.push_back(isd);
}
}
if (consume(">"))
- cmd->memoryRegionName = next();
+ cmd->memoryRegionName = std::string(next());
if (consume("AT")) {
expect(">");
- cmd->lmaRegionName = next();
+ cmd->lmaRegionName = std::string(next());
}
if (cmd->lmaExpr && !cmd->lmaRegionName.empty())
@@ -883,8 +918,11 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
// When reading a hexstring, ld.bfd handles it as a blob of arbitrary
// size, while ld.gold always handles it as a 32-bit big-endian number.
// We are compatible with ld.gold because it's easier to implement.
+// Also, we require that expressions with operators must be wrapped into
+// round brackets. We did it to resolve the ambiguity when parsing scripts like:
+// SECTIONS { .foo : { ... } =120+3 /DISCARD/ : { ... } }
std::array<uint8_t, 4> ScriptParser::readFill() {
- uint64_t value = readExpr()().val;
+ uint64_t value = readPrimary()().val;
if (value > UINT32_MAX)
setError("filler expression result does not fit 32-bit: 0x" +
Twine::utohexstr(value));
@@ -1103,6 +1141,63 @@ ByteCommand *ScriptParser::readByteCommand(StringRef tok) {
return make<ByteCommand>(e, size, commandString);
}
+static llvm::Optional<uint64_t> parseFlag(StringRef tok) {
+ if (llvm::Optional<uint64_t> asInt = parseInt(tok))
+ return asInt;
+#define CASE_ENT(enum) #enum, ELF::enum
+ return StringSwitch<llvm::Optional<uint64_t>>(tok)
+ .Case(CASE_ENT(SHF_WRITE))
+ .Case(CASE_ENT(SHF_ALLOC))
+ .Case(CASE_ENT(SHF_EXECINSTR))
+ .Case(CASE_ENT(SHF_MERGE))
+ .Case(CASE_ENT(SHF_STRINGS))
+ .Case(CASE_ENT(SHF_INFO_LINK))
+ .Case(CASE_ENT(SHF_LINK_ORDER))
+ .Case(CASE_ENT(SHF_OS_NONCONFORMING))
+ .Case(CASE_ENT(SHF_GROUP))
+ .Case(CASE_ENT(SHF_TLS))
+ .Case(CASE_ENT(SHF_COMPRESSED))
+ .Case(CASE_ENT(SHF_EXCLUDE))
+ .Case(CASE_ENT(SHF_ARM_PURECODE))
+ .Default(None);
+#undef CASE_ENT
+}
+
+// Reads the '(' <flags> ')' list of section flags in
+// INPUT_SECTION_FLAGS '(' <flags> ')' in the
+// following form:
+// <flags> ::= <flag>
+// | <flags> & flag
+// <flag> ::= Recognized Flag Name, or Integer value of flag.
+// If the first character of <flag> is a ! then this means without flag,
+// otherwise with flag.
+// Example: SHF_EXECINSTR & !SHF_WRITE means with flag SHF_EXECINSTR and
+// without flag SHF_WRITE.
+std::pair<uint64_t, uint64_t> ScriptParser::readInputSectionFlags() {
+ uint64_t withFlags = 0;
+ uint64_t withoutFlags = 0;
+ expect("(");
+ while (!errorCount()) {
+ StringRef tok = unquote(next());
+ bool without = tok.consume_front("!");
+ if (llvm::Optional<uint64_t> flag = parseFlag(tok)) {
+ if (without)
+ withoutFlags |= *flag;
+ else
+ withFlags |= *flag;
+ } else {
+ setError("unrecognised flag: " + tok);
+ }
+ if (consume(")"))
+ break;
+ if (!consume("&")) {
+ next();
+ setError("expected & or )");
+ }
+ }
+ return std::make_pair(withFlags, withoutFlags);
+}
+
StringRef ScriptParser::readParenLiteral() {
expect("(");
bool orig = inExpr;
@@ -1223,7 +1318,7 @@ Expr ScriptParser::readPrimary() {
setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return script->memoryRegions[name]->length; };
+ return script->memoryRegions[name]->length;
}
if (tok == "LOADADDR") {
StringRef name = readParenLiteral();
@@ -1250,7 +1345,7 @@ Expr ScriptParser::readPrimary() {
setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return script->memoryRegions[name]->origin; };
+ return script->memoryRegions[name]->origin;
}
if (tok == "SEGMENT_START") {
expect("(");
@@ -1269,7 +1364,7 @@ Expr ScriptParser::readPrimary() {
return [=] { return cmd->size; };
}
if (tok == "SIZEOF_HEADERS")
- return [=] { return getHeaderSize(); };
+ return [=] { return elf::getHeaderSize(); };
// Tok is the dot.
if (tok == ".")
@@ -1375,12 +1470,11 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) {
// as a parent. This version hierarchy is, probably against your
// instinct, purely for hint; the runtime doesn't care about it
// at all. In LLD, we simply ignore it.
- if (peek() != ";")
- skip();
- expect(";");
+ if (next() != ";")
+ expect(";");
}
-static bool hasWildcard(StringRef s) {
+bool elf::hasWildcard(StringRef s) {
return s.find_first_of("?*[") != StringRef::npos;
}
@@ -1441,14 +1535,14 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
return ret;
}
-uint64_t ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
- StringRef s3) {
+Expr ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
+ StringRef s3) {
if (!consume(s1) && !consume(s2) && !consume(s3)) {
setError("expected one of: " + s1 + ", " + s2 + ", or " + s3);
- return 0;
+ return [] { return 0; };
}
expect("=");
- return readExpr()().getValue();
+ return readExpr();
}
// Parse the MEMORY command as specified in:
@@ -1472,9 +1566,9 @@ void ScriptParser::readMemory() {
}
expect(":");
- uint64_t origin = readMemoryAssignment("ORIGIN", "org", "o");
+ Expr origin = readMemoryAssignment("ORIGIN", "org", "o");
expect(",");
- uint64_t length = readMemoryAssignment("LENGTH", "len", "l");
+ Expr length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
@@ -1512,19 +1606,18 @@ std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
return {flags, negFlags};
}
-void readLinkerScript(MemoryBufferRef mb) {
+void elf::readLinkerScript(MemoryBufferRef mb) {
ScriptParser(mb).readLinkerScript();
}
-void readVersionScript(MemoryBufferRef mb) {
+void elf::readVersionScript(MemoryBufferRef mb) {
ScriptParser(mb).readVersionScript();
}
-void readDynamicList(MemoryBufferRef mb) { ScriptParser(mb).readDynamicList(); }
+void elf::readDynamicList(MemoryBufferRef mb) {
+ ScriptParser(mb).readDynamicList();
+}
-void readDefsym(StringRef name, MemoryBufferRef mb) {
+void elf::readDefsym(StringRef name, MemoryBufferRef mb) {
ScriptParser(mb).readDefsym(name);
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/ScriptParser.h b/contrib/llvm-project/lld/ELF/ScriptParser.h
index c953fb302b9a..eed1958647f8 100644
--- a/contrib/llvm-project/lld/ELF/ScriptParser.h
+++ b/contrib/llvm-project/lld/ELF/ScriptParser.h
@@ -27,6 +27,8 @@ void readDynamicList(MemoryBufferRef mb);
// Parses the defsym expression.
void readDefsym(StringRef name, MemoryBufferRef mb);
+bool hasWildcard(StringRef s);
+
} // namespace elf
} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/SymbolTable.cpp b/contrib/llvm-project/lld/ELF/SymbolTable.cpp
index f7a8a99cf8f9..afc8b05f8767 100644
--- a/contrib/llvm-project/lld/ELF/SymbolTable.cpp
+++ b/contrib/llvm-project/lld/ELF/SymbolTable.cpp
@@ -26,10 +26,10 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
-SymbolTable *symtab;
+SymbolTable *elf::symtab;
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
// Swap symbols as instructed by -wrap.
@@ -40,12 +40,18 @@ void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
idx2 = idx1;
idx1 = idx3;
- // Now renaming is complete. No one refers Real symbol. We could leave
- // Real as-is, but if Real is written to the symbol table, that may
- // contain irrelevant values. So, we copy all values from Sym to Real.
- StringRef s = real->getName();
+ if (real->exportDynamic)
+ sym->exportDynamic = true;
+
+ // Now renaming is complete, and no one refers to real. We drop real from
+ // .symtab and .dynsym. If real is undefined, it is important that we don't
+ // leave it in .dynsym, because otherwise it might lead to an undefined symbol
+ // error in a subsequent link. If real is defined, we could emit real as an
+ // alias for sym, but that could degrade the user experience of some tools
+ // that can print out only one symbol for each location: sym is a preferred
+ // name than real, but they might print out real instead.
memcpy(real, sym, sizeof(SymbolUnion));
- real->setName(s);
+ real->isUsedInRegularObj = false;
}
// Find an existing symbol or create a new one.
@@ -88,7 +94,7 @@ Symbol *SymbolTable::insert(StringRef name) {
}
Symbol *SymbolTable::addSymbol(const Symbol &newSym) {
- Symbol *sym = symtab->insert(newSym.getName());
+ Symbol *sym = insert(newSym.getName());
sym->resolve(newSym);
return sym;
}
@@ -103,6 +109,13 @@ Symbol *SymbolTable::find(StringRef name) {
return sym;
}
+// A version script/dynamic list is only meaningful for a Defined symbol.
+// A CommonSymbol will be converted to a Defined in replaceCommonSymbols().
+// A lazy symbol may be made Defined if an LTO libcall fetches it.
+static bool canBeVersioned(const Symbol &sym) {
+ return sym.isDefined() || sym.isCommon() || sym.isLazy();
+}
+
// Initialize demangledSyms with a map from demangled symbols to symbol
// objects. Used to handle "extern C++" directive in version scripts.
//
@@ -119,11 +132,9 @@ Symbol *SymbolTable::find(StringRef name) {
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
if (!demangledSyms) {
demangledSyms.emplace();
- for (Symbol *sym : symVector) {
- if (!sym->isDefined() && !sym->isCommon())
- continue;
- (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
- }
+ for (Symbol *sym : symVector)
+ if (canBeVersioned(*sym))
+ (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
}
return *demangledSyms;
}
@@ -131,15 +142,15 @@ StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
if (ver.isExternCpp)
return getDemangledSyms().lookup(ver.name);
- if (Symbol *b = find(ver.name))
- if (b->isDefined() || b->isCommon())
- return {b};
+ if (Symbol *sym = find(ver.name))
+ if (canBeVersioned(*sym))
+ return {sym};
return {};
}
std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
std::vector<Symbol *> res;
- StringMatcher m(ver.name);
+ SingleStringMatcher m(ver.name);
if (ver.isExternCpp) {
for (auto &p : getDemangledSyms())
@@ -149,7 +160,7 @@ std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
}
for (Symbol *sym : symVector)
- if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName()))
+ if (canBeVersioned(*sym) && m.match(sym->getName()))
res.push_back(sym);
return res;
}
@@ -264,6 +275,3 @@ void SymbolTable::scanVersionScript() {
// --dynamic-list.
handleDynamicList();
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Symbols.cpp b/contrib/llvm-project/lld/ELF/Symbols.cpp
index 0dcf34722d33..8f2f55418df5 100644
--- a/contrib/llvm-project/lld/ELF/Symbols.cpp
+++ b/contrib/llvm-project/lld/ELF/Symbols.cpp
@@ -16,27 +16,39 @@
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include <cstring>
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
// Returns a symbol for an error message.
static std::string demangle(StringRef symName) {
if (elf::config->demangle)
return demangleItanium(symName);
- return symName;
+ return std::string(symName);
}
-std::string toString(const elf::Symbol &b) { return demangle(b.getName()); }
-std::string toELFString(const Archive::Symbol &b) {
+std::string lld::toString(const elf::Symbol &sym) {
+ StringRef name = sym.getName();
+ std::string ret = demangle(name);
+
+ // If sym has a non-default version, its name may have been truncated at '@'
+ // by Symbol::parseSymbolVersion(). Add the trailing part. This check is safe
+ // because every symbol name ends with '\0'.
+ if (name.data()[name.size()] == '@')
+ ret += name.data() + name.size();
+ return ret;
+}
+
+std::string lld::toELFString(const Archive::Symbol &b) {
return demangle(b.getName());
}
-namespace elf {
Defined *ElfSym::bss;
Defined *ElfSym::etext1;
Defined *ElfSym::etext2;
@@ -52,6 +64,7 @@ Defined *ElfSym::relaIpltStart;
Defined *ElfSym::relaIpltEnd;
Defined *ElfSym::riscvGlobalPointer;
Defined *ElfSym::tlsModuleBase;
+DenseMap<const Symbol *, const InputFile *> elf::backwardReferences;
static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
switch (sym.kind()) {
@@ -99,7 +112,7 @@ static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
// MIPS relocatable files can mix regular and microMIPS code.
// Linker needs to distinguish such code. To do so microMIPS
// symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other`
- // field. Unfortunately, the `MIPS::relocateOne()` method has
+ // field. Unfortunately, the `MIPS::relocate()` method has
// a symbol value only. To pass type of the symbol (regular/microMIPS)
// to that routine as well as other places where we write
// a symbol value as-is (.dynamic section, `Elf_Ehdr::e_entry`
@@ -265,7 +278,7 @@ uint8_t Symbol::computeBinding() const {
if (config->relocatable)
return binding;
if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
- versionId == VER_NDX_LOCAL)
+ (versionId == VER_NDX_LOCAL && isDefined()))
return STB_LOCAL;
if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
@@ -288,7 +301,7 @@ bool Symbol::includeInDynsym() const {
}
// Print out a log message for --trace-symbol.
-void printTraceSymbol(const Symbol *sym) {
+void elf::printTraceSymbol(const Symbol *sym) {
std::string s;
if (sym->isUndefined())
s = ": reference to ";
@@ -304,7 +317,7 @@ void printTraceSymbol(const Symbol *sym) {
message(toString(sym->file) + s + sym->getName());
}
-void maybeWarnUnorderableSymbol(const Symbol *sym) {
+void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
if (!config->warnSymbolOrdering)
return;
@@ -336,7 +349,7 @@ void maybeWarnUnorderableSymbol(const Symbol *sym) {
// Returns true if a symbol can be replaced at load-time by a symbol
// with the same name defined in other ELF executable or DSO.
-bool computeIsPreemptible(const Symbol &sym) {
+bool elf::computeIsPreemptible(const Symbol &sym) {
assert(!sym.isLocal());
// Only symbols with default visibility that appear in dynsym can be
@@ -352,16 +365,22 @@ bool computeIsPreemptible(const Symbol &sym) {
if (!config->shared)
return false;
- // If the dynamic list is present, it specifies preemptable symbols in a DSO.
- if (config->hasDynamicList)
+ // If -Bsymbolic or --dynamic-list is specified, or -Bsymbolic-functions is
+ // specified and the symbol is STT_FUNC, the symbol is preemptible iff it is
+ // in the dynamic list.
+ if (config->symbolic || (config->bsymbolicFunctions && sym.isFunc()))
return sym.inDynamicList;
-
- // -Bsymbolic means that definitions are not preempted.
- if (config->bsymbolic || (config->bsymbolicFunctions && sym.isFunc()))
- return false;
return true;
}
+void elf::reportBackrefs() {
+ for (auto &it : backwardReferences) {
+ const Symbol &sym = *it.first;
+ warn("backward reference detected: " + sym.getName() + " in " +
+ toString(it.second) + " refers to " + toString(sym.file));
+ }
+}
+
static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
if (va == STV_DEFAULT)
return vb;
@@ -494,13 +513,28 @@ void Symbol::resolveUndefined(const Undefined &other) {
// group assignment rule simulates the traditional linker's semantics.
bool backref = config->warnBackrefs && other.file &&
file->groupId < other.file->groupId;
+ if (backref) {
+ // Some libraries have known problems and can cause noise. Filter them out
+ // with --warn-backrefs-exclude=.
+ StringRef name =
+ !file->archiveName.empty() ? file->archiveName : file->getName();
+ for (const llvm::GlobPattern &pat : config->warnBackrefsExclude)
+ if (pat.match(name)) {
+ backref = false;
+ break;
+ }
+ }
fetch();
// We don't report backward references to weak symbols as they can be
// overridden later.
+ //
+ // A traditional linker does not error for -ldef1 -lref -ldef2 (linking
+ // sandwich), where def2 may or may not be the same as def1. We don't want
+ // to warn for this case, so dismiss the warning if we see a subsequent lazy
+ // definition.
if (backref && !isWeak())
- warn("backward reference detected: " + other.getName() + " in " +
- toString(other.file) + " refers to " + toString(file));
+ backwardReferences.try_emplace(this, other.file);
return;
}
@@ -514,7 +548,6 @@ void Symbol::resolveUndefined(const Undefined &other) {
// reference is weak.
if (other.binding != STB_WEAK || !referenced)
binding = other.binding;
- referenced = true;
}
}
@@ -658,8 +691,12 @@ void Symbol::resolveDefined(const Defined &other) {
}
template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
- if (!isUndefined())
+ if (!isUndefined()) {
+ // See the comment in resolveUndefined().
+ if (isDefined())
+ backwardReferences.erase(this);
return;
+ }
// An undefined weak will not fetch archive members. See comment on Lazy in
// Symbols.h for the details.
@@ -687,9 +724,6 @@ void Symbol::resolveShared(const SharedSymbol &other) {
uint8_t bind = binding;
replace(other);
binding = bind;
- referenced = true;
- }
+ } else if (traced)
+ printTraceSymbol(&other);
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Symbols.h b/contrib/llvm-project/lld/ELF/Symbols.h
index ac606198afd8..b69d263153d2 100644
--- a/contrib/llvm-project/lld/ELF/Symbols.h
+++ b/contrib/llvm-project/lld/ELF/Symbols.h
@@ -17,10 +17,12 @@
#include "InputSection.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Strings.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
namespace lld {
+// Returns a string representation for a symbol for diagnostics.
std::string toString(const elf::Symbol &);
// There are two different ways to convert an Archive::Symbol to a string:
@@ -256,6 +258,9 @@ public:
uint8_t isPreemptible : 1;
// True if an undefined or shared symbol is used from a live section.
+ //
+ // NOTE: In Writer.cpp the field is used to mark local defined symbols
+ // which are referenced by relocations when -r or --emit-relocs is given.
uint8_t used : 1;
// True if a call to this symbol needs to be followed by a restore of the
@@ -515,13 +520,16 @@ size_t Symbol::getSymbolSize() const {
void Symbol::replace(const Symbol &newSym) {
using llvm::ELF::STT_TLS;
- // Symbols representing thread-local variables must be referenced by
- // TLS-aware relocations, and non-TLS symbols must be reference by
- // non-TLS relocations, so there's a clear distinction between TLS
- // and non-TLS symbols. It is an error if the same symbol is defined
- // as a TLS symbol in one file and as a non-TLS symbol in other file.
- if (symbolKind != PlaceholderKind && !isLazy() && !newSym.isLazy() &&
- (type == STT_TLS) != (newSym.type == STT_TLS))
+ // st_value of STT_TLS represents the assigned offset, not the actual address
+ // which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can only be
+ // referenced by special TLS relocations. It is usually an error if a STT_TLS
+ // symbol is replaced by a non-STT_TLS symbol, vice versa. There are two
+ // exceptions: (a) a STT_NOTYPE lazy/undefined symbol can be replaced by a
+ // STT_TLS symbol, (b) a STT_TLS undefined symbol can be replaced by a
+ // STT_NOTYPE lazy symbol.
+ if (symbolKind != PlaceholderKind && !newSym.isLazy() &&
+ (type == STT_TLS) != (newSym.type == STT_TLS) &&
+ type != llvm::ELF::STT_NOTYPE)
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
toString(newSym.file) + "\n>>> defined in " + toString(file));
@@ -555,6 +563,11 @@ void Symbol::replace(const Symbol &newSym) {
void maybeWarnUnorderableSymbol(const Symbol *sym);
bool computeIsPreemptible(const Symbol &sym);
+void reportBackrefs();
+
+// A mapping from a symbol to an InputFile referencing it backward. Used by
+// --warn-backrefs.
+extern llvm::DenseMap<const Symbol *, const InputFile *> backwardReferences;
} // namespace elf
} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/SyntheticSections.cpp b/contrib/llvm-project/lld/ELF/SyntheticSections.cpp
index 67709916cd32..731b9f658060 100644
--- a/contrib/llvm-project/lld/ELF/SyntheticSections.cpp
+++ b/contrib/llvm-project/lld/ELF/SyntheticSections.cpp
@@ -22,10 +22,10 @@
#include "Symbols.h"
#include "Target.h"
#include "Writer.h"
+#include "lld/Common/DWARF.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/StringExtras.h"
@@ -36,6 +36,8 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MD5.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/TimeProfiler.h"
#include <cstdlib>
#include <thread>
@@ -44,13 +46,13 @@ using namespace llvm::dwarf;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
+using namespace lld;
+using namespace lld::elf;
using llvm::support::endian::read32le;
using llvm::support::endian::write32le;
using llvm::support::endian::write64le;
-namespace lld {
-namespace elf {
constexpr size_t MergeNoTailSection::numShards;
static uint64_t readUint(uint8_t *buf) {
@@ -81,7 +83,7 @@ static ArrayRef<uint8_t> getVersion() {
// With this feature, you can identify LLD-generated binaries easily
// by "readelf --string-dump .comment <file>".
// The returned object is a mergeable string section.
-MergeInputSection *createCommentSection() {
+MergeInputSection *elf::createCommentSection() {
return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
getVersion(), ".comment");
}
@@ -137,7 +139,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
flags.ases |= s->ases;
flags.flags1 |= s->flags1;
flags.flags2 |= s->flags2;
- flags.fp_abi = getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
+ flags.fp_abi = elf::getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
};
if (create)
@@ -251,7 +253,7 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
return make<MipsReginfoSection<ELFT>>(reginfo);
}
-InputSection *createInterpSection() {
+InputSection *elf::createInterpSection() {
// StringSaver guarantees that the returned string ends with '\0'.
StringRef s = saver.save(config->dynamicLinker);
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
@@ -260,8 +262,8 @@ InputSection *createInterpSection() {
".interp");
}
-Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
- uint64_t size, InputSectionBase &section) {
+Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
+ uint64_t size, InputSectionBase &section) {
auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
value, size, &section);
if (in.symTab)
@@ -1273,7 +1275,7 @@ static uint64_t addPltRelSz() {
// Add remaining entries to complete .dynamic contents.
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
- Partition &part = getPartition();
+ elf::Partition &part = getPartition();
bool isMain = part.name.empty();
for (StringRef s : config->filterList)
@@ -1402,7 +1404,7 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
if (config->emachine == EM_AARCH64) {
if (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
addInt(DT_AARCH64_BTI_PLT, 0);
- if (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)
+ if (config->zPacPlt)
addInt(DT_AARCH64_PAC_PLT, 0);
}
@@ -2151,7 +2153,7 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *buf) {
eSym->st_size = sym->getSize();
// st_value is usually an address of a symbol, but that has a
- // special meaining for uninstantiated common symbols (this can
+ // special meaning for uninstantiated common symbols (this can
// occur if -r is given).
if (BssSection *commonSec = getCommonSec(ent.sym))
eSym->st_value = commonSec->alignment;
@@ -2178,7 +2180,7 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *buf) {
// We already set the less-significant bit for symbols
// marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT
// records. That allows us to distinguish such symbols in
- // the `MIPS<ELFT>::relocateOne()` routine. Now we should
+ // the `MIPS<ELFT>::relocate()` routine. Now we should
// clear that bit for non-dynamic symbol table, so tools
// like `objdump` will be able to deal with a correct
// symbol position.
@@ -2250,7 +2252,7 @@ size_t SymtabShndxSection::getSize() const {
// DSOs. That means resolving all dynamic symbols takes O(m)*O(n)
// where m is the number of DSOs and n is the number of dynamic
// symbols. For modern large programs, both m and n are large. So
-// making each step faster by using hash tables substiantially
+// making each step faster by using hash tables substantially
// improves time to load programs.
//
// (Note that this is not the only way to design the shared library.
@@ -2674,12 +2676,12 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
uint32_t cuIdx = 0;
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
if (Error e = cu->tryExtractDIEsIfNeeded(false)) {
- error(toString(sec) + ": " + toString(std::move(e)));
+ warn(toString(sec) + ": " + toString(std::move(e)));
return {};
}
Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
if (!ranges) {
- error(toString(sec) + ": " + toString(ranges.takeError()));
+ warn(toString(sec) + ": " + toString(ranges.takeError()));
return {};
}
@@ -2687,15 +2689,11 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
for (DWARFAddressRange &r : *ranges) {
if (r.SectionIndex == -1ULL)
continue;
- InputSectionBase *s = sections[r.SectionIndex];
- if (!s || s == &InputSection::discarded || !s->isLive())
- continue;
// Range list with zero size has no effect.
- if (r.LowPC == r.HighPC)
- continue;
- auto *isec = cast<InputSection>(s);
- uint64_t offset = isec->getOffsetInFile();
- ret.push_back({isec, r.LowPC - offset, r.HighPC - offset, cuIdx});
+ InputSectionBase *s = sections[r.SectionIndex];
+ if (s && s != &InputSection::discarded && s->isLive())
+ if (r.LowPC != r.HighPC)
+ ret.push_back({cast<InputSection>(s), r.LowPC, r.HighPC, cuIdx});
}
++cuIdx;
}
@@ -2707,12 +2705,16 @@ template <class ELFT>
static std::vector<GdbIndexSection::NameAttrEntry>
readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
const std::vector<GdbIndexSection::CuEntry> &cus) {
- const DWARFSection &pubNames = obj.getGnuPubnamesSection();
- const DWARFSection &pubTypes = obj.getGnuPubtypesSection();
+ const LLDDWARFSection &pubNames = obj.getGnuPubnamesSection();
+ const LLDDWARFSection &pubTypes = obj.getGnuPubtypesSection();
std::vector<GdbIndexSection::NameAttrEntry> ret;
- for (const DWARFSection *pub : {&pubNames, &pubTypes}) {
- DWARFDebugPubTable table(obj, *pub, config->isLE, true);
+ for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) {
+ DWARFDataExtractor data(obj, *pub, config->isLE, config->wordsize);
+ DWARFDebugPubTable table;
+ table.extract(data, /*GnuStyle=*/true, [&](Error e) {
+ warn(toString(pub->sec) + ": " + toString(std::move(e)));
+ });
for (const DWARFDebugPubTable::Set &set : table.getData()) {
// The value written into the constant pool is kind << 24 | cuIndex. As we
// don't know how many compilation units precede this object to compute
@@ -2750,11 +2752,11 @@ createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> nameAttrs,
// The number of symbols we will handle in this function is of the order
// of millions for very large executables, so we use multi-threading to
// speed it up.
- size_t numShards = 32;
- size_t concurrency = 1;
- if (threadsEnabled)
- concurrency =
- std::min<size_t>(PowerOf2Floor(hardware_concurrency()), numShards);
+ constexpr size_t numShards = 32;
+ size_t concurrency = PowerOf2Floor(
+ std::min<size_t>(hardware_concurrency(parallel::strategy.ThreadsRequested)
+ .compute_thread_count(),
+ numShards));
// A sharded map to uniquify symbols by name.
std::vector<DenseMap<CachedHashStringRef, size_t>> map(numShards);
@@ -2826,6 +2828,8 @@ template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
std::vector<std::vector<NameAttrEntry>> nameAttrs(sections.size());
parallelForEachN(0, sections.size(), [&](size_t i) {
+ // To keep memory usage low, we don't want to keep cached DWARFContext, so
+ // avoid getDwarf() here.
ObjFile<ELFT> *file = sections[i]->getFile<ELFT>();
DWARFContext dwarf(std::make_unique<LLDDwarfObj<ELFT>>(file));
@@ -3051,7 +3055,7 @@ bool VersionTableSection::isNeeded() const {
(getPartition().verDef || getPartition().verNeed->isNeeded());
}
-void addVerneed(Symbol *ss) {
+void elf::addVerneed(Symbol *ss) {
auto &file = cast<SharedFile>(*ss->file);
if (ss->verdefIndex == VER_NDX_GLOBAL) {
ss->versionId = VER_NDX_GLOBAL;
@@ -3195,10 +3199,10 @@ void MergeNoTailSection::finalizeContents() {
// Concurrency level. Must be a power of 2 to avoid expensive modulo
// operations in the following tight loop.
- size_t concurrency = 1;
- if (threadsEnabled)
- concurrency =
- std::min<size_t>(PowerOf2Floor(hardware_concurrency()), numShards);
+ size_t concurrency = PowerOf2Floor(
+ std::min<size_t>(hardware_concurrency(parallel::strategy.ThreadsRequested)
+ .compute_thread_count(),
+ numShards));
// Add section pieces to the builders.
parallelForEachN(0, concurrency, [&](size_t threadId) {
@@ -3234,16 +3238,17 @@ void MergeNoTailSection::finalizeContents() {
});
}
-MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
- uint64_t flags,
- uint32_t alignment) {
+MergeSyntheticSection *elf::createMergeSynthetic(StringRef name, uint32_t type,
+ uint64_t flags,
+ uint32_t alignment) {
bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
if (shouldTailMerge)
return make<MergeTailSection>(name, type, flags, alignment);
return make<MergeNoTailSection>(name, type, flags, alignment);
}
-template <class ELFT> void splitSections() {
+template <class ELFT> void elf::splitSections() {
+ llvm::TimeTraceScope timeScope("Split sections");
// splitIntoPieces needs to be called on each MergeInputSection
// before calling finalizeContents().
parallelForEach(inputSections, [](InputSectionBase *sec) {
@@ -3264,7 +3269,7 @@ ARMExidxSyntheticSection::ARMExidxSyntheticSection()
static InputSection *findExidxSection(InputSection *isec) {
for (InputSection *d : isec->dependentSections)
- if (d->type == SHT_ARM_EXIDX)
+ if (d->type == SHT_ARM_EXIDX && d->isLive())
return d;
return nullptr;
}
@@ -3277,8 +3282,13 @@ static bool isValidExidxSectionDep(InputSection *isec) {
bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
if (isec->type == SHT_ARM_EXIDX) {
if (InputSection *dep = isec->getLinkOrderDep())
- if (isValidExidxSectionDep(dep))
+ if (isValidExidxSectionDep(dep)) {
exidxSections.push_back(isec);
+ // Every exidxSection is 8 bytes, we need an estimate of
+ // size before assignAddresses can be called. Final size
+ // will only be known after finalize is called.
+ size += 8;
+ }
return true;
}
@@ -3359,19 +3369,30 @@ void ARMExidxSyntheticSection::finalizeContents() {
// ICF may remove executable InputSections and their dependent .ARM.exidx
// section that we recorded earlier.
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
- llvm::erase_if(executableSections, isDiscarded);
llvm::erase_if(exidxSections, isDiscarded);
+ // We need to remove discarded InputSections and InputSections without
+ // .ARM.exidx sections that if we generated the .ARM.exidx it would be out
+ // of range.
+ auto isDiscardedOrOutOfRange = [this](InputSection *isec) {
+ if (!isec->isLive())
+ return true;
+ if (findExidxSection(isec))
+ return false;
+ int64_t off = static_cast<int64_t>(isec->getVA() - getVA());
+ return off != llvm::SignExtend64(off, 31);
+ };
+ llvm::erase_if(executableSections, isDiscardedOrOutOfRange);
// Sort the executable sections that may or may not have associated
// .ARM.exidx sections by order of ascending address. This requires the
- // relative positions of InputSections to be known.
+ // relative positions of InputSections and OutputSections to be known.
auto compareByFilePosition = [](const InputSection *a,
const InputSection *b) {
OutputSection *aOut = a->getParent();
OutputSection *bOut = b->getParent();
if (aOut != bOut)
- return aOut->sectionIndex < bOut->sectionIndex;
+ return aOut->addr < bOut->addr;
return a->outSecOff < b->outSecOff;
};
llvm::stable_sort(executableSections, compareByFilePosition);
@@ -3438,7 +3459,7 @@ void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
memcpy(buf + offset, cantUnwindData, sizeof(cantUnwindData));
uint64_t s = isec->getVA();
uint64_t p = getVA() + offset;
- target->relocateOne(buf + offset, R_ARM_PREL31, s - p);
+ target->relocateNoSym(buf + offset, R_ARM_PREL31, s - p);
offset += 8;
}
}
@@ -3446,7 +3467,7 @@ void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
memcpy(buf + offset, cantUnwindData, sizeof(cantUnwindData));
uint64_t s = sentinel->getVA(sentinel->getSize());
uint64_t p = getVA() + offset;
- target->relocateOne(buf + offset, R_ARM_PREL31, s - p);
+ target->relocateNoSym(buf + offset, R_ARM_PREL31, s - p);
assert(size == offset + 8);
}
@@ -3612,7 +3633,7 @@ static uint8_t getAbiVersion() {
return 0;
}
-template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part) {
+template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) {
// For executable segments, the trap instructions are written before writing
// the header. Setting Elf header bytes to zero ensures that any unused bytes
// in header are zero-cleared, instead of having trap instructions.
@@ -3638,7 +3659,7 @@ template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part) {
}
}
-template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part) {
+template <typename ELFT> void elf::writePhdrs(uint8_t *buf, Partition &part) {
// Write the program header table.
auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
for (PhdrEntry *p : part.phdrs) {
@@ -3713,85 +3734,82 @@ void PartitionIndexSection::writeTo(uint8_t *buf) {
}
}
-InStruct in;
+InStruct elf::in;
-std::vector<Partition> partitions;
-Partition *mainPart;
+std::vector<Partition> elf::partitions;
+Partition *elf::mainPart;
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
template GdbIndexSection *GdbIndexSection::create<ELF64BE>();
-template void splitSections<ELF32LE>();
-template void splitSections<ELF32BE>();
-template void splitSections<ELF64LE>();
-template void splitSections<ELF64BE>();
-
-template class MipsAbiFlagsSection<ELF32LE>;
-template class MipsAbiFlagsSection<ELF32BE>;
-template class MipsAbiFlagsSection<ELF64LE>;
-template class MipsAbiFlagsSection<ELF64BE>;
-
-template class MipsOptionsSection<ELF32LE>;
-template class MipsOptionsSection<ELF32BE>;
-template class MipsOptionsSection<ELF64LE>;
-template class MipsOptionsSection<ELF64BE>;
-
-template class MipsReginfoSection<ELF32LE>;
-template class MipsReginfoSection<ELF32BE>;
-template class MipsReginfoSection<ELF64LE>;
-template class MipsReginfoSection<ELF64BE>;
-
-template class DynamicSection<ELF32LE>;
-template class DynamicSection<ELF32BE>;
-template class DynamicSection<ELF64LE>;
-template class DynamicSection<ELF64BE>;
-
-template class RelocationSection<ELF32LE>;
-template class RelocationSection<ELF32BE>;
-template class RelocationSection<ELF64LE>;
-template class RelocationSection<ELF64BE>;
-
-template class AndroidPackedRelocationSection<ELF32LE>;
-template class AndroidPackedRelocationSection<ELF32BE>;
-template class AndroidPackedRelocationSection<ELF64LE>;
-template class AndroidPackedRelocationSection<ELF64BE>;
-
-template class RelrSection<ELF32LE>;
-template class RelrSection<ELF32BE>;
-template class RelrSection<ELF64LE>;
-template class RelrSection<ELF64BE>;
-
-template class SymbolTableSection<ELF32LE>;
-template class SymbolTableSection<ELF32BE>;
-template class SymbolTableSection<ELF64LE>;
-template class SymbolTableSection<ELF64BE>;
-
-template class VersionNeedSection<ELF32LE>;
-template class VersionNeedSection<ELF32BE>;
-template class VersionNeedSection<ELF64LE>;
-template class VersionNeedSection<ELF64BE>;
-
-template void writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
-template void writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
-template void writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
-template void writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
-
-template void writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
-template void writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
-template void writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
-template void writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
-
-template class PartitionElfHeaderSection<ELF32LE>;
-template class PartitionElfHeaderSection<ELF32BE>;
-template class PartitionElfHeaderSection<ELF64LE>;
-template class PartitionElfHeaderSection<ELF64BE>;
-
-template class PartitionProgramHeadersSection<ELF32LE>;
-template class PartitionProgramHeadersSection<ELF32BE>;
-template class PartitionProgramHeadersSection<ELF64LE>;
-template class PartitionProgramHeadersSection<ELF64BE>;
-
-} // namespace elf
-} // namespace lld
+template void elf::splitSections<ELF32LE>();
+template void elf::splitSections<ELF32BE>();
+template void elf::splitSections<ELF64LE>();
+template void elf::splitSections<ELF64BE>();
+
+template class elf::MipsAbiFlagsSection<ELF32LE>;
+template class elf::MipsAbiFlagsSection<ELF32BE>;
+template class elf::MipsAbiFlagsSection<ELF64LE>;
+template class elf::MipsAbiFlagsSection<ELF64BE>;
+
+template class elf::MipsOptionsSection<ELF32LE>;
+template class elf::MipsOptionsSection<ELF32BE>;
+template class elf::MipsOptionsSection<ELF64LE>;
+template class elf::MipsOptionsSection<ELF64BE>;
+
+template class elf::MipsReginfoSection<ELF32LE>;
+template class elf::MipsReginfoSection<ELF32BE>;
+template class elf::MipsReginfoSection<ELF64LE>;
+template class elf::MipsReginfoSection<ELF64BE>;
+
+template class elf::DynamicSection<ELF32LE>;
+template class elf::DynamicSection<ELF32BE>;
+template class elf::DynamicSection<ELF64LE>;
+template class elf::DynamicSection<ELF64BE>;
+
+template class elf::RelocationSection<ELF32LE>;
+template class elf::RelocationSection<ELF32BE>;
+template class elf::RelocationSection<ELF64LE>;
+template class elf::RelocationSection<ELF64BE>;
+
+template class elf::AndroidPackedRelocationSection<ELF32LE>;
+template class elf::AndroidPackedRelocationSection<ELF32BE>;
+template class elf::AndroidPackedRelocationSection<ELF64LE>;
+template class elf::AndroidPackedRelocationSection<ELF64BE>;
+
+template class elf::RelrSection<ELF32LE>;
+template class elf::RelrSection<ELF32BE>;
+template class elf::RelrSection<ELF64LE>;
+template class elf::RelrSection<ELF64BE>;
+
+template class elf::SymbolTableSection<ELF32LE>;
+template class elf::SymbolTableSection<ELF32BE>;
+template class elf::SymbolTableSection<ELF64LE>;
+template class elf::SymbolTableSection<ELF64BE>;
+
+template class elf::VersionNeedSection<ELF32LE>;
+template class elf::VersionNeedSection<ELF32BE>;
+template class elf::VersionNeedSection<ELF64LE>;
+template class elf::VersionNeedSection<ELF64BE>;
+
+template void elf::writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
+template void elf::writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
+template void elf::writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
+template void elf::writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
+
+template void elf::writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
+template void elf::writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
+template void elf::writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
+template void elf::writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
+
+template class elf::PartitionElfHeaderSection<ELF32LE>;
+template class elf::PartitionElfHeaderSection<ELF32BE>;
+template class elf::PartitionElfHeaderSection<ELF64LE>;
+template class elf::PartitionElfHeaderSection<ELF64BE>;
+
+template class elf::PartitionProgramHeadersSection<ELF32LE>;
+template class elf::PartitionProgramHeadersSection<ELF32BE>;
+template class elf::PartitionProgramHeadersSection<ELF64LE>;
+template class elf::PartitionProgramHeadersSection<ELF64BE>;
diff --git a/contrib/llvm-project/lld/ELF/SyntheticSections.h b/contrib/llvm-project/lld/ELF/SyntheticSections.h
index 190a4fd3ac9e..8ed82ba64a6e 100644
--- a/contrib/llvm-project/lld/ELF/SyntheticSections.h
+++ b/contrib/llvm-project/lld/ELF/SyntheticSections.h
@@ -364,7 +364,7 @@ private:
// Try to merge two GOTs. In case of success the `Dst` contains
// result of merging and the function returns true. In case of
- // ovwerflow the `Dst` is unchanged and the function returns false.
+ // overflow the `Dst` is unchanged and the function returns false.
bool tryMergeGots(FileGot & dst, FileGot & src, bool isPrimary);
};
@@ -1046,7 +1046,7 @@ public:
std::vector<InputSection *> exidxSections;
private:
- size_t size;
+ size_t size = 0;
// Instead of storing pointers to the .ARM.exidx InputSections from
// InputObjects, we store pointers to the executable sections that need
diff --git a/contrib/llvm-project/lld/ELF/Target.cpp b/contrib/llvm-project/lld/ELF/Target.cpp
index d3899d0f18f1..6abd8b452e23 100644
--- a/contrib/llvm-project/lld/ELF/Target.cpp
+++ b/contrib/llvm-project/lld/ELF/Target.cpp
@@ -35,19 +35,19 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-std::string toString(elf::RelType type) {
+const TargetInfo *elf::target;
+
+std::string lld::toString(RelType type) {
StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
if (s == "Unknown")
return ("Unknown (" + Twine(type) + ")").str();
- return s;
+ return std::string(s);
}
-namespace elf {
-const TargetInfo *target;
-
-TargetInfo *getTarget() {
+TargetInfo *elf::getTarget() {
switch (config->emachine) {
case EM_386:
case EM_IAMCU:
@@ -112,7 +112,7 @@ template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
return {};
}
-ErrorPlace getErrorPlace(const uint8_t *loc) {
+ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
switch (config->ekind) {
case ELF32LEKind:
return getErrPlace<ELF32LE>(loc);
@@ -155,26 +155,27 @@ RelExpr TargetInfo::adjustRelaxExpr(RelType type, const uint8_t *data,
return expr;
}
-void TargetInfo::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+void TargetInfo::relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToLe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToIe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsIeToLe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsLdToLe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
@@ -185,6 +186,3 @@ uint64_t TargetInfo::getImageBase() const {
return *config->imageBase;
return config->isPic ? 0 : defaultImageBase;
}
-
-} // namespace elf
-} // namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Target.h b/contrib/llvm-project/lld/ELF/Target.h
index 949a7bfdf64b..47905ae64a47 100644
--- a/contrib/llvm-project/lld/ELF/Target.h
+++ b/contrib/llvm-project/lld/ELF/Target.h
@@ -82,10 +82,27 @@ public:
virtual bool inBranchRange(RelType type, uint64_t src,
uint64_t dst) const;
- virtual void relocateOne(uint8_t *loc, RelType type, uint64_t val) const = 0;
+ virtual void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const = 0;
+ void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const {
+ relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val);
+ }
+
+ virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
+ JumpModType val) const {}
virtual ~TargetInfo();
+ // This deletes a jump insn at the end of the section if it is a fall thru to
+ // the next section. Further, if there is a conditional jump and a direct
+ // jump consecutively, it tries to flip the conditional jump to convert the
+ // direct jump into a fall thru and delete it. Returns true if a jump
+ // instruction can be deleted.
+ virtual bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
+ InputSection *nextIS) const {
+ return false;
+ }
+
unsigned defaultCommonPageSize = 4096;
unsigned defaultMaxPageSize = 4096;
@@ -122,6 +139,10 @@ public:
// executable OutputSections.
std::array<uint8_t, 4> trapInstr;
+ // Stores the NOP instructions of different sizes for the target and is used
+ // to pad sections that are relaxed.
+ llvm::Optional<std::vector<std::vector<uint8_t>>> nopInstrs;
+
// If a target needs to rewrite calls to __morestack to instead call
// __morestack_non_split when a split-stack enabled caller calls a
// non-split-stack callee this will return true. Otherwise returns false.
@@ -129,11 +150,16 @@ public:
virtual RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const;
- virtual void relaxGot(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const;
+ virtual void relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
protected:
// On FreeBSD x86_64 the first page cannot be mmaped.
@@ -171,8 +197,7 @@ static inline std::string getErrorLocation(const uint8_t *loc) {
void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
-bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
- uint8_t *bufLoc);
+bool tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc);
unsigned getPPCDFormOp(unsigned secondaryOp);
// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first
@@ -188,6 +213,7 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther);
// the .toc section.
bool isPPC64SmallCodeModelTocReloc(RelType type);
+void addPPC64SaveRestore();
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t expr);
@@ -196,44 +222,36 @@ TargetInfo *getTarget();
template <class ELFT> bool isMipsPIC(const Defined *sym);
-static inline void reportRangeError(uint8_t *loc, RelType type, const Twine &v,
- int64_t min, uint64_t max) {
- ErrorPlace errPlace = getErrorPlace(loc);
- StringRef hint;
- if (errPlace.isec && errPlace.isec->name.startswith(".debug"))
- hint = "; consider recompiling with -fdebug-types-section to reduce size "
- "of debug sections";
-
- errorOrWarn(errPlace.loc + "relocation " + lld::toString(type) +
- " out of range: " + v.str() + " is not in [" + Twine(min).str() +
- ", " + Twine(max).str() + "]" + hint);
-}
+void reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v,
+ int64_t min, uint64_t max);
// Make sure that V can be represented as an N bit signed integer.
-inline void checkInt(uint8_t *loc, int64_t v, int n, RelType type) {
+inline void checkInt(uint8_t *loc, int64_t v, int n, const Relocation &rel) {
if (v != llvm::SignExtend64(v, n))
- reportRangeError(loc, type, Twine(v), llvm::minIntN(n), llvm::maxIntN(n));
+ reportRangeError(loc, rel, Twine(v), llvm::minIntN(n), llvm::maxIntN(n));
}
// Make sure that V can be represented as an N bit unsigned integer.
-inline void checkUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
+inline void checkUInt(uint8_t *loc, uint64_t v, int n, const Relocation &rel) {
if ((v >> n) != 0)
- reportRangeError(loc, type, Twine(v), 0, llvm::maxUIntN(n));
+ reportRangeError(loc, rel, Twine(v), 0, llvm::maxUIntN(n));
}
// Make sure that V can be represented as an N bit signed or unsigned integer.
-inline void checkIntUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
+inline void checkIntUInt(uint8_t *loc, uint64_t v, int n,
+ const Relocation &rel) {
// For the error message we should cast V to a signed integer so that error
// messages show a small negative value rather than an extremely large one
if (v != (uint64_t)llvm::SignExtend64(v, n) && (v >> n) != 0)
- reportRangeError(loc, type, Twine((int64_t)v), llvm::minIntN(n),
+ reportRangeError(loc, rel, Twine((int64_t)v), llvm::minIntN(n),
llvm::maxUIntN(n));
}
-inline void checkAlignment(uint8_t *loc, uint64_t v, int n, RelType type) {
+inline void checkAlignment(uint8_t *loc, uint64_t v, int n,
+ const Relocation &rel) {
if ((v & (n - 1)) != 0)
error(getErrorLocation(loc) + "improper alignment for relocation " +
- lld::toString(type) + ": 0x" + llvm::utohexstr(v) +
+ lld::toString(rel.type) + ": 0x" + llvm::utohexstr(v) +
" is not aligned to " + Twine(n) + " bytes");
}
diff --git a/contrib/llvm-project/lld/ELF/Thunks.cpp b/contrib/llvm-project/lld/ELF/Thunks.cpp
index f9c2e2d74e0a..ea74d343ebb2 100644
--- a/contrib/llvm-project/lld/ELF/Thunks.cpp
+++ b/contrib/llvm-project/lld/ELF/Thunks.cpp
@@ -40,9 +40,8 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
@@ -280,6 +279,20 @@ public:
void addSymbols(ThunkSection &isec) override;
};
+// PPC64 R2 Save Stub
+// When the caller requires a valid R2 TOC pointer but the callee does not
+// require a TOC pointer and the callee cannot guarantee that it doesn't
+// clobber R2 then we need to save R2. This stub:
+// 1) Saves the TOC pointer to the stack.
+// 2) Tail calls the callee.
+class PPC64R2SaveStub final : public Thunk {
+public:
+ PPC64R2SaveStub(Symbol &dest) : Thunk(dest, 0) {}
+ uint32_t size() override { return 8; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+};
+
// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
// alignment. This gives a possible 26 bits of 'reach'. If the call offset is
// larger then that we need to emit a long-branch thunk. The target address
@@ -352,7 +365,7 @@ void AArch64ABSLongThunk::writeTo(uint8_t *buf) {
};
uint64_t s = getAArch64ThunkDestVA(destination, addend);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 8, R_AARCH64_ABS64, s);
+ target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s);
}
void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
@@ -376,9 +389,9 @@ void AArch64ADRPThunk::writeTo(uint8_t *buf) {
uint64_t s = getAArch64ThunkDestVA(destination, addend);
uint64_t p = getThunkTargetSym()->getVA();
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(s) - getAArch64Page(p));
- target->relocateOne(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
+ target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(s) - getAArch64Page(p));
+ target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
}
void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
@@ -422,7 +435,7 @@ void ARMThunk::writeTo(uint8_t *buf) {
0x00, 0x00, 0x00, 0xea, // b S
};
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_JUMP24, offset);
+ target->relocateNoSym(buf, R_ARM_JUMP24, offset);
}
bool ARMThunk::isCompatibleWith(const InputSection &isec,
@@ -460,7 +473,7 @@ void ThumbThunk::writeTo(uint8_t *buf) {
0x00, 0xf0, 0x00, 0xb0, // b.w S
};
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_THM_JUMP24, offset);
+ target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset);
}
bool ThumbThunk::isCompatibleWith(const InputSection &isec,
@@ -477,8 +490,8 @@ void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
};
uint64_t s = getARMThunkDestVA(destination);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_MOVW_ABS_NC, s);
- target->relocateOne(buf + 4, R_ARM_MOVT_ABS, s);
+ target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s);
+ target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s);
}
void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
@@ -495,8 +508,8 @@ void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
};
uint64_t s = getARMThunkDestVA(destination);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_THM_MOVW_ABS_NC, s);
- target->relocateOne(buf + 4, R_ARM_THM_MOVT_ABS, s);
+ target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s);
+ target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s);
}
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
@@ -516,8 +529,8 @@ void ARMV7PILongThunk::writeLong(uint8_t *buf) {
uint64_t p = getThunkTargetSym()->getVA();
int64_t offset = s - p - 16;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_MOVW_PREL_NC, offset);
- target->relocateOne(buf + 4, R_ARM_MOVT_PREL, offset);
+ target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset);
+ target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset);
}
void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
@@ -537,8 +550,8 @@ void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
int64_t offset = s - p - 12;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_THM_MOVW_PREL_NC, offset);
- target->relocateOne(buf + 4, R_ARM_THM_MOVT_PREL, offset);
+ target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset);
+ target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset);
}
void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
@@ -553,7 +566,7 @@ void ARMV5ABSLongThunk::writeLong(uint8_t *buf) {
0x00, 0x00, 0x00, 0x00, // L1: .word S
};
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
+ target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
}
void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
@@ -579,7 +592,7 @@ void ARMV5PILongThunk::writeLong(uint8_t *buf) {
uint64_t s = getARMThunkDestVA(destination);
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12);
+ target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
}
void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
@@ -609,7 +622,7 @@ void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
};
uint64_t s = getARMThunkDestVA(destination);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 8, R_ARM_ABS32, s);
+ target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
}
void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
@@ -635,7 +648,7 @@ void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
uint64_t s = getARMThunkDestVA(destination);
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12);
+ target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
}
void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
@@ -652,8 +665,8 @@ void MipsThunk::writeTo(uint8_t *buf) {
write32(buf + 4, 0x08000000 | (s >> 2)); // j func
write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
write32(buf + 12, 0x00000000); // nop
- target->relocateOne(buf, R_MIPS_HI16, s);
- target->relocateOne(buf + 8, R_MIPS_LO16, s);
+ target->relocateNoSym(buf, R_MIPS_HI16, s);
+ target->relocateNoSym(buf + 8, R_MIPS_LO16, s);
}
void MipsThunk::addSymbols(ThunkSection &isec) {
@@ -674,9 +687,9 @@ void MicroMipsThunk::writeTo(uint8_t *buf) {
write16(buf + 4, 0xd400); // j func
write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func)
write16(buf + 12, 0x0c00); // nop
- target->relocateOne(buf, R_MICROMIPS_HI16, s);
- target->relocateOne(buf + 4, R_MICROMIPS_26_S1, s);
- target->relocateOne(buf + 8, R_MICROMIPS_LO16, s);
+ target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
+ target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s);
+ target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s);
}
void MicroMipsThunk::addSymbols(ThunkSection &isec) {
@@ -698,9 +711,9 @@ void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
write16(buf, 0x1320); // lui $25, %hi(func)
write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func)
write16(buf + 8, 0x9400); // bc func
- target->relocateOne(buf, R_MICROMIPS_HI16, s);
- target->relocateOne(buf + 4, R_MICROMIPS_LO16, s);
- target->relocateOne(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
+ target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
+ target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s);
+ target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
}
void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
@@ -714,8 +727,8 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
return dyn_cast<InputSection>(dr.section);
}
-void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
- const InputFile *file, int64_t addend) {
+void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
+ const InputFile *file, int64_t addend) {
if (!config->isPic) {
write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11)
@@ -799,7 +812,7 @@ void PPC32LongThunk::writeTo(uint8_t *buf) {
write32(buf + 4, 0x4e800420); // bctr
}
-void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
+void elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
uint16_t offHa = (offset + 0x8000) >> 16;
uint16_t offLo = offset & 0xffff;
@@ -823,6 +836,21 @@ void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
s->file = destination.file;
}
+void PPC64R2SaveStub::writeTo(uint8_t *buf) {
+ int64_t offset = destination.getVA() - (getThunkTargetSym()->getVA() + 4);
+ // The branch offset needs to fit in 26 bits.
+ if (!isInt<26>(offset))
+ fatal("R2 save stub branch offset is too large: " + Twine(offset));
+ write32(buf + 0, 0xf8410018); // std r2,24(r1)
+ write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b <offset>
+}
+
+void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
+ Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ s->needsTocRestore = true;
+}
+
void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
getPPC64TocBase();
@@ -839,7 +867,8 @@ Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {}
Thunk::~Thunk() = default;
static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
- if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
+ type != R_AARCH64_PLT32)
fatal("unrecognized relocation type");
if (config->picThunk)
return make<AArch64ADRPThunk>(s, a);
@@ -945,17 +974,23 @@ static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel,
}
static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
- assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk");
+ assert((type == R_PPC64_REL14 || type == R_PPC64_REL24) &&
+ "unexpected relocation type for thunk");
if (s.isInPlt())
return make<PPC64PltCallStub>(s);
+ // This check looks at the st_other bits of the callee. If the value is 1
+ // then the callee clobbers the TOC and we need an R2 save stub.
+ if ((s.stOther >> 5) == 1)
+ return make<PPC64R2SaveStub>(s);
+
if (config->picThunk)
return make<PPC64PILongBranchThunk>(s, a);
return make<PPC64PDLongBranchThunk>(s, a);
}
-Thunk *addThunk(const InputSection &isec, Relocation &rel) {
+Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) {
Symbol &s = *rel.sym;
int64_t a = rel.addend;
@@ -976,6 +1011,3 @@ Thunk *addThunk(const InputSection &isec, Relocation &rel) {
llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
}
-
-} // end namespace elf
-} // end namespace lld
diff --git a/contrib/llvm-project/lld/ELF/Writer.cpp b/contrib/llvm-project/lld/ELF/Writer.cpp
index 7114ea2efb4d..b9fd03bc2eda 100644
--- a/contrib/llvm-project/lld/ELF/Writer.cpp
+++ b/contrib/llvm-project/lld/ELF/Writer.cpp
@@ -22,22 +22,25 @@
#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/RandomNumberGenerator.h"
#include "llvm/Support/SHA1.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/xxhash.h"
#include <climits>
+#define DEBUG_TYPE "lld"
+
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
// The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer {
@@ -56,6 +59,7 @@ private:
void sortSections();
void resolveShfLinkOrder();
void finalizeAddressDependentContent();
+ void optimizeBasicBlockJumps();
void sortInputSections();
void finalizeSections();
void checkExecuteOnly();
@@ -91,7 +95,7 @@ static bool isSectionPrefix(StringRef prefix, StringRef name) {
return name.startswith(prefix) || name == prefix.drop_back();
}
-StringRef getOutputSectionName(const InputSectionBase *s) {
+StringRef elf::getOutputSectionName(const InputSectionBase *s) {
if (config->relocatable)
return s->name;
@@ -107,15 +111,29 @@ StringRef getOutputSectionName(const InputSectionBase *s) {
}
}
- // This check is for -z keep-text-section-prefix. This option separates text
- // sections with prefix ".text.hot", ".text.unlikely", ".text.startup" or
- // ".text.exit".
- // When enabled, this allows identifying the hot code region (.text.hot) in
- // the final binary which can be selectively mapped to huge pages or mlocked,
- // for instance.
+ // A BssSection created for a common symbol is identified as "COMMON" in
+ // linker scripts. It should go to .bss section.
+ if (s->name == "COMMON")
+ return ".bss";
+
+ if (script->hasSectionsCommand)
+ return s->name;
+
+ // When no SECTIONS is specified, emulate GNU ld's internal linker scripts
+ // by grouping sections with certain prefixes.
+
+ // GNU ld places text sections with prefix ".text.hot.", ".text.unknown.",
+ // ".text.unlikely.", ".text.startup." or ".text.exit." before others.
+ // We provide an option -z keep-text-section-prefix to group such sections
+ // into separate output sections. This is more flexible. See also
+ // sortISDBySectionOrder().
+ // ".text.unknown" means the hotness of the section is unknown. When
+ // SampleFDO is used, if a function doesn't have sample, it could be very
+ // cold or it could be a new function never being sampled. Those functions
+ // will be kept in the ".text.unknown" section.
if (config->zKeepTextSectionPrefix)
- for (StringRef v :
- {".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."})
+ for (StringRef v : {".text.hot.", ".text.unknown.", ".text.unlikely.",
+ ".text.startup.", ".text.exit."})
if (isSectionPrefix(v, s->name))
return v.drop_back();
@@ -126,11 +144,6 @@ StringRef getOutputSectionName(const InputSectionBase *s) {
if (isSectionPrefix(v, s->name))
return v.drop_back();
- // CommonSection is identified as "COMMON" in linker scripts.
- // By default, it should go to .bss section.
- if (s->name == "COMMON")
- return ".bss";
-
return s->name;
}
@@ -139,20 +152,32 @@ static bool needsInterpSection() {
!config->dynamicLinker.empty() && script->needsInterpSection();
}
-template <class ELFT> void writeResult() { Writer<ELFT>().run(); }
+template <class ELFT> void elf::writeResult() {
+ llvm::TimeTraceScope timeScope("Write output file");
+ Writer<ELFT>().run();
+}
static void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
- llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
- if (p->p_type != PT_LOAD)
- return false;
- if (!p->firstSec)
- return true;
- uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr;
- return size == 0;
- });
+ auto it = std::stable_partition(
+ phdrs.begin(), phdrs.end(), [&](const PhdrEntry *p) {
+ if (p->p_type != PT_LOAD)
+ return true;
+ if (!p->firstSec)
+ return false;
+ uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr;
+ return size != 0;
+ });
+
+ // Clear OutputSection::ptLoad for sections contained in removed
+ // segments.
+ DenseSet<PhdrEntry *> removed(it, phdrs.end());
+ for (OutputSection *sec : outputSections)
+ if (removed.count(sec->ptLoad))
+ sec->ptLoad = nullptr;
+ phdrs.erase(it, phdrs.end());
}
-void copySectionsIntoPartitions() {
+void elf::copySectionsIntoPartitions() {
std::vector<InputSectionBase *> newSections;
for (unsigned part = 2; part != partitions.size() + 1; ++part) {
for (InputSectionBase *s : inputSections) {
@@ -174,7 +199,7 @@ void copySectionsIntoPartitions() {
newSections.end());
}
-void combineEhSections() {
+void elf::combineEhSections() {
for (InputSectionBase *&s : inputSections) {
// Ignore dead sections and the partition end marker (.part.end),
// whose partition number is out of bounds.
@@ -215,7 +240,7 @@ static Defined *addAbsolute(StringRef name) {
// The linker is expected to define some symbols depending on
// the linking result. This function defines such symbols.
-void addReservedSymbols() {
+void elf::addReservedSymbols() {
if (config->emachine == EM_MIPS) {
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
// so that it points to an absolute address which by default is relative
@@ -239,6 +264,8 @@ void addReservedSymbols() {
// glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't
// support Small Data Area, define it arbitrarily as 0.
addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN);
+ } else if (config->emachine == EM_PPC64) {
+ addPPC64SaveRestore();
}
// The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which
@@ -308,7 +335,7 @@ static OutputSection *findSection(StringRef name, unsigned partition = 1) {
return nullptr;
}
-template <class ELFT> void createSyntheticSections() {
+template <class ELFT> void elf::createSyntheticSections() {
// Initialize all pointers with NULL. This is needed because
// you can call lld::elf::main more than once as a library.
memset(&Out::first, 0, sizeof(Out));
@@ -549,8 +576,7 @@ template <class ELFT> void createSyntheticSections() {
// The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() {
- if (config->discard != DiscardPolicy::All)
- copyLocalSymbols();
+ copyLocalSymbols();
if (config->copyRelocs)
addSectionSymbols();
@@ -591,6 +617,14 @@ template <class ELFT> void Writer<ELFT>::run() {
for (OutputSection *sec : outputSections)
sec->addr = 0;
+ // Handle --print-map(-M)/--Map, --cref and --print-archive-stats=. Dump them
+ // before checkSections() because the files may be useful in case
+ // checkSections() or openFile() fails, for example, due to an erroneous file
+ // size.
+ writeMapFile();
+ writeCrossReferenceTable();
+ writeArchiveStats();
+
if (config->checkSections)
checkSections();
@@ -617,27 +651,67 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- // Handle -Map and -cref options.
- writeMapFile();
- writeCrossReferenceTable();
- if (errorCount())
- return;
-
if (auto e = buffer->commit())
error("failed to write to the output file: " + toString(std::move(e)));
}
+template <class ELFT, class RelTy>
+static void markUsedLocalSymbolsImpl(ObjFile<ELFT> *file,
+ llvm::ArrayRef<RelTy> rels) {
+ for (const RelTy &rel : rels) {
+ Symbol &sym = file->getRelocTargetSym(rel);
+ if (sym.isLocal())
+ sym.used = true;
+ }
+}
+
+// The function ensures that the "used" field of local symbols reflects the fact
+// that the symbol is used in a relocation from a live section.
+template <class ELFT> static void markUsedLocalSymbols() {
+ // With --gc-sections, the field is already filled.
+ // See MarkLive<ELFT>::resolveReloc().
+ if (config->gcSections)
+ return;
+ // Without --gc-sections, the field is initialized with "true".
+ // Drop the flag first and then rise for symbols referenced in relocations.
+ for (InputFile *file : objectFiles) {
+ ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
+ for (Symbol *b : f->getLocalSymbols())
+ b->used = false;
+ for (InputSectionBase *s : f->getSections()) {
+ InputSection *isec = dyn_cast_or_null<InputSection>(s);
+ if (!isec)
+ continue;
+ if (isec->type == SHT_REL)
+ markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rel>());
+ else if (isec->type == SHT_RELA)
+ markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rela>());
+ }
+ }
+}
+
static bool shouldKeepInSymtab(const Defined &sym) {
if (sym.isSection())
return false;
- if (config->discard == DiscardPolicy::None)
+ // If --emit-reloc or -r is given, preserve symbols referenced by relocations
+ // from live sections.
+ if (config->copyRelocs && sym.used)
return true;
- // If -emit-reloc is given, all symbols including local ones need to be
- // copied because they may be referenced by relocations.
- if (config->emitRelocs)
+ // Exclude local symbols pointing to .ARM.exidx sections.
+ // They are probably mapping symbols "$d", which are optional for these
+ // sections. After merging the .ARM.exidx sections, some of these symbols
+ // may become dangling. The easiest way to avoid the issue is not to add
+ // them to the symbol table from the beginning.
+ if (config->emachine == EM_ARM && sym.section &&
+ sym.section->type == SHT_ARM_EXIDX)
+ return false;
+
+ if (config->discard == DiscardPolicy::None)
return true;
+ if (config->discard == DiscardPolicy::All)
+ return false;
// In ELF assembly .L symbols are normally discarded by the assembler.
// If the assembler fails to do so, the linker discards them if
@@ -684,12 +758,12 @@ static bool includeInSymtab(const Symbol &b) {
template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
if (!in.symTab)
return;
+ if (config->copyRelocs && config->discard != DiscardPolicy::None)
+ markUsedLocalSymbols<ELFT>();
for (InputFile *file : objectFiles) {
ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
for (Symbol *b : f->getLocalSymbols()) {
- if (!b->isLocal())
- fatal(toString(f) +
- ": broken object: getLocalSymbols returns a non-local symbol");
+ assert(b->isLocal() && "should have been caught in initializeSymbols()");
auto *dr = dyn_cast<Defined>(b);
// No reason to keep local undefined symbol in symtab.
@@ -811,7 +885,8 @@ static bool isRelroSection(const OutputSection *sec) {
StringRef s = sec->name;
return s == ".data.rel.ro" || s == ".bss.rel.ro" || s == ".ctors" ||
s == ".dtors" || s == ".jcr" || s == ".eh_frame" ||
- s == ".openbsd.randomdata";
+ s == ".fini_array" || s == ".init_array" ||
+ s == ".openbsd.randomdata" || s == ".preinit_array";
}
// We compute a rank for each section. The rank indicates where the
@@ -1202,6 +1277,27 @@ findOrphanPos(std::vector<BaseCommand *>::iterator b,
return i;
}
+// Adds random priorities to sections not already in the map.
+static void maybeShuffle(DenseMap<const InputSectionBase *, int> &order) {
+ if (!config->shuffleSectionSeed)
+ return;
+
+ std::vector<int> priorities(inputSections.size() - order.size());
+ // Existing priorities are < 0, so use priorities >= 0 for the missing
+ // sections.
+ int curPrio = 0;
+ for (int &prio : priorities)
+ prio = curPrio++;
+ uint32_t seed = *config->shuffleSectionSeed;
+ std::mt19937 g(seed ? seed : std::random_device()());
+ llvm::shuffle(priorities.begin(), priorities.end(), g);
+ int prioIndex = 0;
+ for (InputSectionBase *sec : inputSections) {
+ if (order.try_emplace(sec, priorities[prioIndex]).second)
+ ++prioIndex;
+ }
+}
+
// Builds section order for handling --symbol-ordering-file.
static DenseMap<const InputSectionBase *, int> buildSectionOrder() {
DenseMap<const InputSectionBase *, int> sectionOrder;
@@ -1331,6 +1427,19 @@ static void sortSection(OutputSection *sec,
const DenseMap<const InputSectionBase *, int> &order) {
StringRef name = sec->name;
+ // Never sort these.
+ if (name == ".init" || name == ".fini")
+ return;
+
+ // Sort input sections by priority using the list provided by
+ // --symbol-ordering-file or --shuffle-sections=. This is a least significant
+ // digit radix sort. The sections may be sorted stably again by a more
+ // significant key.
+ if (!order.empty())
+ for (BaseCommand *b : sec->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ sortISDBySectionOrder(isd, order);
+
// Sort input sections by section name suffixes for
// __attribute__((init_priority(N))).
if (name == ".init_array" || name == ".fini_array") {
@@ -1346,10 +1455,6 @@ static void sortSection(OutputSection *sec,
return;
}
- // Never sort these.
- if (name == ".init" || name == ".fini")
- return;
-
// .toc is allocated just after .got and is accessed using GOT-relative
// relocations. Object files compiled with small code model have an
// addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations.
@@ -1367,13 +1472,6 @@ static void sortSection(OutputSection *sec,
});
return;
}
-
- // Sort input sections by priority using the list provided
- // by --symbol-ordering-file.
- if (!order.empty())
- for (BaseCommand *b : sec->sectionCommands)
- if (auto *isd = dyn_cast<InputSectionDescription>(b))
- sortISDBySectionOrder(isd, order);
}
// If no layout was provided by linker script, we want to apply default
@@ -1381,6 +1479,7 @@ static void sortSection(OutputSection *sec,
template <class ELFT> void Writer<ELFT>::sortInputSections() {
// Build the order once since it is expensive.
DenseMap<const InputSectionBase *, int> order = buildSectionOrder();
+ maybeShuffle(order);
for (BaseCommand *base : script->sectionCommands)
if (auto *sec = dyn_cast<OutputSection>(base))
sortSection(sec, order);
@@ -1417,9 +1516,15 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
llvm::find_if(script->sectionCommands, isSection),
llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(),
compareSections);
+
+ // Process INSERT commands. From this point onwards the order of
+ // script->sectionCommands is fixed.
+ script->processInsertCommands();
return;
}
+ script->processInsertCommands();
+
// Orphan sections are sections present in the input files which are
// not explicitly placed into the output file by the linker script.
//
@@ -1505,7 +1610,7 @@ static bool compareByFilePosition(InputSection *a, InputSection *b) {
OutputSection *bOut = lb->getParent();
if (aOut != bOut)
- return aOut->sectionIndex < bOut->sectionIndex;
+ return aOut->addr < bOut->addr;
return la->outSecOff < lb->outSecOff;
}
@@ -1561,6 +1666,11 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
}
}
+static void finalizeSynthetic(SyntheticSection *sec) {
+ if (sec && sec->isNeeded() && sec->getParent())
+ sec->finalizeContents();
+}
+
// We need to generate and finalize the content that depends on the address of
// InputSections. As the generation of the content may also alter InputSection
// addresses we must converge to a fixed point. We do that here. See the comment
@@ -1570,6 +1680,17 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
AArch64Err843419Patcher a64p;
ARMErr657417Patcher a32p;
script->assignAddresses();
+ // .ARM.exidx and SHF_LINK_ORDER do not require precise addresses, but they
+ // do require the relative addresses of OutputSections because linker scripts
+ // can assign Virtual Addresses to OutputSections that are not monotonically
+ // increasing.
+ for (Partition &part : partitions)
+ finalizeSynthetic(part.armExidx);
+ resolveShfLinkOrder();
+
+ // Converts call x@GDPLT to call __tls_get_addr
+ if (config->emachine == EM_HEXAGON)
+ hexagonTLSSymbolUpdate(outputSections);
int assignPasses = 0;
for (;;) {
@@ -1616,11 +1737,103 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
}
}
}
+
+ // If addrExpr is set, the address may not be a multiple of the alignment.
+ // Warn because this is error-prone.
+ for (BaseCommand *cmd : script->sectionCommands)
+ if (auto *os = dyn_cast<OutputSection>(cmd))
+ if (os->addr % os->alignment != 0)
+ warn("address (0x" + Twine::utohexstr(os->addr) + ") of section " +
+ os->name + " is not a multiple of alignment (" +
+ Twine(os->alignment) + ")");
+}
+
+// If Input Sections have been shrinked (basic block sections) then
+// update symbol values and sizes associated with these sections. With basic
+// block sections, input sections can shrink when the jump instructions at
+// the end of the section are relaxed.
+static void fixSymbolsAfterShrinking() {
+ for (InputFile *File : objectFiles) {
+ parallelForEach(File->getSymbols(), [&](Symbol *Sym) {
+ auto *def = dyn_cast<Defined>(Sym);
+ if (!def)
+ return;
+
+ const SectionBase *sec = def->section;
+ if (!sec)
+ return;
+
+ const InputSectionBase *inputSec = dyn_cast<InputSectionBase>(sec->repl);
+ if (!inputSec || !inputSec->bytesDropped)
+ return;
+
+ const size_t OldSize = inputSec->data().size();
+ const size_t NewSize = OldSize - inputSec->bytesDropped;
+
+ if (def->value > NewSize && def->value <= OldSize) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "Moving symbol " << Sym->getName() << " from "
+ << def->value << " to "
+ << def->value - inputSec->bytesDropped << " bytes\n");
+ def->value -= inputSec->bytesDropped;
+ return;
+ }
+
+ if (def->value + def->size > NewSize && def->value <= OldSize &&
+ def->value + def->size <= OldSize) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "Shrinking symbol " << Sym->getName() << " from "
+ << def->size << " to " << def->size - inputSec->bytesDropped
+ << " bytes\n");
+ def->size -= inputSec->bytesDropped;
+ }
+ });
+ }
}
-static void finalizeSynthetic(SyntheticSection *sec) {
- if (sec && sec->isNeeded() && sec->getParent())
- sec->finalizeContents();
+// If basic block sections exist, there are opportunities to delete fall thru
+// jumps and shrink jump instructions after basic block reordering. This
+// relaxation pass does that. It is only enabled when --optimize-bb-jumps
+// option is used.
+template <class ELFT> void Writer<ELFT>::optimizeBasicBlockJumps() {
+ assert(config->optimizeBBJumps);
+
+ script->assignAddresses();
+ // For every output section that has executable input sections, this
+ // does the following:
+ // 1. Deletes all direct jump instructions in input sections that
+ // jump to the following section as it is not required.
+ // 2. If there are two consecutive jump instructions, it checks
+ // if they can be flipped and one can be deleted.
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_EXECINSTR))
+ continue;
+ std::vector<InputSection *> sections = getInputSections(os);
+ std::vector<unsigned> result(sections.size());
+ // Delete all fall through jump instructions. Also, check if two
+ // consecutive jump instructions can be flipped so that a fall
+ // through jmp instruction can be deleted.
+ parallelForEachN(0, sections.size(), [&](size_t i) {
+ InputSection *next = i + 1 < sections.size() ? sections[i + 1] : nullptr;
+ InputSection &is = *sections[i];
+ result[i] =
+ target->deleteFallThruJmpInsn(is, is.getFile<ELFT>(), next) ? 1 : 0;
+ });
+ size_t numDeleted = std::count(result.begin(), result.end(), 1);
+ if (numDeleted > 0) {
+ script->assignAddresses();
+ LLVM_DEBUG(llvm::dbgs()
+ << "Removing " << numDeleted << " fall through jumps\n");
+ }
+ }
+
+ fixSymbolsAfterShrinking();
+
+ for (OutputSection *os : outputSections) {
+ std::vector<InputSection *> sections = getInputSections(os);
+ for (InputSection *is : sections)
+ is->trim();
+ }
}
// In order to allow users to manipulate linker-synthesized sections,
@@ -1647,12 +1860,15 @@ static void removeUnusedSyntheticSections() {
if (!os || ss->isNeeded())
continue;
- // If we reach here, then SS is an unused synthetic section and we want to
- // remove it from corresponding input section description of output section.
+ // If we reach here, then ss is an unused synthetic section and we want to
+ // remove it from the corresponding input section description, and
+ // orphanSections.
for (BaseCommand *b : os->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(b))
llvm::erase_if(isd->sections,
[=](InputSection *isec) { return isec == ss; });
+ llvm::erase_if(script->orphanSections,
+ [=](const InputSectionBase *isec) { return isec == ss; });
}
}
@@ -1733,6 +1949,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// we can correctly decide if a dynamic relocation is needed. This is called
// after processSymbolAssignments() because it needs to know whether a
// linker-script-defined symbol is absolute.
+ ppc64noTocRelax.clear();
if (!config->relocatable) {
forEachRelSec(scanRelocations<ELFT>);
reportUndefinedSymbols<ELFT>();
@@ -1761,7 +1978,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
if (sym->isUndefined() && !sym->isWeak())
if (auto *f = dyn_cast_or_null<SharedFile>(sym->file))
if (f->allNeededIsKnown)
- error(toString(f) + ": undefined reference to " + toString(*sym));
+ errorOrWarn(toString(f) + ": undefined reference to " +
+ toString(*sym) + " [--no-allow-shlib-undefined]");
}
// Now that we have defined all possible global symbols including linker-
@@ -1799,6 +2017,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
in.mipsGot->build();
removeUnusedSyntheticSections();
+ script->diagnoseOrphanHandling();
sortSections();
@@ -1815,6 +2034,15 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
sec->addrExpr = [=] { return i->second; };
}
+ // With the outputSections available check for GDPLT relocations
+ // and add __tls_get_addr symbol if needed.
+ if (config->emachine == EM_HEXAGON && hexagonNeedsTLSSymbol(outputSections)) {
+ Symbol *sym = symtab->addSymbol(Undefined{
+ nullptr, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE});
+ sym->isPreemptible = true;
+ partitions[0].dynSymTab->addSymbol(sym);
+ }
+
// This is a bit of a hack. A value of 0 means undef, so we set it
// to 1 to make __ehdr_start defined. The section number is not
// particularly relevant.
@@ -1878,7 +2106,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Dynamic section must be the last one in this list and dynamic
// symbol table section (dynSymTab) must be the first one.
for (Partition &part : partitions) {
- finalizeSynthetic(part.armExidx);
finalizeSynthetic(part.dynSymTab);
finalizeSynthetic(part.gnuHashTab);
finalizeSynthetic(part.hashTab);
@@ -1894,12 +2121,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
if (!script->hasSectionsCommand && !config->relocatable)
fixSectionAlignments();
- // SHFLinkOrder processing must be processed after relative section placements are
- // known but before addresses are allocated.
- resolveShfLinkOrder();
- if (errorCount())
- return;
-
// This is used to:
// 1) Create "thunks":
// Jump instructions in many ISAs have small displacements, and therefore
@@ -1922,11 +2143,19 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// sometimes using forward symbol declarations. We want to set the correct
// values. They also might change after adding the thunks.
finalizeAddressDependentContent();
+ if (errorCount())
+ return;
// finalizeAddressDependentContent may have added local symbols to the static symbol table.
finalizeSynthetic(in.symTab);
finalizeSynthetic(in.ppc64LongBranchTarget);
+ // Relaxation to delete inter-basic block jumps created by basic block
+ // sections. Run after in.symTab is finalized as optimizeBasicBlockJumps
+ // can relax jump instructions based on symbol offset.
+ if (config->optimizeBBJumps)
+ optimizeBasicBlockJumps();
+
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
// of finalizing other sections.
@@ -2000,8 +2229,10 @@ void Writer<ELFT>::addStartStopSymbols(OutputSection *sec) {
StringRef s = sec->name;
if (!isValidCIdentifier(s))
return;
- addOptionalRegular(saver.save("__start_" + s), sec, 0, STV_PROTECTED);
- addOptionalRegular(saver.save("__stop_" + s), sec, -1, STV_PROTECTED);
+ addOptionalRegular(saver.save("__start_" + s), sec, 0,
+ config->zStartStopVisibility);
+ addOptionalRegular(saver.save("__stop_" + s), sec, -1,
+ config->zStartStopVisibility);
}
static bool needsPtLoad(OutputSection *sec) {
@@ -2120,12 +2351,11 @@ std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
// time, we don't want to create a separate load segment for the headers,
// even if the first output section has an AT or AT> attribute.
uint64_t newFlags = computeFlags(sec->getPhdrFlags());
- if (!load ||
- ((sec->lmaExpr ||
- (sec->lmaRegion && (sec->lmaRegion != load->firstSec->lmaRegion))) &&
- load->lastSec != Out::programHeaders) ||
- sec->memRegion != load->firstSec->memRegion || flags != newFlags ||
- sec == relroEnd) {
+ bool sameLMARegion =
+ load && !sec->lmaExpr && sec->lmaRegion == load->firstSec->lmaRegion;
+ if (!(load && newFlags == flags && sec != relroEnd &&
+ sec->memRegion == load->firstSec->memRegion &&
+ (sameLMARegion || load->lastSec == Out::programHeaders))) {
load = addHdr(PT_LOAD, newFlags);
flags = newFlags;
}
@@ -2219,7 +2449,10 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
const PhdrEntry *prev;
auto pageAlign = [&](const PhdrEntry *p) {
OutputSection *cmd = p->firstSec;
- if (cmd && !cmd->addrExpr) {
+ if (!cmd)
+ return;
+ cmd->alignExpr = [align = cmd->alignment]() { return align; };
+ if (!cmd->addrExpr) {
// Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid
// padding in the file contents.
//
@@ -2730,15 +2963,12 @@ template <class ELFT> void Writer<ELFT>::writeBuildId() {
part.buildId->writeBuildId(buildId);
}
-template void createSyntheticSections<ELF32LE>();
-template void createSyntheticSections<ELF32BE>();
-template void createSyntheticSections<ELF64LE>();
-template void createSyntheticSections<ELF64BE>();
-
-template void writeResult<ELF32LE>();
-template void writeResult<ELF32BE>();
-template void writeResult<ELF64LE>();
-template void writeResult<ELF64BE>();
+template void elf::createSyntheticSections<ELF32LE>();
+template void elf::createSyntheticSections<ELF32BE>();
+template void elf::createSyntheticSections<ELF64LE>();
+template void elf::createSyntheticSections<ELF64BE>();
-} // namespace elf
-} // namespace lld
+template void elf::writeResult<ELF32LE>();
+template void elf::writeResult<ELF32BE>();
+template void elf::writeResult<ELF64LE>();
+template void elf::writeResult<ELF64BE>();
diff --git a/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp b/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp
new file mode 100644
index 000000000000..36f686ca2f1d
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp
@@ -0,0 +1,286 @@
+//===- X86_64.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm::MachO;
+using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::macho;
+
+namespace {
+
+struct X86_64 : TargetInfo {
+ X86_64();
+
+ uint64_t getImplicitAddend(MemoryBufferRef, const section_64 &,
+ const relocation_info &) const override;
+ void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const override;
+
+ void writeStub(uint8_t *buf, const DylibSymbol &) const override;
+ void writeStubHelperHeader(uint8_t *buf) const override;
+ void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+ uint64_t entryAddr) const override;
+
+ void prepareSymbolRelocation(lld::macho::Symbol &, const InputSection *,
+ const Reloc &) override;
+ uint64_t getSymbolVA(const lld::macho::Symbol &, uint8_t type) const override;
+};
+
+} // namespace
+
+static std::string getErrorLocation(MemoryBufferRef mb, const section_64 &sec,
+ const relocation_info &rel) {
+ return ("invalid relocation at offset " + std::to_string(rel.r_address) +
+ " of " + sec.segname + "," + sec.sectname + " in " +
+ mb.getBufferIdentifier())
+ .str();
+}
+
+static void validateLength(MemoryBufferRef mb, const section_64 &sec,
+ const relocation_info &rel,
+ const std::vector<uint8_t> &validLengths) {
+ if (std::find(validLengths.begin(), validLengths.end(), rel.r_length) !=
+ validLengths.end())
+ return;
+
+ std::string msg = getErrorLocation(mb, sec, rel) + ": relocations of type " +
+ std::to_string(rel.r_type) + " must have r_length of ";
+ bool first = true;
+ for (uint8_t length : validLengths) {
+ if (!first)
+ msg += " or ";
+ first = false;
+ msg += std::to_string(length);
+ }
+ fatal(msg);
+}
+
+uint64_t X86_64::getImplicitAddend(MemoryBufferRef mb, const section_64 &sec,
+ const relocation_info &rel) const {
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ const uint8_t *loc = buf + sec.offset + rel.r_address;
+ switch (rel.r_type) {
+ case X86_64_RELOC_BRANCH:
+ // XXX: ld64 also supports r_length = 0 here but I'm not sure when such a
+ // relocation will actually be generated.
+ validateLength(mb, sec, rel, {2});
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ if (!rel.r_pcrel)
+ fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " +
+ std::to_string(rel.r_type) + " must be pcrel");
+ validateLength(mb, sec, rel, {2});
+ break;
+ case X86_64_RELOC_UNSIGNED:
+ if (rel.r_pcrel)
+ fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " +
+ std::to_string(rel.r_type) + " must not be pcrel");
+ validateLength(mb, sec, rel, {2, 3});
+ break;
+ default:
+ error("TODO: Unhandled relocation type " + std::to_string(rel.r_type));
+ return 0;
+ }
+
+ switch (rel.r_length) {
+ case 0:
+ return *loc;
+ case 1:
+ return read16le(loc);
+ case 2:
+ return read32le(loc);
+ case 3:
+ return read64le(loc);
+ default:
+ llvm_unreachable("invalid r_length");
+ }
+}
+
+void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const {
+ switch (r.type) {
+ case X86_64_RELOC_BRANCH:
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ // These types are only used for pc-relative relocations, so offset by 4
+ // since the RIP has advanced by 4 at this point. This is only valid when
+ // r_length = 2, which is enforced by validateLength().
+ val -= 4;
+ break;
+ case X86_64_RELOC_UNSIGNED:
+ break;
+ default:
+ llvm_unreachable(
+ "getImplicitAddend should have flagged all unhandled relocation types");
+ }
+
+ switch (r.length) {
+ case 0:
+ *loc = val;
+ break;
+ case 1:
+ write16le(loc, val);
+ break;
+ case 2:
+ write32le(loc, val);
+ break;
+ case 3:
+ write64le(loc, val);
+ break;
+ default:
+ llvm_unreachable("invalid r_length");
+ }
+}
+
+// The following methods emit a number of assembly sequences with RIP-relative
+// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing
+// to the next instruction, not the current instruction, so we always have to
+// account for the current instruction's size when calculating offsets.
+// writeRipRelative helps with that.
+//
+// bufAddr: The virtual address corresponding to buf[0].
+// bufOff: The offset within buf of the next instruction.
+// destAddr: The destination address that the current instruction references.
+static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff,
+ uint64_t destAddr) {
+ uint64_t rip = bufAddr + bufOff;
+ // For the instructions we care about, the RIP-relative address is always
+ // stored in the last 4 bytes of the instruction.
+ write32le(buf + bufOff - 4, destAddr - rip);
+}
+
+static constexpr uint8_t stub[] = {
+ 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
+};
+
+void X86_64::writeStub(uint8_t *buf, const DylibSymbol &sym) const {
+ memcpy(buf, stub, 2); // just copy the two nonzero bytes
+ uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
+ writeRipRelative(buf, stubAddr, sizeof(stub),
+ in.lazyPointers->addr + sym.stubsIndex * WordSize);
+}
+
+static constexpr uint8_t stubHelperHeader[] = {
+ 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11
+ 0x41, 0x53, // 0x7: pushq %r11
+ 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip)
+ 0x90, // 0xf: nop
+};
+
+static constexpr uint8_t stubHelperEntry[] = {
+ 0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>
+ 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
+};
+
+void X86_64::writeStubHelperHeader(uint8_t *buf) const {
+ memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
+ writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA());
+ writeRipRelative(buf, in.stubHelper->addr, 0xf,
+ in.got->addr +
+ in.stubHelper->stubBinder->gotIndex * WordSize);
+}
+
+void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
+ uint64_t entryAddr) const {
+ memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
+ write32le(buf + 1, sym.lazyBindOffset);
+ writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry),
+ in.stubHelper->addr);
+}
+
+void X86_64::prepareSymbolRelocation(lld::macho::Symbol &sym,
+ const InputSection *isec, const Reloc &r) {
+ switch (r.type) {
+ case X86_64_RELOC_GOT_LOAD:
+ // TODO: implement mov -> lea relaxation for non-dynamic symbols
+ case X86_64_RELOC_GOT:
+ in.got->addEntry(sym);
+ break;
+ case X86_64_RELOC_BRANCH: {
+ if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
+ in.stubs->addEntry(*dysym);
+ break;
+ }
+ case X86_64_RELOC_UNSIGNED: {
+ if (auto *dysym = dyn_cast<DylibSymbol>(&sym)) {
+ if (r.length != 3) {
+ error("X86_64_RELOC_UNSIGNED referencing the dynamic symbol " +
+ dysym->getName() + " must have r_length = 3");
+ return;
+ }
+ in.binding->addEntry(dysym, isec, r.offset, r.addend);
+ }
+ break;
+ }
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ break;
+ case X86_64_RELOC_SUBTRACTOR:
+ case X86_64_RELOC_TLV:
+ fatal("TODO: handle relocation type " + std::to_string(r.type));
+ break;
+ default:
+ llvm_unreachable("unexpected relocation type");
+ }
+}
+
+uint64_t X86_64::getSymbolVA(const lld::macho::Symbol &sym,
+ uint8_t type) const {
+ switch (type) {
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ return in.got->addr + sym.gotIndex * WordSize;
+ case X86_64_RELOC_BRANCH:
+ if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
+ return in.stubs->addr + dysym->stubsIndex * sizeof(stub);
+ return sym.getVA();
+ case X86_64_RELOC_UNSIGNED:
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ return sym.getVA();
+ case X86_64_RELOC_SUBTRACTOR:
+ case X86_64_RELOC_TLV:
+ fatal("TODO: handle relocation type " + std::to_string(type));
+ default:
+ llvm_unreachable("Unexpected relocation type");
+ }
+}
+
+X86_64::X86_64() {
+ cpuType = CPU_TYPE_X86_64;
+ cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
+
+ stubSize = sizeof(stub);
+ stubHelperHeaderSize = sizeof(stubHelperHeader);
+ stubHelperEntrySize = sizeof(stubHelperEntry);
+}
+
+TargetInfo *macho::createX86_64TargetInfo() {
+ static X86_64 t;
+ return &t;
+}
diff --git a/contrib/llvm-project/lld/MachO/Config.h b/contrib/llvm-project/lld/MachO/Config.h
new file mode 100644
index 000000000000..79812a433563
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Config.h
@@ -0,0 +1,57 @@
+//===- Config.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_MACHO_CONFIG_H
+#define LLD_MACHO_CONFIG_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/TextAPI/MachO/Architecture.h"
+
+#include <vector>
+
+namespace lld {
+namespace macho {
+
+class Symbol;
+struct SymbolPriorityEntry;
+
+struct Configuration {
+ Symbol *entry;
+ bool hasReexports = false;
+ llvm::StringRef installName;
+ llvm::StringRef outputFile;
+ llvm::MachO::Architecture arch;
+ llvm::MachO::HeaderFileType outputType;
+ std::vector<llvm::StringRef> librarySearchPaths;
+ // TODO: use the framework search paths
+ std::vector<llvm::StringRef> frameworkSearchPaths;
+ llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
+};
+
+// The symbol with the highest priority should be ordered first in the output
+// section (modulo input section contiguity constraints). Using priority
+// (highest first) instead of order (lowest first) has the convenient property
+// that the default-constructed zero priority -- for symbols/sections without a
+// user-defined order -- naturally ends up putting them at the end of the
+// output.
+struct SymbolPriorityEntry {
+ // The priority given to a matching symbol, regardless of which object file
+ // it originated from.
+ size_t anyObjectFile = 0;
+ // The priority given to a matching symbol from a particular object file.
+ llvm::DenseMap<llvm::StringRef, size_t> objectFiles;
+};
+
+extern Configuration *config;
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/Driver.cpp b/contrib/llvm-project/lld/MachO/Driver.cpp
new file mode 100644
index 000000000000..2a3b0042162e
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Driver.cpp
@@ -0,0 +1,451 @@
+//===- Driver.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+#include "Config.h"
+#include "InputFiles.h"
+#include "OutputSection.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Target.h"
+#include "Writer.h"
+
+#include "lld/Common/Args.h"
+#include "lld/Common/Driver.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Version.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::sys;
+using namespace llvm::opt;
+using namespace lld;
+using namespace lld::macho;
+
+Configuration *lld::macho::config;
+
+// Create prefix string literals used in Options.td
+#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in Options.td
+static const opt::OptTable::Info optInfo[] = {
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
+ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
+ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
+#include "Options.inc"
+#undef OPTION
+};
+
+MachOOptTable::MachOOptTable() : OptTable(optInfo) {}
+
+opt::InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) {
+ // Make InputArgList from string vectors.
+ unsigned missingIndex;
+ unsigned missingCount;
+ SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
+
+ opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount);
+
+ if (missingCount)
+ error(Twine(args.getArgString(missingIndex)) + ": missing argument");
+
+ for (opt::Arg *arg : args.filtered(OPT_UNKNOWN))
+ error("unknown argument: " + arg->getSpelling());
+ return args;
+}
+
+void MachOOptTable::printHelp(const char *argv0, bool showHidden) const {
+ PrintHelp(lld::outs(), (std::string(argv0) + " [options] file...").c_str(),
+ "LLVM Linker", showHidden);
+ lld::outs() << "\n";
+}
+
+static Optional<std::string> findLibrary(StringRef name) {
+ std::string stub = (llvm::Twine("lib") + name + ".tbd").str();
+ std::string shared = (llvm::Twine("lib") + name + ".dylib").str();
+ std::string archive = (llvm::Twine("lib") + name + ".a").str();
+ llvm::SmallString<260> location;
+
+ for (StringRef dir : config->librarySearchPaths) {
+ for (StringRef library : {stub, shared, archive}) {
+ location = dir;
+ llvm::sys::path::append(location, library);
+ if (fs::exists(location))
+ return location.str().str();
+ }
+ }
+ return {};
+}
+
+static TargetInfo *createTargetInfo(opt::InputArgList &args) {
+ StringRef arch = args.getLastArgValue(OPT_arch, "x86_64");
+ config->arch = llvm::MachO::getArchitectureFromName(
+ args.getLastArgValue(OPT_arch, arch));
+ switch (config->arch) {
+ case llvm::MachO::AK_x86_64:
+ case llvm::MachO::AK_x86_64h:
+ return createX86_64TargetInfo();
+ default:
+ fatal("missing or unsupported -arch " + arch);
+ }
+}
+
+static bool isDirectory(StringRef option, StringRef path) {
+ if (!fs::exists(path)) {
+ warn("directory not found for option -" + option + path);
+ return false;
+ } else if (!fs::is_directory(path)) {
+ warn("option -" + option + path + " references a non-directory path");
+ return false;
+ }
+ return true;
+}
+
+static void getSearchPaths(std::vector<StringRef> &paths, unsigned optionCode,
+ opt::InputArgList &args,
+ const SmallVector<StringRef, 2> &systemPaths) {
+ StringRef optionLetter{(optionCode == OPT_F ? "F" : "L")};
+ for (auto const &path : args::getStrings(args, optionCode)) {
+ if (isDirectory(optionLetter, path))
+ paths.push_back(path);
+ }
+ if (!args.hasArg(OPT_Z) && Triple(sys::getProcessTriple()).isOSDarwin()) {
+ for (auto const &path : systemPaths) {
+ if (isDirectory(optionLetter, path))
+ paths.push_back(path);
+ }
+ }
+}
+
+static void getLibrarySearchPaths(std::vector<StringRef> &paths,
+ opt::InputArgList &args) {
+ getSearchPaths(paths, OPT_L, args, {"/usr/lib", "/usr/local/lib"});
+}
+
+static void getFrameworkSearchPaths(std::vector<StringRef> &paths,
+ opt::InputArgList &args) {
+ getSearchPaths(paths, OPT_F, args,
+ {"/Library/Frameworks", "/System/Library/Frameworks"});
+}
+
+static void addFile(StringRef path) {
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer)
+ return;
+ MemoryBufferRef mbref = *buffer;
+
+ switch (identify_magic(mbref.getBuffer())) {
+ case file_magic::archive: {
+ std::unique_ptr<object::Archive> file = CHECK(
+ object::Archive::create(mbref), path + ": failed to parse archive");
+
+ if (!file->isEmpty() && !file->hasSymbolTable())
+ error(path + ": archive has no index; run ranlib to add one");
+
+ inputFiles.push_back(make<ArchiveFile>(std::move(file)));
+ break;
+ }
+ case file_magic::macho_object:
+ inputFiles.push_back(make<ObjFile>(mbref));
+ break;
+ case file_magic::macho_dynamically_linked_shared_lib:
+ inputFiles.push_back(make<DylibFile>(mbref));
+ break;
+ case file_magic::tapi_file: {
+ llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
+ TextAPIReader::get(mbref);
+ if (!result)
+ return;
+
+ inputFiles.push_back(make<DylibFile>(std::move(*result)));
+ break;
+ }
+ default:
+ error(path + ": unhandled file type");
+ }
+}
+
+static std::array<StringRef, 6> archNames{"arm", "arm64", "i386",
+ "x86_64", "ppc", "ppc64"};
+static bool isArchString(StringRef s) {
+ static DenseSet<StringRef> archNamesSet(archNames.begin(), archNames.end());
+ return archNamesSet.find(s) != archNamesSet.end();
+}
+
+// An order file has one entry per line, in the following format:
+//
+// <arch>:<object file>:<symbol name>
+//
+// <arch> and <object file> are optional. If not specified, then that entry
+// matches any symbol of that name.
+//
+// If a symbol is matched by multiple entries, then it takes the lowest-ordered
+// entry (the one nearest to the front of the list.)
+//
+// The file can also have line comments that start with '#'.
+void parseOrderFile(StringRef path) {
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer) {
+ error("Could not read order file at " + path);
+ return;
+ }
+
+ MemoryBufferRef mbref = *buffer;
+ size_t priority = std::numeric_limits<size_t>::max();
+ for (StringRef rest : args::getLines(mbref)) {
+ StringRef arch, objectFile, symbol;
+
+ std::array<StringRef, 3> fields;
+ uint8_t fieldCount = 0;
+ while (rest != "" && fieldCount < 3) {
+ std::pair<StringRef, StringRef> p = getToken(rest, ": \t\n\v\f\r");
+ StringRef tok = p.first;
+ rest = p.second;
+
+ // Check if we have a comment
+ if (tok == "" || tok[0] == '#')
+ break;
+
+ fields[fieldCount++] = tok;
+ }
+
+ switch (fieldCount) {
+ case 3:
+ arch = fields[0];
+ objectFile = fields[1];
+ symbol = fields[2];
+ break;
+ case 2:
+ (isArchString(fields[0]) ? arch : objectFile) = fields[0];
+ symbol = fields[1];
+ break;
+ case 1:
+ symbol = fields[0];
+ break;
+ case 0:
+ break;
+ default:
+ llvm_unreachable("too many fields in order file");
+ }
+
+ if (!arch.empty()) {
+ if (!isArchString(arch)) {
+ error("invalid arch \"" + arch + "\" in order file: expected one of " +
+ llvm::join(archNames, ", "));
+ continue;
+ }
+
+ // TODO: Update when we extend support for other archs
+ if (arch != "x86_64")
+ continue;
+ }
+
+ if (!objectFile.empty() && !objectFile.endswith(".o")) {
+ error("invalid object file name \"" + objectFile +
+ "\" in order file: should end with .o");
+ continue;
+ }
+
+ if (!symbol.empty()) {
+ SymbolPriorityEntry &entry = config->priorities[symbol];
+ if (!objectFile.empty())
+ entry.objectFiles.insert(std::make_pair(objectFile, priority));
+ else
+ entry.anyObjectFile = std::max(entry.anyObjectFile, priority);
+ }
+
+ --priority;
+ }
+}
+
+// We expect sub-library names of the form "libfoo", which will match a dylib
+// with a path of .*/libfoo.dylib.
+static bool markSubLibrary(StringRef searchName) {
+ for (InputFile *file : inputFiles) {
+ if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
+ StringRef filename = path::filename(dylibFile->getName());
+ if (filename.consume_front(searchName) && filename == ".dylib") {
+ dylibFile->reexport = true;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void handlePlatformVersion(const opt::Arg *arg) {
+ // TODO: implementation coming very soon ...
+}
+
+static void warnIfDeprecatedOption(const opt::Option &opt) {
+ if (!opt.getGroup().isValid())
+ return;
+ if (opt.getGroup().getID() == OPT_grp_deprecated) {
+ warn("Option `" + opt.getPrefixedName() + "' is deprecated in ld64:");
+ warn(opt.getHelpText());
+ }
+}
+
+static void warnIfUnimplementedOption(const opt::Option &opt) {
+ if (!opt.getGroup().isValid())
+ return;
+ switch (opt.getGroup().getID()) {
+ case OPT_grp_deprecated:
+ // warn about deprecated options elsewhere
+ break;
+ case OPT_grp_undocumented:
+ warn("Option `" + opt.getPrefixedName() +
+ "' is undocumented. Should lld implement it?");
+ break;
+ case OPT_grp_obsolete:
+ warn("Option `" + opt.getPrefixedName() +
+ "' is obsolete. Please modernize your usage.");
+ break;
+ case OPT_grp_ignored:
+ warn("Option `" + opt.getPrefixedName() + "' is ignored.");
+ break;
+ default:
+ warn("Option `" + opt.getPrefixedName() +
+ "' is not yet implemented. Stay tuned...");
+ break;
+ }
+}
+
+bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
+ raw_ostream &stdoutOS, raw_ostream &stderrOS) {
+ lld::stdoutOS = &stdoutOS;
+ lld::stderrOS = &stderrOS;
+
+ stderrOS.enable_colors(stderrOS.has_colors());
+ // TODO: Set up error handler properly, e.g. the errorLimitExceededMsg
+
+ MachOOptTable parser;
+ opt::InputArgList args = parser.parse(argsArr.slice(1));
+
+ if (args.hasArg(OPT_help_hidden)) {
+ parser.printHelp(argsArr[0], /*showHidden=*/true);
+ return true;
+ } else if (args.hasArg(OPT_help)) {
+ parser.printHelp(argsArr[0], /*showHidden=*/false);
+ return true;
+ }
+
+ config = make<Configuration>();
+ symtab = make<SymbolTable>();
+ target = createTargetInfo(args);
+
+ config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"));
+ config->outputFile = args.getLastArgValue(OPT_o, "a.out");
+ config->installName =
+ args.getLastArgValue(OPT_install_name, config->outputFile);
+ getLibrarySearchPaths(config->librarySearchPaths, args);
+ getFrameworkSearchPaths(config->frameworkSearchPaths, args);
+ config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE;
+
+ if (args.hasArg(OPT_v)) {
+ message(getLLDVersion());
+ message(StringRef("Library search paths:") +
+ (config->librarySearchPaths.size()
+ ? "\n\t" + llvm::join(config->librarySearchPaths, "\n\t")
+ : ""));
+ message(StringRef("Framework search paths:") +
+ (config->frameworkSearchPaths.size()
+ ? "\n\t" + llvm::join(config->frameworkSearchPaths, "\n\t")
+ : ""));
+ freeArena();
+ return !errorCount();
+ }
+
+ for (const auto &arg : args) {
+ const auto &opt = arg->getOption();
+ warnIfDeprecatedOption(opt);
+ switch (arg->getOption().getID()) {
+ case OPT_INPUT:
+ addFile(arg->getValue());
+ break;
+ case OPT_l: {
+ StringRef name = arg->getValue();
+ if (Optional<std::string> path = findLibrary(name)) {
+ addFile(*path);
+ break;
+ }
+ error("library not found for -l" + name);
+ break;
+ }
+ case OPT_platform_version:
+ handlePlatformVersion(arg);
+ break;
+ case OPT_o:
+ case OPT_dylib:
+ case OPT_e:
+ case OPT_L:
+ case OPT_Z:
+ case OPT_arch:
+ // handled elsewhere
+ break;
+ default:
+ warnIfUnimplementedOption(opt);
+ break;
+ }
+ }
+
+ // Now that all dylibs have been loaded, search for those that should be
+ // re-exported.
+ for (opt::Arg *arg : args.filtered(OPT_sub_library)) {
+ config->hasReexports = true;
+ StringRef searchName = arg->getValue();
+ if (!markSubLibrary(searchName))
+ error("-sub_library " + searchName + " does not match a supplied dylib");
+ }
+
+ StringRef orderFile = args.getLastArgValue(OPT_order_file);
+ if (!orderFile.empty())
+ parseOrderFile(orderFile);
+
+ if (config->outputType == MH_EXECUTE && !isa<Defined>(config->entry)) {
+ error("undefined symbol: " + config->entry->getName());
+ return false;
+ }
+
+ createSyntheticSections();
+
+ // Initialize InputSections.
+ for (InputFile *file : inputFiles) {
+ for (SubsectionMap &map : file->subsections) {
+ for (auto &p : map) {
+ InputSection *isec = p.second;
+ inputSections.push_back(isec);
+ }
+ }
+ }
+
+ // Write to an output file.
+ writeResult();
+
+ if (canExitEarly)
+ exitLld(errorCount() ? 1 : 0);
+
+ freeArena();
+ return !errorCount();
+}
diff --git a/contrib/llvm-project/lld/MachO/Driver.h b/contrib/llvm-project/lld/MachO/Driver.h
new file mode 100644
index 000000000000..2233740d1db8
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Driver.h
@@ -0,0 +1,36 @@
+//===- Driver.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_MACHO_DRIVER_H
+#define LLD_MACHO_DRIVER_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/Option/OptTable.h"
+
+namespace lld {
+namespace macho {
+
+class MachOOptTable : public llvm::opt::OptTable {
+public:
+ MachOOptTable();
+ llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
+ void printHelp(const char *argv0, bool showHidden) const;
+};
+
+// Create enum with OPT_xxx values for each option in Options.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/ExportTrie.cpp b/contrib/llvm-project/lld/MachO/ExportTrie.cpp
new file mode 100644
index 000000000000..7cc81bcfd5f1
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/ExportTrie.cpp
@@ -0,0 +1,283 @@
+//===- ExportTrie.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a partial implementation of the Mach-O export trie format. It's
+// essentially a symbol table encoded as a compressed prefix trie, meaning that
+// the common prefixes of each symbol name are shared for a more compact
+// representation. The prefixes are stored on the edges of the trie, and one
+// edge can represent multiple characters. For example, given two exported
+// symbols _bar and _baz, we will have a trie like this (terminal nodes are
+// marked with an asterisk):
+//
+// +-+-+
+// | | // root node
+// +-+-+
+// |
+// | _ba
+// |
+// +-+-+
+// | |
+// +-+-+
+// r / \ z
+// / \
+// +-+-+ +-+-+
+// | * | | * |
+// +-+-+ +-+-+
+//
+// More documentation of the format can be found in
+// llvm/tools/obj2yaml/macho2yaml.cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExportTrie.h"
+#include "Symbols.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/LEB128.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+namespace {
+
+struct Edge {
+ Edge(StringRef s, TrieNode *node) : substring(s), child(node) {}
+
+ StringRef substring;
+ struct TrieNode *child;
+};
+
+struct ExportInfo {
+ uint64_t address;
+ // TODO: Add proper support for re-exports & stub-and-resolver flags.
+};
+
+} // namespace
+
+struct macho::TrieNode {
+ std::vector<Edge> edges;
+ Optional<ExportInfo> info;
+ // Estimated offset from the start of the serialized trie to the current node.
+ // This will converge to the true offset when updateOffset() is run to a
+ // fixpoint.
+ size_t offset = 0;
+
+ // Returns whether the new estimated offset differs from the old one.
+ bool updateOffset(size_t &nextOffset);
+ void writeTo(uint8_t *buf) const;
+};
+
+bool TrieNode::updateOffset(size_t &nextOffset) {
+ // Size of the whole node (including the terminalSize and the outgoing edges.)
+ // In contrast, terminalSize only records the size of the other data in the
+ // node.
+ size_t nodeSize;
+ if (info) {
+ uint64_t flags = 0;
+ uint32_t terminalSize =
+ getULEB128Size(flags) + getULEB128Size(info->address);
+ // Overall node size so far is the uleb128 size of the length of the symbol
+ // info + the symbol info itself.
+ nodeSize = terminalSize + getULEB128Size(terminalSize);
+ } else {
+ nodeSize = 1; // Size of terminalSize (which has a value of 0)
+ }
+ // Compute size of all child edges.
+ ++nodeSize; // Byte for number of children.
+ for (Edge &edge : edges) {
+ nodeSize += edge.substring.size() + 1 // String length.
+ + getULEB128Size(edge.child->offset); // Offset len.
+ }
+ // On input, 'nextOffset' is the new preferred location for this node.
+ bool result = (offset != nextOffset);
+ // Store new location in node object for use by parents.
+ offset = nextOffset;
+ nextOffset += nodeSize;
+ return result;
+}
+
+void TrieNode::writeTo(uint8_t *buf) const {
+ buf += offset;
+ if (info) {
+ // TrieNodes with Symbol info: size, flags address
+ uint64_t flags = 0; // TODO: emit proper flags
+ uint32_t terminalSize =
+ getULEB128Size(flags) + getULEB128Size(info->address);
+ buf += encodeULEB128(terminalSize, buf);
+ buf += encodeULEB128(flags, buf);
+ buf += encodeULEB128(info->address, buf);
+ } else {
+ // TrieNode with no Symbol info.
+ *buf++ = 0; // terminalSize
+ }
+ // Add number of children. TODO: Handle case where we have more than 256.
+ assert(edges.size() < 256);
+ *buf++ = edges.size();
+ // Append each child edge substring and node offset.
+ for (const Edge &edge : edges) {
+ memcpy(buf, edge.substring.data(), edge.substring.size());
+ buf += edge.substring.size();
+ *buf++ = '\0';
+ buf += encodeULEB128(edge.child->offset, buf);
+ }
+}
+
+TrieNode *TrieBuilder::makeNode() {
+ auto *node = make<TrieNode>();
+ nodes.emplace_back(node);
+ return node;
+}
+
+static int charAt(const Symbol *sym, size_t pos) {
+ StringRef str = sym->getName();
+ if (pos >= str.size())
+ return -1;
+ return str[pos];
+}
+
+// Build the trie by performing a three-way radix quicksort: We start by sorting
+// the strings by their first characters, then sort the strings with the same
+// first characters by their second characters, and so on recursively. Each
+// time the prefixes diverge, we add a node to the trie.
+//
+// node: The most recently created node along this path in the trie (i.e.
+// the furthest from the root.)
+// lastPos: The prefix length of the most recently created node, i.e. the number
+// of characters along its path from the root.
+// pos: The string index we are currently sorting on. Note that each symbol
+// S contained in vec has the same prefix S[0...pos).
+void TrieBuilder::sortAndBuild(MutableArrayRef<const Symbol *> vec,
+ TrieNode *node, size_t lastPos, size_t pos) {
+tailcall:
+ if (vec.empty())
+ return;
+
+ // Partition items so that items in [0, i) are less than the pivot,
+ // [i, j) are the same as the pivot, and [j, vec.size()) are greater than
+ // the pivot.
+ const Symbol *pivotSymbol = vec[vec.size() / 2];
+ int pivot = charAt(pivotSymbol, pos);
+ size_t i = 0;
+ size_t j = vec.size();
+ for (size_t k = 0; k < j;) {
+ int c = charAt(vec[k], pos);
+ if (c < pivot)
+ std::swap(vec[i++], vec[k++]);
+ else if (c > pivot)
+ std::swap(vec[--j], vec[k]);
+ else
+ k++;
+ }
+
+ bool isTerminal = pivot == -1;
+ bool prefixesDiverge = i != 0 || j != vec.size();
+ if (lastPos != pos && (isTerminal || prefixesDiverge)) {
+ TrieNode *newNode = makeNode();
+ node->edges.emplace_back(pivotSymbol->getName().slice(lastPos, pos),
+ newNode);
+ node = newNode;
+ lastPos = pos;
+ }
+
+ sortAndBuild(vec.slice(0, i), node, lastPos, pos);
+ sortAndBuild(vec.slice(j), node, lastPos, pos);
+
+ if (isTerminal) {
+ assert(j - i == 1); // no duplicate symbols
+ node->info = {pivotSymbol->getVA()};
+ } else {
+ // This is the tail-call-optimized version of the following:
+ // sortAndBuild(vec.slice(i, j - i), node, lastPos, pos + 1);
+ vec = vec.slice(i, j - i);
+ ++pos;
+ goto tailcall;
+ }
+}
+
+size_t TrieBuilder::build() {
+ if (exported.empty())
+ return 0;
+
+ TrieNode *root = makeNode();
+ sortAndBuild(exported, root, 0, 0);
+
+ // Assign each node in the vector an offset in the trie stream, iterating
+ // until all uleb128 sizes have stabilized.
+ size_t offset;
+ bool more;
+ do {
+ offset = 0;
+ more = false;
+ for (TrieNode *node : nodes)
+ more |= node->updateOffset(offset);
+ } while (more);
+
+ return offset;
+}
+
+void TrieBuilder::writeTo(uint8_t *buf) const {
+ for (TrieNode *node : nodes)
+ node->writeTo(buf);
+}
+
+namespace {
+
+// Parse a serialized trie and invoke a callback for each entry.
+class TrieParser {
+public:
+ TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback)
+ : start(buf), end(start + size), callback(callback) {}
+
+ void parse(const uint8_t *buf, const Twine &cumulativeString);
+
+ void parse() { parse(start, ""); }
+
+ const uint8_t *start;
+ const uint8_t *end;
+ const TrieEntryCallback &callback;
+};
+
+} // namespace
+
+void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) {
+ if (buf >= end)
+ fatal("Node offset points outside export section");
+
+ unsigned ulebSize;
+ uint64_t terminalSize = decodeULEB128(buf, &ulebSize);
+ buf += ulebSize;
+ uint64_t flags = 0;
+ size_t offset;
+ if (terminalSize != 0) {
+ flags = decodeULEB128(buf, &ulebSize);
+ callback(cumulativeString, flags);
+ }
+ buf += terminalSize;
+ uint8_t numEdges = *buf++;
+ for (uint8_t i = 0; i < numEdges; ++i) {
+ const char *cbuf = reinterpret_cast<const char *>(buf);
+ StringRef substring = StringRef(cbuf, strnlen(cbuf, end - buf));
+ buf += substring.size() + 1;
+ offset = decodeULEB128(buf, &ulebSize);
+ buf += ulebSize;
+ parse(start + offset, cumulativeString + substring);
+ }
+}
+
+void macho::parseTrie(const uint8_t *buf, size_t size,
+ const TrieEntryCallback &callback) {
+ if (size == 0)
+ return;
+
+ TrieParser(buf, size, callback).parse();
+}
diff --git a/contrib/llvm-project/lld/MachO/ExportTrie.h b/contrib/llvm-project/lld/MachO/ExportTrie.h
new file mode 100644
index 000000000000..2bd8c33db9a0
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/ExportTrie.h
@@ -0,0 +1,47 @@
+//===- ExportTrie.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_MACHO_EXPORT_TRIE_H
+#define LLD_MACHO_EXPORT_TRIE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+
+#include <vector>
+
+namespace lld {
+namespace macho {
+
+struct TrieNode;
+class Symbol;
+
+class TrieBuilder {
+public:
+ void addSymbol(const Symbol &sym) { exported.push_back(&sym); }
+ // Returns the size in bytes of the serialized trie.
+ size_t build();
+ void writeTo(uint8_t *buf) const;
+
+private:
+ TrieNode *makeNode();
+ void sortAndBuild(llvm::MutableArrayRef<const Symbol *> vec, TrieNode *node,
+ size_t lastPos, size_t pos);
+
+ std::vector<const Symbol *> exported;
+ std::vector<TrieNode *> nodes;
+};
+
+using TrieEntryCallback =
+ llvm::function_ref<void(const llvm::Twine & /*name*/, uint64_t /*flags*/)>;
+
+void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &);
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/InputFiles.cpp b/contrib/llvm-project/lld/MachO/InputFiles.cpp
new file mode 100644
index 000000000000..46fe82f98822
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/InputFiles.cpp
@@ -0,0 +1,433 @@
+//===- InputFiles.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to parse Mach-O object files. In this comment,
+// we describe the Mach-O file structure and how we parse it.
+//
+// Mach-O is not very different from ELF or COFF. The notion of symbols,
+// sections and relocations exists in Mach-O as it does in ELF and COFF.
+//
+// Perhaps the notion that is new to those who know ELF/COFF is "subsections".
+// In ELF/COFF, sections are an atomic unit of data copied from input files to
+// output files. When we merge or garbage-collect sections, we treat each
+// section as an atomic unit. In Mach-O, that's not the case. Sections can
+// consist of multiple subsections, and subsections are a unit of merging and
+// garbage-collecting. Therefore, Mach-O's subsections are more similar to
+// ELF/COFF's sections than Mach-O's sections are.
+//
+// A section can have multiple symbols. A symbol that does not have the
+// N_ALT_ENTRY attribute indicates a beginning of a subsection. Therefore, by
+// definition, a symbol is always present at the beginning of each subsection. A
+// symbol with N_ALT_ENTRY attribute does not start a new subsection and can
+// point to a middle of a subsection.
+//
+// The notion of subsections also affects how relocations are represented in
+// Mach-O. All references within a section need to be explicitly represented as
+// relocations if they refer to different subsections, because we obviously need
+// to fix up addresses if subsections are laid out in an output file differently
+// than they were in object files. To represent that, Mach-O relocations can
+// refer to an unnamed location via its address. Scattered relocations (those
+// with the R_SCATTERED bit set) always refer to unnamed locations.
+// Non-scattered relocations refer to an unnamed location if r_extern is not set
+// and r_symbolnum is zero.
+//
+// Without the above differences, I think you can use your knowledge about ELF
+// and COFF for Mach-O.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Config.h"
+#include "ExportTrie.h"
+#include "InputSection.h"
+#include "MachOStructs.h"
+#include "OutputSection.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Target.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::support::endian;
+using namespace llvm::sys;
+using namespace lld;
+using namespace lld::macho;
+
+std::vector<InputFile *> macho::inputFiles;
+
+// Open a given file path and return it as a memory-mapped file.
+Optional<MemoryBufferRef> macho::readFile(StringRef path) {
+ // Open a file.
+ auto mbOrErr = MemoryBuffer::getFile(path);
+ if (auto ec = mbOrErr.getError()) {
+ error("cannot open " + path + ": " + ec.message());
+ return None;
+ }
+
+ std::unique_ptr<MemoryBuffer> &mb = *mbOrErr;
+ MemoryBufferRef mbref = mb->getMemBufferRef();
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take mb ownership
+
+ // If this is a regular non-fat file, return it.
+ const char *buf = mbref.getBufferStart();
+ auto *hdr = reinterpret_cast<const MachO::fat_header *>(buf);
+ if (read32be(&hdr->magic) != MachO::FAT_MAGIC)
+ return mbref;
+
+ // Object files and archive files may be fat files, which contains
+ // multiple real files for different CPU ISAs. Here, we search for a
+ // file that matches with the current link target and returns it as
+ // a MemoryBufferRef.
+ auto *arch = reinterpret_cast<const MachO::fat_arch *>(buf + sizeof(*hdr));
+
+ for (uint32_t i = 0, n = read32be(&hdr->nfat_arch); i < n; ++i) {
+ if (reinterpret_cast<const char *>(arch + i + 1) >
+ buf + mbref.getBufferSize()) {
+ error(path + ": fat_arch struct extends beyond end of file");
+ return None;
+ }
+
+ if (read32be(&arch[i].cputype) != target->cpuType ||
+ read32be(&arch[i].cpusubtype) != target->cpuSubtype)
+ continue;
+
+ uint32_t offset = read32be(&arch[i].offset);
+ uint32_t size = read32be(&arch[i].size);
+ if (offset + size > mbref.getBufferSize())
+ error(path + ": slice extends beyond end of file");
+ return MemoryBufferRef(StringRef(buf + offset, size), path.copy(bAlloc));
+ }
+
+ error("unable to find matching architecture in " + path);
+ return None;
+}
+
+static const load_command *findCommand(const mach_header_64 *hdr,
+ uint32_t type) {
+ const uint8_t *p =
+ reinterpret_cast<const uint8_t *>(hdr) + sizeof(mach_header_64);
+
+ for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
+ auto *cmd = reinterpret_cast<const load_command *>(p);
+ if (cmd->cmd == type)
+ return cmd;
+ p += cmd->cmdsize;
+ }
+ return nullptr;
+}
+
+void InputFile::parseSections(ArrayRef<section_64> sections) {
+ subsections.reserve(sections.size());
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+
+ for (const section_64 &sec : sections) {
+ InputSection *isec = make<InputSection>();
+ isec->file = this;
+ isec->name = StringRef(sec.sectname, strnlen(sec.sectname, 16));
+ isec->segname = StringRef(sec.segname, strnlen(sec.segname, 16));
+ isec->data = {isZeroFill(sec.flags) ? nullptr : buf + sec.offset,
+ static_cast<size_t>(sec.size)};
+ if (sec.align >= 32)
+ error("alignment " + std::to_string(sec.align) + " of section " +
+ isec->name + " is too large");
+ else
+ isec->align = 1 << sec.align;
+ isec->flags = sec.flags;
+ subsections.push_back({{0, isec}});
+ }
+}
+
+// Find the subsection corresponding to the greatest section offset that is <=
+// that of the given offset.
+//
+// offset: an offset relative to the start of the original InputSection (before
+// any subsection splitting has occurred). It will be updated to represent the
+// same location as an offset relative to the start of the containing
+// subsection.
+static InputSection *findContainingSubsection(SubsectionMap &map,
+ uint32_t *offset) {
+ auto it = std::prev(map.upper_bound(*offset));
+ *offset -= it->first;
+ return it->second;
+}
+
+void InputFile::parseRelocations(const section_64 &sec,
+ SubsectionMap &subsecMap) {
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ ArrayRef<any_relocation_info> relInfos(
+ reinterpret_cast<const any_relocation_info *>(buf + sec.reloff),
+ sec.nreloc);
+
+ for (const any_relocation_info &anyRel : relInfos) {
+ if (anyRel.r_word0 & R_SCATTERED)
+ fatal("TODO: Scattered relocations not supported");
+
+ auto rel = reinterpret_cast<const relocation_info &>(anyRel);
+
+ Reloc r;
+ r.type = rel.r_type;
+ r.pcrel = rel.r_pcrel;
+ r.length = rel.r_length;
+ uint64_t rawAddend = target->getImplicitAddend(mb, sec, rel);
+
+ if (rel.r_extern) {
+ r.target = symbols[rel.r_symbolnum];
+ r.addend = rawAddend;
+ } else {
+ if (rel.r_symbolnum == 0 || rel.r_symbolnum > subsections.size())
+ fatal("invalid section index in relocation for offset " +
+ std::to_string(r.offset) + " in section " + sec.sectname +
+ " of " + getName());
+
+ SubsectionMap &targetSubsecMap = subsections[rel.r_symbolnum - 1];
+ const section_64 &targetSec = sectionHeaders[rel.r_symbolnum - 1];
+ uint32_t targetOffset;
+ if (rel.r_pcrel) {
+ // The implicit addend for pcrel section relocations is the pcrel offset
+ // in terms of the addresses in the input file. Here we adjust it so
+ // that it describes the offset from the start of the target section.
+ // TODO: The offset of 4 is probably not right for ARM64, nor for
+ // relocations with r_length != 2.
+ targetOffset =
+ sec.addr + rel.r_address + 4 + rawAddend - targetSec.addr;
+ } else {
+ // The addend for a non-pcrel relocation is its absolute address.
+ targetOffset = rawAddend - targetSec.addr;
+ }
+ r.target = findContainingSubsection(targetSubsecMap, &targetOffset);
+ r.addend = targetOffset;
+ }
+
+ r.offset = rel.r_address;
+ InputSection *subsec = findContainingSubsection(subsecMap, &r.offset);
+ subsec->relocs.push_back(r);
+ }
+}
+
+void InputFile::parseSymbols(ArrayRef<structs::nlist_64> nList,
+ const char *strtab, bool subsectionsViaSymbols) {
+ // resize(), not reserve(), because we are going to create N_ALT_ENTRY symbols
+ // out-of-sequence.
+ symbols.resize(nList.size());
+ std::vector<size_t> altEntrySymIdxs;
+
+ auto createDefined = [&](const structs::nlist_64 &sym, InputSection *isec,
+ uint32_t value) -> Symbol * {
+ StringRef name = strtab + sym.n_strx;
+ if (sym.n_type & N_EXT)
+ // Global defined symbol
+ return symtab->addDefined(name, isec, value);
+ else
+ // Local defined symbol
+ return make<Defined>(name, isec, value);
+ };
+
+ for (size_t i = 0, n = nList.size(); i < n; ++i) {
+ const structs::nlist_64 &sym = nList[i];
+
+ // Undefined symbol
+ if (!sym.n_sect) {
+ StringRef name = strtab + sym.n_strx;
+ symbols[i] = symtab->addUndefined(name);
+ continue;
+ }
+
+ const section_64 &sec = sectionHeaders[sym.n_sect - 1];
+ SubsectionMap &subsecMap = subsections[sym.n_sect - 1];
+ uint64_t offset = sym.n_value - sec.addr;
+
+ // If the input file does not use subsections-via-symbols, all symbols can
+ // use the same subsection. Otherwise, we must split the sections along
+ // symbol boundaries.
+ if (!subsectionsViaSymbols) {
+ symbols[i] = createDefined(sym, subsecMap[0], offset);
+ continue;
+ }
+
+ // nList entries aren't necessarily arranged in address order. Therefore,
+ // we can't create alt-entry symbols at this point because a later symbol
+ // may split its section, which may affect which subsection the alt-entry
+ // symbol is assigned to. So we need to handle them in a second pass below.
+ if (sym.n_desc & N_ALT_ENTRY) {
+ altEntrySymIdxs.push_back(i);
+ continue;
+ }
+
+ // Find the subsection corresponding to the greatest section offset that is
+ // <= that of the current symbol. The subsection that we find either needs
+ // to be used directly or split in two.
+ uint32_t firstSize = offset;
+ InputSection *firstIsec = findContainingSubsection(subsecMap, &firstSize);
+
+ if (firstSize == 0) {
+ // Alias of an existing symbol, or the first symbol in the section. These
+ // are handled by reusing the existing section.
+ symbols[i] = createDefined(sym, firstIsec, 0);
+ continue;
+ }
+
+ // We saw a symbol definition at a new offset. Split the section into two
+ // subsections. The new symbol uses the second subsection.
+ auto *secondIsec = make<InputSection>(*firstIsec);
+ secondIsec->data = firstIsec->data.slice(firstSize);
+ firstIsec->data = firstIsec->data.slice(0, firstSize);
+ // TODO: ld64 appears to preserve the original alignment as well as each
+ // subsection's offset from the last aligned address. We should consider
+ // emulating that behavior.
+ secondIsec->align = MinAlign(firstIsec->align, offset);
+
+ subsecMap[offset] = secondIsec;
+ // By construction, the symbol will be at offset zero in the new section.
+ symbols[i] = createDefined(sym, secondIsec, 0);
+ }
+
+ for (size_t idx : altEntrySymIdxs) {
+ const structs::nlist_64 &sym = nList[idx];
+ SubsectionMap &subsecMap = subsections[sym.n_sect - 1];
+ uint32_t off = sym.n_value - sectionHeaders[sym.n_sect - 1].addr;
+ InputSection *subsec = findContainingSubsection(subsecMap, &off);
+ symbols[idx] = createDefined(sym, subsec, off);
+ }
+}
+
+ObjFile::ObjFile(MemoryBufferRef mb) : InputFile(ObjKind, mb) {
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
+
+ if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) {
+ auto *c = reinterpret_cast<const segment_command_64 *>(cmd);
+ sectionHeaders = ArrayRef<section_64>{
+ reinterpret_cast<const section_64 *>(c + 1), c->nsects};
+ parseSections(sectionHeaders);
+ }
+
+ // TODO: Error on missing LC_SYMTAB?
+ if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) {
+ auto *c = reinterpret_cast<const symtab_command *>(cmd);
+ ArrayRef<structs::nlist_64> nList(
+ reinterpret_cast<const structs::nlist_64 *>(buf + c->symoff), c->nsyms);
+ const char *strtab = reinterpret_cast<const char *>(buf) + c->stroff;
+ bool subsectionsViaSymbols = hdr->flags & MH_SUBSECTIONS_VIA_SYMBOLS;
+ parseSymbols(nList, strtab, subsectionsViaSymbols);
+ }
+
+ // The relocations may refer to the symbols, so we parse them after we have
+ // parsed all the symbols.
+ for (size_t i = 0, n = subsections.size(); i < n; ++i)
+ parseRelocations(sectionHeaders[i], subsections[i]);
+}
+
+DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella)
+ : InputFile(DylibKind, mb) {
+ if (umbrella == nullptr)
+ umbrella = this;
+
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
+
+ // Initialize dylibName.
+ if (const load_command *cmd = findCommand(hdr, LC_ID_DYLIB)) {
+ auto *c = reinterpret_cast<const dylib_command *>(cmd);
+ dylibName = reinterpret_cast<const char *>(cmd) + read32le(&c->dylib.name);
+ } else {
+ error("dylib " + getName() + " missing LC_ID_DYLIB load command");
+ return;
+ }
+
+ // Initialize symbols.
+ if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) {
+ auto *c = reinterpret_cast<const dyld_info_command *>(cmd);
+ parseTrie(buf + c->export_off, c->export_size,
+ [&](const Twine &name, uint64_t flags) {
+ symbols.push_back(symtab->addDylib(saver.save(name), umbrella));
+ });
+ } else {
+ error("LC_DYLD_INFO_ONLY not found in " + getName());
+ return;
+ }
+
+ if (hdr->flags & MH_NO_REEXPORTED_DYLIBS)
+ return;
+
+ const uint8_t *p =
+ reinterpret_cast<const uint8_t *>(hdr) + sizeof(mach_header_64);
+ for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
+ auto *cmd = reinterpret_cast<const load_command *>(p);
+ p += cmd->cmdsize;
+ if (cmd->cmd != LC_REEXPORT_DYLIB)
+ continue;
+
+ auto *c = reinterpret_cast<const dylib_command *>(cmd);
+ StringRef reexportPath =
+ reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
+ // TODO: Expand @loader_path, @executable_path etc in reexportPath
+ Optional<MemoryBufferRef> buffer = readFile(reexportPath);
+ if (!buffer) {
+ error("unable to read re-exported dylib at " + reexportPath);
+ return;
+ }
+ reexported.push_back(make<DylibFile>(*buffer, umbrella));
+ }
+}
+
+DylibFile::DylibFile(std::shared_ptr<llvm::MachO::InterfaceFile> interface,
+ DylibFile *umbrella)
+ : InputFile(DylibKind, MemoryBufferRef()) {
+ if (umbrella == nullptr)
+ umbrella = this;
+
+ dylibName = saver.save(interface->getInstallName());
+ // TODO(compnerd) filter out symbols based on the target platform
+ for (const auto symbol : interface->symbols())
+ if (symbol->getArchitectures().has(config->arch))
+ symbols.push_back(
+ symtab->addDylib(saver.save(symbol->getName()), umbrella));
+ // TODO(compnerd) properly represent the hierarchy of the documents as it is
+ // in theory possible to have re-exported dylibs from re-exported dylibs which
+ // should be parent'ed to the child.
+ for (auto document : interface->documents())
+ reexported.push_back(make<DylibFile>(document, umbrella));
+}
+
+ArchiveFile::ArchiveFile(std::unique_ptr<llvm::object::Archive> &&f)
+ : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {
+ for (const object::Archive::Symbol &sym : file->symbols())
+ symtab->addLazy(sym.getName(), this, sym);
+}
+
+void ArchiveFile::fetch(const object::Archive::Symbol &sym) {
+ object::Archive::Child c =
+ CHECK(sym.getMember(), toString(this) +
+ ": could not get the member for symbol " +
+ sym.getName());
+
+ if (!seen.insert(c.getChildOffset()).second)
+ return;
+
+ MemoryBufferRef mb =
+ CHECK(c.getMemoryBufferRef(),
+ toString(this) +
+ ": could not get the buffer for the member defining symbol " +
+ sym.getName());
+ auto file = make<ObjFile>(mb);
+ symbols.insert(symbols.end(), file->symbols.begin(), file->symbols.end());
+ subsections.insert(subsections.end(), file->subsections.begin(),
+ file->subsections.end());
+}
+
+// Returns "<internal>" or "baz.o".
+std::string lld::toString(const InputFile *file) {
+ return file ? std::string(file->getName()) : "<internal>";
+}
diff --git a/contrib/llvm-project/lld/MachO/InputFiles.h b/contrib/llvm-project/lld/MachO/InputFiles.h
new file mode 100644
index 000000000000..bc5ad86ccaa9
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/InputFiles.h
@@ -0,0 +1,121 @@
+//===- InputFiles.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_MACHO_INPUT_FILES_H
+#define LLD_MACHO_INPUT_FILES_H
+
+#include "MachOStructs.h"
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/TextAPI/MachO/InterfaceFile.h"
+#include "llvm/TextAPI/MachO/TextAPIReader.h"
+
+#include <map>
+#include <vector>
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class Symbol;
+struct Reloc;
+
+// If .subsections_via_symbols is set, each InputSection will be split along
+// symbol boundaries. The keys of a SubsectionMap represent the offsets of
+// each subsection from the start of the original pre-split InputSection.
+using SubsectionMap = std::map<uint32_t, InputSection *>;
+
+class InputFile {
+public:
+ enum Kind {
+ ObjKind,
+ DylibKind,
+ ArchiveKind,
+ };
+
+ virtual ~InputFile() = default;
+ Kind kind() const { return fileKind; }
+ StringRef getName() const { return mb.getBufferIdentifier(); }
+
+ MemoryBufferRef mb;
+ std::vector<Symbol *> symbols;
+ ArrayRef<llvm::MachO::section_64> sectionHeaders;
+ std::vector<SubsectionMap> subsections;
+
+protected:
+ InputFile(Kind kind, MemoryBufferRef mb) : mb(mb), fileKind(kind) {}
+
+ void parseSections(ArrayRef<llvm::MachO::section_64>);
+
+ void parseSymbols(ArrayRef<lld::structs::nlist_64> nList, const char *strtab,
+ bool subsectionsViaSymbols);
+
+ void parseRelocations(const llvm::MachO::section_64 &, SubsectionMap &);
+
+private:
+ const Kind fileKind;
+};
+
+// .o file
+class ObjFile : public InputFile {
+public:
+ explicit ObjFile(MemoryBufferRef mb);
+ static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
+};
+
+// .dylib file
+class DylibFile : public InputFile {
+public:
+ explicit DylibFile(std::shared_ptr<llvm::MachO::InterfaceFile> interface,
+ DylibFile *umbrella = nullptr);
+
+ // Mach-O dylibs can re-export other dylibs as sub-libraries, meaning that the
+ // symbols in those sub-libraries will be available under the umbrella
+ // library's namespace. Those sub-libraries can also have their own
+ // re-exports. When loading a re-exported dylib, `umbrella` should be set to
+ // the root dylib to ensure symbols in the child library are correctly bound
+ // to the root. On the other hand, if a dylib is being directly loaded
+ // (through an -lfoo flag), then `umbrella` should be a nullptr.
+ explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella = nullptr);
+
+ static bool classof(const InputFile *f) { return f->kind() == DylibKind; }
+
+ StringRef dylibName;
+ uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel
+ bool reexport = false;
+ std::vector<DylibFile *> reexported;
+};
+
+// .a file
+class ArchiveFile : public InputFile {
+public:
+ explicit ArchiveFile(std::unique_ptr<llvm::object::Archive> &&file);
+ static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
+ void fetch(const llvm::object::Archive::Symbol &sym);
+
+private:
+ std::unique_ptr<llvm::object::Archive> file;
+ // Keep track of children fetched from the archive by tracking
+ // which address offsets have been fetched already.
+ llvm::DenseSet<uint64_t> seen;
+};
+
+extern std::vector<InputFile *> inputFiles;
+
+llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+
+} // namespace macho
+
+std::string toString(const macho::InputFile *file);
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/InputSection.cpp b/contrib/llvm-project/lld/MachO/InputSection.cpp
new file mode 100644
index 000000000000..72d489283051
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/InputSection.cpp
@@ -0,0 +1,48 @@
+//===- InputSection.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputSection.h"
+#include "OutputSegment.h"
+#include "Symbols.h"
+#include "Target.h"
+#include "lld/Common/Memory.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::support;
+using namespace lld;
+using namespace lld::macho;
+
+std::vector<InputSection *> macho::inputSections;
+
+uint64_t InputSection::getFileOffset() const {
+ return parent->fileOff + outSecFileOff;
+}
+
+uint64_t InputSection::getVA() const { return parent->addr + outSecOff; }
+
+void InputSection::writeTo(uint8_t *buf) {
+ if (getFileSize() == 0)
+ return;
+
+ memcpy(buf, data.data(), data.size());
+
+ for (Reloc &r : relocs) {
+ uint64_t va = 0;
+ if (auto *s = r.target.dyn_cast<Symbol *>())
+ va = target->getSymbolVA(*s, r.type);
+ else if (auto *isec = r.target.dyn_cast<InputSection *>())
+ va = isec->getVA();
+
+ uint64_t val = va + r.addend;
+ if (r.pcrel)
+ val -= getVA() + r.offset;
+ target->relocateOne(buf + r.offset, r, val);
+ }
+}
diff --git a/contrib/llvm-project/lld/MachO/InputSection.h b/contrib/llvm-project/lld/MachO/InputSection.h
new file mode 100644
index 000000000000..96ae0cbe6ea4
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/InputSection.h
@@ -0,0 +1,74 @@
+//===- InputSection.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_MACHO_INPUT_SECTION_H
+#define LLD_MACHO_INPUT_SECTION_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+namespace lld {
+namespace macho {
+
+class InputFile;
+class InputSection;
+class OutputSection;
+class Symbol;
+
+struct Reloc {
+ uint8_t type;
+ bool pcrel;
+ uint8_t length;
+ // The offset from the start of the subsection that this relocation belongs
+ // to.
+ uint32_t offset;
+ // Adding this offset to the address of the target symbol or subsection gives
+ // the destination that this relocation refers to.
+ uint64_t addend;
+ llvm::PointerUnion<Symbol *, InputSection *> target;
+};
+
+inline bool isZeroFill(uint8_t flags) {
+ return (flags & llvm::MachO::SECTION_TYPE) == llvm::MachO::S_ZEROFILL;
+}
+
+class InputSection {
+public:
+ virtual ~InputSection() = default;
+ virtual uint64_t getSize() const { return data.size(); }
+ virtual uint64_t getFileSize() const {
+ return isZeroFill(flags) ? 0 : getSize();
+ }
+ uint64_t getFileOffset() const;
+ uint64_t getVA() const;
+
+ virtual void writeTo(uint8_t *buf);
+
+ InputFile *file = nullptr;
+ StringRef name;
+ StringRef segname;
+
+ OutputSection *parent = nullptr;
+ uint64_t outSecOff = 0;
+ uint64_t outSecFileOff = 0;
+
+ uint32_t align = 1;
+ uint32_t flags = 0;
+
+ ArrayRef<uint8_t> data;
+ std::vector<Reloc> relocs;
+};
+
+extern std::vector<InputSection *> inputSections;
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/MachOStructs.h b/contrib/llvm-project/lld/MachO/MachOStructs.h
new file mode 100644
index 000000000000..69b50ec23173
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/MachOStructs.h
@@ -0,0 +1,36 @@
+//===- MachOStructs.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines structures used in the MachO object file format. Note that
+// unlike llvm/BinaryFormat/MachO.h, the structs here are defined in terms of
+// endian- and alignment-compatibility wrappers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_MACHO_STRUCTS_H
+#define LLD_MACHO_MACHO_STRUCTS_H
+
+#include "llvm/Support/Endian.h"
+
+namespace lld {
+
+namespace structs {
+
+struct nlist_64 {
+ llvm::support::ulittle32_t n_strx;
+ uint8_t n_type;
+ uint8_t n_sect;
+ llvm::support::ulittle16_t n_desc;
+ llvm::support::ulittle64_t n_value;
+};
+
+} // namespace structs
+
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/MergedOutputSection.cpp b/contrib/llvm-project/lld/MachO/MergedOutputSection.cpp
new file mode 100644
index 000000000000..2d0be2538347
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/MergedOutputSection.cpp
@@ -0,0 +1,74 @@
+//===- OutputSection.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "MergedOutputSection.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+void MergedOutputSection::mergeInput(InputSection *input) {
+ if (inputs.empty()) {
+ align = input->align;
+ flags = input->flags;
+ } else {
+ mergeFlags(input->flags);
+ align = std::max(align, input->align);
+ }
+
+ inputs.push_back(input);
+ input->parent = this;
+}
+
+void MergedOutputSection::finalize() {
+ uint64_t isecAddr = addr;
+ uint64_t isecFileOff = fileOff;
+ for (InputSection *isec : inputs) {
+ isecAddr = alignTo(isecAddr, isec->align);
+ isecFileOff = alignTo(isecFileOff, isec->align);
+ isec->outSecOff = isecAddr - addr;
+ isec->outSecFileOff = isecFileOff - fileOff;
+ isecAddr += isec->getSize();
+ isecFileOff += isec->getFileSize();
+ }
+ size = isecAddr - addr;
+ fileSize = isecFileOff - fileOff;
+}
+
+void MergedOutputSection::writeTo(uint8_t *buf) const {
+ for (InputSection *isec : inputs) {
+ isec->writeTo(buf + isec->outSecFileOff);
+ }
+}
+
+// TODO: this is most likely wrong; reconsider how section flags
+// are actually merged. The logic presented here was written without
+// any form of informed research.
+void MergedOutputSection::mergeFlags(uint32_t inputFlags) {
+ uint8_t sectionFlag = MachO::SECTION_TYPE & inputFlags;
+ if (sectionFlag != (MachO::SECTION_TYPE & flags))
+ error("Cannot add merge section; inconsistent type flags " +
+ Twine(sectionFlag));
+
+ uint32_t inconsistentFlags =
+ MachO::S_ATTR_DEBUG | MachO::S_ATTR_STRIP_STATIC_SYMS |
+ MachO::S_ATTR_NO_DEAD_STRIP | MachO::S_ATTR_LIVE_SUPPORT;
+ if ((inputFlags ^ flags) & inconsistentFlags)
+ error("Cannot add merge section; cannot merge inconsistent flags");
+
+ // Negate pure instruction presence if any section isn't pure.
+ uint32_t pureMask = ~MachO::S_ATTR_PURE_INSTRUCTIONS | (inputFlags & flags);
+
+ // Merge the rest
+ flags |= inputFlags;
+ flags &= pureMask;
+}
diff --git a/contrib/llvm-project/lld/MachO/MergedOutputSection.h b/contrib/llvm-project/lld/MachO/MergedOutputSection.h
new file mode 100644
index 000000000000..279a7e0f75cd
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/MergedOutputSection.h
@@ -0,0 +1,56 @@
+//===- OutputSection.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_MACHO_MERGED_OUTPUT_SECTION_H
+#define LLD_MACHO_MERGED_OUTPUT_SECTION_H
+
+#include "InputSection.h"
+#include "OutputSection.h"
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/MapVector.h"
+
+namespace lld {
+namespace macho {
+
+// Linking multiple files will inevitably mean resolving sections in different
+// files that are labeled with the same segment and section name. This class
+// contains all such sections and writes the data from each section sequentially
+// in the final binary.
+class MergedOutputSection : public OutputSection {
+public:
+ MergedOutputSection(StringRef name) : OutputSection(MergedKind, name) {}
+
+ const InputSection *firstSection() const { return inputs.front(); }
+ const InputSection *lastSection() const { return inputs.back(); }
+
+ // These accessors will only be valid after finalizing the section
+ uint64_t getSize() const override { return size; }
+ uint64_t getFileSize() const override { return fileSize; }
+
+ void mergeInput(InputSection *input);
+ void finalize() override;
+
+ void writeTo(uint8_t *buf) const override;
+
+ std::vector<InputSection *> inputs;
+
+ static bool classof(const OutputSection *sec) {
+ return sec->kind() == MergedKind;
+ }
+
+private:
+ void mergeFlags(uint32_t inputFlags);
+
+ size_t size = 0;
+ uint64_t fileSize = 0;
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/Options.td b/contrib/llvm-project/lld/MachO/Options.td
new file mode 100644
index 000000000000..1e42542b9ac4
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Options.td
@@ -0,0 +1,1297 @@
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["-", "--"], "help">;
+def help_hidden : Flag<["--"], "help-hidden">,
+ HelpText<"Display help for hidden options">;
+
+// This is a complete Options.td compiled from Apple's ld(1) manpage
+// dated 2018-03-07 and cross checked with ld64 source code in repo
+// https://github.com/apple-opensource/ld64 at git tag "512.4" dated
+// 2018-03-18.
+
+// Flags<[HelpHidden]> marks options that are not yet ported to lld,
+// and serve as a scoreboard for annotating our progress toward
+// implementing ld64 options in lld. As you add implementions to
+// Driver.cpp, please remove the hidden flag here.
+
+def grp_kind : OptionGroup<"kind">, HelpText<"OUTPUT KIND">;
+
+def execute : Flag<["-"], "execute">,
+ HelpText<"Produce a main executable (default)">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def dylib : Flag<["-"], "dylib">,
+ HelpText<"Produce a shared library">,
+ Group<grp_kind>;
+def bundle : Flag<["-"], "bundle">,
+ HelpText<"Produce a bundle">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def r : Flag<["-"], "r">,
+ HelpText<"Merge multiple object files into one, retaining relocations">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def dylinker : Flag<["-"], "dylinker">,
+ HelpText<"Produce a dylinker only used when building dyld">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def dynamic : Flag<["-"], "dynamic">,
+ HelpText<"Link dynamically (default)">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def static : Flag<["-"], "static">,
+ HelpText<"Link statically">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def preload : Flag<["-"], "preload">,
+ HelpText<"Produce an unsegmented binary for embedded systems">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def arch : Separate<["-"], "arch">,
+ MetaVarName<"<arch_name>">,
+ HelpText<"The architecture (e.g. ppc, ppc64, i386, x86_64)">,
+ Group<grp_kind>;
+def o : Separate<["-"], "o">,
+ MetaVarName<"<path>">,
+ HelpText<"The name of the output file (default: `a.out')">,
+ Group<grp_kind>;
+
+def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARIES">;
+
+def l : Joined<["-"], "l">,
+ MetaVarName<"<name>">,
+ HelpText<"Search for lib<name>.dylib or lib<name>.a on the library search path">,
+ Group<grp_libs>;
+def weak_l : Joined<["-"], "weak-l">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -l<name>, but mark library and its references as weak imports">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def weak_library : Separate<["-"], "weak_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Like bare <path>, but mark library and its references as weak imports">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def reexport_l : Joined<["-"], "reexport-l">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -l<name>, but export all symbols of <name> from newly created library">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def reexport_library : Separate<["-"], "reexport_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Like bare <path>, but export all symbols of <path> from newly created library">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def upward_l : Joined<["-"], "upward-l">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -l<name>, but specify dylib as an upward dependency">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def upward_library : Separate<["-"], "upward_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Like bare <path>, but specify dylib as an upward dependency">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def L : JoinedOrSeparate<["-"], "L">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add dir to the library search path">,
+ Group<grp_libs>;
+def Z : Flag<["-"], "Z">,
+ HelpText<"Remove standard directories from the library and framework search paths">,
+ Group<grp_libs>;
+def syslibroot : Separate<["-"], "syslibroot">,
+ MetaVarName<"<rootdir>">,
+ HelpText<"Prepend <rootdir> to all library and framework search paths">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def search_paths_first : Flag<["-"], "search_paths_first">,
+ HelpText<"Search for lib<name>.dylib and lib<name>.a at each step in traversing search path (default for Xcode 4 and later)">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def search_dylibs_first : Flag<["-"], "search_dylibs_first">,
+ HelpText<"Search for lib<name>.dylib on first pass, then for lib<name>.a on second pass through search path (default for Xcode 3 and earlier)">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def framework : Separate<["-"], "framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Search for <name>.framework/<name> on the framework search path">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def weak_framework : Separate<["-"], "weak_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -framework <name>, but mark framework and its references as weak imports">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def reexport_framework : Separate<["-"], "reexport_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -framework <name>, but export all symbols of <name> from the newly created library">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def upward_framework : Separate<["-"], "upward_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -framework <name>, but specify the framework as an upward dependency">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def F : JoinedOrSeparate<["-"], "F">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add dir to the framework search path">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def all_load : Flag<["-"], "all_load">,
+ HelpText<"Load all members of all static archive libraries">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def ObjC : Flag<["-"], "ObjC">,
+ HelpText<"Load all members of static archives that are an Objective-C class or category.">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def force_load : Separate<["-"], "force_load">,
+ MetaVarName<"<path>">,
+ HelpText<"Load all members static archive library at <path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+
+def grp_content : OptionGroup<"content">, HelpText<"ADDITIONAL CONTENT">;
+
+def sectcreate : MultiArg<["-"], "sectcreate", 3>,
+ MetaVarName<"<segment> <section> <file>">,
+ HelpText<"Create <section> in <segment> from the contents of <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+def segcreate : MultiArg<["-"], "segcreate", 3>,
+ MetaVarName<"<segment> <section> <file>">,
+ Alias<sectcreate>,
+ HelpText<"Alias for -sectcreate">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+def filelist : Separate<["-"], "filelist">,
+ MetaVarName<"<file>">,
+ HelpText<"Read names of files to link from <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+def dtrace : Separate<["-"], "dtrace">,
+ MetaVarName<"<script>">,
+ HelpText<"Enable DTrace static probes according to declarations in <script>">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+
+def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
+
+def dead_strip : Flag<["-"], "dead_strip">,
+ HelpText<"Remove unreachable functions and data">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def order_file : Separate<["-"], "order_file">,
+ MetaVarName<"<file>">,
+ HelpText<"Layout functions and data according to specification in <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def sectorder : MultiArg<["-"], "sectorder", 3>,
+ MetaVarName<"<segname> <sectname> <orderfile>">,
+ HelpText<"Replaced by more general -order_file option">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_order_inits : Flag<["-"], "no_order_inits">,
+ HelpText<"Disable default reordering of initializer and terminator functions">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_order_data : Flag<["-"], "no_order_data">,
+ HelpText<"Disable default reordering of global data accessed at launch time">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def platform_version : MultiArg<["-"], "platform_version", 3>,
+ MetaVarName<"<platform> <min_version> <sdk_version>">,
+ HelpText<"Platform (e.g., macos, ios, tvos, watchos, bridgeos, mac-catalyst, ios-sim, tvos-sim, watchos-sim, driverkit) and version numbers">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def macos_version_min : Separate<["-"], "macos_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Oldest macOS version for which linked output is useable">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def macosx_version_min : Separate<["-"], "macosx_version_min">,
+ MetaVarName<"<version>">,
+ Alias<macos_version_min>,
+ HelpText<"Alias for -macos_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def ios_version_min : Separate<["-"], "ios_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Oldest iOS version for which linked output is useable">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
+ MetaVarName<"<version>">,
+ Alias<ios_version_min>,
+ HelpText<"Alias for -ios_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def image_base : Separate<["-"], "image_base">,
+ MetaVarName<"<address>">,
+ HelpText<"Preferred hex load address for a dylib or bundle.">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def seg1addr : Separate<["-"], "seg1addr">,
+ MetaVarName<"<address>">,
+ Alias<image_base>,
+ HelpText<"Alias for -image_base">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_implicit_dylibs : Flag<["-"], "no_implicit_dylibs">,
+ HelpText<"Do not optimize public dylib transitive symbol references">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def exported_symbols_order : Separate<["-"], "exported_symbols_order">,
+ MetaVarName<"<file>">,
+ HelpText<"Specify frequently-used symbols in <file> to optimize symbol exports">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_zero_fill_sections : Flag<["-"], "no_zero_fill_sections">,
+ HelpText<"Explicitly store zeroed data in the final image">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def merge_zero_fill_sections : Flag<["-"], "merge_zero_fill_sections">,
+ HelpText<"Merge all zeroed data into the __zerofill section">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_branch_islands : Flag<["-"], "no_branch_islands">,
+ HelpText<"Disable infra for branches beyond the maximum branch distance.">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+
+def grp_dylib : OptionGroup<"dylib">, HelpText<"DYNAMIC LIBRARIES (DYLIB)">;
+
+def install_name : Separate<["-"], "install_name">,
+ MetaVarName<"<name>">,
+ HelpText<"Set an internal install path in a dylib">,
+ Group<grp_dylib>;
+def dylib_install_name : Separate<["-"], "dylib_install_name">,
+ MetaVarName<"<name>">,
+ Alias<install_name>,
+ HelpText<"Alias for -install_name">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def dylinker_install_name : Separate<["-"], "dylinker_install_name">,
+ MetaVarName<"<name>">,
+ Alias<install_name>,
+ HelpText<"Alias for -install_name">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
+ HelpText<"Clients can discard this dylib if it is unreferenced">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def compatibility_version : Separate<["-"], "compatibility_version">,
+ MetaVarName<"<version>">,
+ HelpText<"Compatibility <version> of this library">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
+ MetaVarName<"<version>">,
+ Alias<compatibility_version>,
+ HelpText<"Alias for -compatibility_version">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def current_version : Separate<["-"], "current_version">,
+ MetaVarName<"<version>">,
+ HelpText<"Current <version> of this library">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def dylib_current_version : Separate<["-"], "dylib_current_version">,
+ MetaVarName<"<version>">,
+ Alias<current_version>,
+ HelpText<"Alias for -current_version">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+
+def grp_main : OptionGroup<"main">, HelpText<"MAIN EXECUTABLE">;
+
+def pie : Flag<["-"], "pie">,
+ HelpText<"Build a position independent executable (default for macOS 10.7 and later)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def no_pie : Flag<["-"], "no_pie">,
+ HelpText<"Do not build a position independent executable (default for macOS 10.6 and earlier)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def pagezero_size : Separate<["-"], "pagezero_size">,
+ MetaVarName<"<size>">,
+ HelpText<"Size of unreadable segment at address zero is hex <size> (default is 4KB on 32-bit and 4GB on 64-bit)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def stack_size : Separate<["-"], "stack_size">,
+ MetaVarName<"<size>">,
+ HelpText<"Maximum hex stack size for the main thread in a program. (default is 8MB)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def allow_stack_execute : Flag<["-"], "allow_stack_execute">,
+ HelpText<"Mark stack segment as executable">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def export_dynamic : Flag<["-"], "export_dynamic">,
+ HelpText<"Preserve all global symbols during LTO">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+
+def grp_bundle : OptionGroup<"bundle">, HelpText<"CREATING A BUNDLE">;
+
+def bundle_loader : Separate<["-"], "bundle_loader">,
+ MetaVarName<"<executable>">,
+ HelpText<"Resolve undefined symbols from <executable>">,
+ Flags<[HelpHidden]>,
+ Group<grp_bundle>;
+
+def grp_object : OptionGroup<"object">, HelpText<"CREATING AN OBJECT FILE">;
+
+def keep_private_externs : Flag<["-"], "keep_private_externs">,
+ HelpText<"Do not convert private external symbols to static symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_object>;
+def d : Flag<["-"], "d">,
+ HelpText<"Force tentative into real definitions for common symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_object>;
+
+def grp_resolve : OptionGroup<"resolve">, HelpText<"SYMBOL RESOLUTION">;
+
+def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Symbols specified in <file> remain global, while others become private externs">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def exported_symbol : Separate<["-"], "exported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"<symbol> remains global, while others become private externs">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Global symbols specified in <file> become private externs">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def unexported_symbol : Separate<["-"], "unexported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Global <symbol> becomes private extern">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def reexported_symbols_list : Separate<["-"], "reexported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Symbols from dependent dylibs specified in <file> are reexported by this dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def alias : MultiArg<["-"], "alias", 2>,
+ MetaVarName<"<symbol_name> <alternate_name>">,
+ HelpText<"Create a symbol alias with default global visibility">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def alias_list : Separate<["-"], "alias_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Create symbol aliases specified in <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def flat_namespace : Flag<["-"], "flat_namespace">,
+ HelpText<"Resolve symbols from all dylibs, both direct & transitive. Do not record source libraries: dyld must re-search at runtime and use the first definition found">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def u : Separate<["-"], "u">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Require that <symbol> be defined for the link to succeed">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def U : Separate<["-"], "U">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Allow <symbol> to have no definition">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def undefined : Separate<["-"], "undefined">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Handle undefined symbols according to <treatment>: error, warning, suppress, or dynamic_lookup (default is error)">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def rpath : Separate<["-"], "rpath">,
+ MetaVarName<"<path>">,
+ HelpText<"Add <path> to dyld search list for dylibs with load path prefix `@rpath/'">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def commons : Separate<["-"], "commons">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Resolve tentative definitions in dylibs according to <treatment>: ignore_dylibs, use_dylibs, error (default is ignore_dylibs)">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+
+def grp_introspect : OptionGroup<"introspect">, HelpText<"INTROSPECTING THE LINKER">;
+
+def why_load : Flag<["-"], "why_load">,
+ HelpText<"Log the symbol that compels loading of each object file from a static library">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def whyload : Flag<["-"], "whyload">,
+ Alias<why_load>,
+ HelpText<"Alias for -why_load">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def why_live : Separate<["-"], "why_live">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Log a chain of references to <symbol>, for use with -dead_strip">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def print_statistics : Flag<["-"], "print_statistics">,
+ HelpText<"Log the linker's memory and CPU usage">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def t : Flag<["-"], "t">,
+ HelpText<"Log every file the linker loads: object, archive, and dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def whatsloaded : Flag<["-"], "whatsloaded">,
+ HelpText<"Logs only the object files the linker loads">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def order_file_statistics : Flag<["-"], "order_file_statistics">,
+ HelpText<"Logs information about -order_file">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def map : Separate<["-"], "map">,
+ MetaVarName<"<path>">,
+ HelpText<"Writes all symbols and their addresses to <path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+
+def grp_symtab : OptionGroup<"symtab">, HelpText<"SYMBOL TABLE OPTIMIZATIONS">;
+
+def S : Flag<["-"], "S">,
+ HelpText<"Strip debug information (STABS or DWARF) from the output">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def x : Flag<["-"], "x">,
+ HelpText<"Exclude non-global symbols from the output symbol table">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def non_global_symbols_strip_list : Separate<["-"], "non_global_symbols_strip_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify in <path> the non-global symbols that should be removed from the output symbol table">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def non_global_symbols_no_strip_list : Separate<["-"], "non_global_symbols_no_strip_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify in <path> the non-global symbols that should remain in the output symbol table">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def oso_prefix : Separate<["-"], "oso_prefix">,
+ MetaVarName<"<path>">,
+ HelpText<"Remove the prefix <path> from OSO symbols in the debug map">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+
+def grp_bitcode : OptionGroup<"bitcode">, HelpText<"BITCODE BUILD FLOW">;
+
+def bitcode_bundle : Flag<["-"], "bitcode_bundle">,
+ HelpText<"Generate an embedded bitcode bundle in the __LLVM,__bundle section of the output">,
+ Flags<[HelpHidden]>,
+ Group<grp_bitcode>;
+def bitcode_hide_symbols : Flag<["-"], "bitcode_hide_symbols">,
+ HelpText<"With -bitcode_bundle, hide all non-exported symbols from output bitcode bundle.">,
+ Flags<[HelpHidden]>,
+ Group<grp_bitcode>;
+def bitcode_symbol_map : Separate<["-"], "bitcode_symbol_map">,
+ MetaVarName<"<path>">,
+ HelpText<"Write the bitcode symbol reverse mapping to file <path>, or if a directory, to <path>/UUID.bcsymbolmap">,
+ Flags<[HelpHidden]>,
+ Group<grp_bitcode>;
+
+def grp_rare : OptionGroup<"rare">, HelpText<"RARELY USED">;
+
+def v : Flag<["-"], "v">,
+ HelpText<"Print the linker version">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def version_details : Flag<["-"], "version_details">,
+ HelpText<"Print the linker version in JSON form">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_weak_imports : Flag<["-"], "no_weak_imports">,
+ HelpText<"Fail if any symbols are weak imports, allowed to be NULL at runtime">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_deduplicate : Flag<["-"], "no_deduplicate">,
+ HelpText<"Omit the deduplication pass">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def verbose_deduplicate : Flag<["-"], "verbose_deduplicate">,
+ HelpText<"Print function names eliminated by deduplication and the total size of code savings">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_inits : Flag<["-"], "no_inits">,
+ HelpText<"Fail if the output contains static initializers">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_warn_inits : Flag<["-"], "no_warn_inits">,
+ HelpText<"Suppress warnings for static initializers in the output">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def debug_variant : Flag<["-"], "debug_variant">,
+ HelpText<"Suppress warnings germane to binaries shipping to customers">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def unaligned_pointers : Separate<["-"], "unaligned_pointers">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Handle unaligned pointers in __DATA segments according to <treatment>: warning, error, or suppress (default for arm64e is error, otherwise suppress)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dirty_data_list : Separate<["-"], "dirty_data_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify data symbols in <path> destined for the __DATA_DIRTY segment">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def max_default_common_align : Separate<["-"], "max_default_common_align">,
+ MetaVarName<"<boundary>">,
+ HelpText<"Reduce maximum alignment for common symbols to a hex power-of-2 <boundary>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def move_to_rw_segment : MultiArg<["-"], "move_to_rw_segment", 2>,
+ MetaVarName<"<segment> <path>">,
+ HelpText<"Move data symbols listed in <path> to another <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def move_to_ro_segment : MultiArg<["-"], "move_to_ro_segment", 2>,
+ MetaVarName<"<segment> <path>">,
+ HelpText<"Move code symbols listed in <path> to another <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def rename_section : MultiArg<["-"], "rename_section", 4>,
+ MetaVarName<"<from_segment> <from_section> <to_segment> <to_section>">,
+ HelpText<"Rename <from_segment>/<from_section> as <to_segment>/<to_section>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def rename_segment : MultiArg<["-"], "rename_segment", 2>,
+ MetaVarName<"<from_segment> <to_segment>">,
+ HelpText<"Rename <from_segment> as <to_segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def trace_symbol_layout : Flag<["-"], "trace_symbol_layout">,
+ HelpText<"Show where and why symbols move, as specified by -move_to_ro_segment, -move_to_rw_segment, -rename_section, and -rename_segment">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def section_order : MultiArg<["-"], "section_order", 2>,
+ MetaVarName<"<segment> <sections>">,
+ HelpText<"With -preload, specify layout sequence of colon-separated <sections> in <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segment_order : Separate<["-"], "segment_order">,
+ MetaVarName<"<colon_separated_segment_list>">,
+ HelpText<"With -preload, specify layout sequence of colon-separated <segments>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def allow_heap_execute : Flag<["-"], "allow_heap_execute">,
+ HelpText<"On i386, allow any page to execute code">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def application_extension : Flag<["-"], "application_extension">,
+ HelpText<"Designate the linker output as safe for use in an application extension">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_application_extension : Flag<["-"], "no_application_extension">,
+ HelpText<"Designate the linker output as unsafe for use in an application extension">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def fatal_warnings : Flag<["-"], "fatal_warnings">,
+ HelpText<"Escalate warnings as errors">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_eh_labels : Flag<["-"], "no_eh_labels">,
+ HelpText<"In -r mode, suppress .eh labels in the __eh_frame section">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_compact_unwind : Flag<["-"], "warn_compact_unwind">,
+ HelpText<"Warn for each FDE that cannot compact into the __unwind_info section and must remain in the __eh_frame section">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_weak_exports : Flag<["-"], "warn_weak_exports">,
+ HelpText<"Warn if the linked image contains weak external symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_weak_exports : Flag<["-"], "no_weak_exports">,
+ HelpText<"Fail if the linked image contains weak external symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def objc_gc_compaction : Flag<["-"], "objc_gc_compaction">,
+ HelpText<"Mark the Objective-C image as compatible with compacting garbage collection">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def objc_gc : Flag<["-"], "objc_gc">,
+ HelpText<"Verify that all code was compiled with -fobjc-gc or -fobjc-gc-only">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def objc_gc_only : Flag<["-"], "objc_gc_only">,
+ HelpText<"Verify that all code was compiled with -fobjc-gc-only">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dead_strip_dylibs : Flag<["-"], "dead_strip_dylibs">,
+ HelpText<"Remove dylibs that are unreachable by the entry point or exported symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def allow_sub_type_mismatches : Flag<["-"], "allow_sub_type_mismatches">,
+ HelpText<"Permit mixing objects compiled for different ARM CPU subtypes">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_uuid : Flag<["-"], "no_uuid">,
+ HelpText<"Do not generate the LC_UUID load command">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def root_safe : Flag<["-"], "root_safe">,
+ HelpText<"Set the MH_ROOT_SAFE bit in the mach-o header">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def setuid_safe : Flag<["-"], "setuid_safe">,
+ HelpText<"Set the MH_SETUID_SAFE bit in the mach-o header">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def interposable : Flag<["-"], "interposable">,
+ HelpText<"Indirects access to all to exported symbols in a dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def multi_module : Flag<["-"], "multi_module">,
+ Alias<interposable>,
+ HelpText<"Alias for -interposable">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def init : Separate<["-"], "init">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Run <symbol> as the first initializer in a dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def sub_library : Separate<["-"], "sub_library">,
+ MetaVarName<"<name>">,
+ HelpText<"Re-export the dylib as <name>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def sub_umbrella : Separate<["-"], "sub_umbrella">,
+ MetaVarName<"<name>">,
+ HelpText<"Re-export the framework as <name>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def allowable_client : Separate<["-"], "allowable_client">,
+ MetaVarName<"<name>">,
+ HelpText<"Specify <name> of a dylib or framework that is allowed to link to this dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def client_name : Separate<["-"], "client_name">,
+ MetaVarName<"<name>">,
+ HelpText<"Specifies a <name> this client should match with the -allowable_client <name> in a dependent dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def umbrella : Separate<["-"], "umbrella">,
+ MetaVarName<"<<name>>">,
+ HelpText<"Re-export this dylib through the umbrella framework <name>a">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def headerpad : Separate<["-"], "headerpad">,
+ MetaVarName<"<size>">,
+ HelpText<"Allocate hex <size> extra space for future expansion of the load commands via install_name_tool">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def headerpad_max_install_names : Flag<["-"], "headerpad_max_install_names">,
+ HelpText<"Allocate extra space so all load-command paths can expand to MAXPATHLEN via install_name_tool">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def bind_at_load : Flag<["-"], "bind_at_load">,
+ HelpText<"Tell dyld to bind all symbols at load time, rather than lazily">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def force_flat_namespace : Flag<["-"], "force_flat_namespace">,
+ HelpText<"Tell dyld to use a flat namespace on this executable and all its dependent dylibs & bundles">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segalign : Separate<["-"], "segalign">,
+ MetaVarName<"<boundary>">,
+ HelpText<"Align all segments to hex power-of-2 <boundary>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def sectalign : MultiArg<["-"], "sectalign", 3>,
+ MetaVarName<"<segment> <section> <boundary>">,
+ HelpText<"Align <section> within <segment> to hex power-of-2 <boundary>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def stack_addr : Separate<["-"], "stack_addr">,
+ MetaVarName<"<address>">,
+ HelpText<"Initialize stack pointer to hex <address> rounded to a page boundary">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segprot : MultiArg<["-"], "segprot", 3>,
+ MetaVarName<"<segment> <max> <init>">,
+ HelpText<"Specifies the <max> and <init> virtual memory protection of <segment> as r/w/x/-seg_addr_table path Specify hex base addresses and dylib install names on successive lines in <path>. This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segs_read_write_addr : Separate<["-"], "segs_read_write_addr">,
+ MetaVarName<"<address>">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segs_read_only_addr : Separate<["-"], "segs_read_only_addr">,
+ MetaVarName<"<address>">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segaddr : MultiArg<["-"], "segaddr", 2>,
+ MetaVarName<"<segment> <address>">,
+ HelpText<"Specify the starting hex <address> at a 4KiB page boundary for <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def seg_page_size : MultiArg<["-"], "seg_page_size", 2>,
+ MetaVarName<"<segment> <size>">,
+ HelpText<"Specifies the page <size> for <segment>. Segment size will be a multiple of its page size">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dylib_file : Separate<["-"], "dylib_file">,
+ MetaVarName<"<install_path:current_path>">,
+ HelpText<"Specify <current_path> as different from where a dylib normally resides at <install_path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def prebind : Flag<["-"], "prebind">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def weak_reference_mismatches : Separate<["-"], "weak_reference_mismatches">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Resolve symbol imports of conflicting weakness according to <treatment> as weak, non-weak, or error (default is non-weak)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def read_only_relocs : Separate<["-"], "read_only_relocs">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Handle relocations that modify read-only pages according to <treatment> of warning, error, or suppress (i.e., allow)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def force_cpusubtype_ALL : Flag<["-"], "force_cpusubtype_ALL">,
+ HelpText<"Mark binary as runnable on any PowerPC, ignoring any PowerPC cpu requirements encoded in the object files">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_arch_warnings : Flag<["-"], "no_arch_warnings">,
+ HelpText<"Suppresses warnings about inputs whose architecture does not match the -arch option">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def arch_errors_fatal : Flag<["-"], "arch_errors_fatal">,
+ HelpText<"Escalate to errors any warnings about inputs whose architecture does not match the -arch option">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def e : Separate<["-"], "e">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Make <symbol> the entry point of an executable (default is \"start\" from crt1.o)">,
+ Group<grp_rare>;
+def w : Flag<["-"], "w">,
+ HelpText<"Suppress all warnings">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def final_output : Separate<["-"], "final_output">,
+ MetaVarName<"<name>">,
+ HelpText<"Specify the dylib install name if -install_name is not used--used by compiler driver for multiple -arch arguments">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def arch_multiple : Flag<["-"], "arch_multiple">,
+ HelpText<"Augment error and warning messages with the architecture name">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def twolevel_namespace_hints : Flag<["-"], "twolevel_namespace_hints">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dot : Separate<["-"], "dot">,
+ MetaVarName<"<path>">,
+ HelpText<"Write a graph of symbol dependencies to <path> as a .dot file viewable with GraphViz">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def keep_relocs : Flag<["-"], "keep_relocs">,
+ HelpText<"Retain section-based relocation records in the output, which are ignored at runtime by dyld">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_stabs : Flag<["-"], "warn_stabs">,
+ HelpText<"Warn when bad stab symbols inside a BINCL/EINCL prevent optimization">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_commons : Flag<["-"], "warn_commons">,
+ HelpText<"Warn when a tentative definition in an object file matches an external symbol in a dylib, which often means \"extern\" is missing from a variable declaration in a header file">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def read_only_stubs : Flag<["-"], "read_only_stubs">,
+ HelpText<"On i386, make the __IMPORT segment of a final linked image read-only">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def slow_stubs : Flag<["-"], "slow_stubs">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def interposable_list : Separate<["-"], "interposable_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Access global symbols listed in <path> indirectly">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_function_starts : Flag<["-"], "no_function_starts">,
+ HelpText<"Do not creates a compressed table of function start addresses">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
+ HelpText<"Do not merge Objective-C categories into their classes">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def object_path_lto : Separate<["-"], "object_path_lto">,
+ MetaVarName<"<path>">,
+ HelpText<"Retain any temporary mach-o file in <path> that would otherwise be deleted during LTO">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def lto_library : Separate<["-"], "lto_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Override the default ../lib/libLTO.dylib as <path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def cache_path_lto : Separate<["-"], "cache_path_lto">,
+ MetaVarName<"<path>">,
+ HelpText<"Use <path> as a directory for the incremental LTO cache">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def prune_interval_lto : Separate<["-"], "prune_interval_lto">,
+ MetaVarName<"<seconds>">,
+ HelpText<"Prune the incremental LTO cache after <seconds> (-1 disables pruning)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def prune_after_lto : Separate<["-"], "prune_after_lto">,
+ MetaVarName<"<seconds>">,
+ HelpText<"Remove LTO cache entries after <seconds>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def max_relative_cache_size_lto : Separate<["-"], "max_relative_cache_size_lto">,
+ MetaVarName<"<percent>">,
+ HelpText<"Limit the incremental LTO cache growth to <percent> of free disk, space">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def page_align_data_atoms : Flag<["-"], "page_align_data_atoms">,
+ HelpText<"Distribute global variables on separate pages so page used/dirty status can guide creation of an order file to cluster commonly used/dirty globals">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def not_for_dyld_shared_cache : Flag<["-"], "not_for_dyld_shared_cache">,
+ HelpText<"Prevent system dylibs from being placed into the dylib shared cache">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+
+def grp_deprecated : OptionGroup<"deprecated">, HelpText<"DEPRECATED">;
+
+def lazy_framework : Separate<["-"], "lazy_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"This option is deprecated and is now an alias for -framework.">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def lazy_library : Separate<["-"], "lazy_library">,
+ MetaVarName<"<path>">,
+ HelpText<"This option is deprecated and is now an alias for regular linking">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def lazy_l : Joined<["-"], "lazy-l">,
+ MetaVarName<"<name>">,
+ HelpText<"This option is deprecated and is now an alias for -l<path>.">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def single_module : Flag<["-"], "single_module">,
+ HelpText<"Unnecessary option: this is already the default">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def no_dead_strip_inits_and_terms : Flag<["-"], "no_dead_strip_inits_and_terms">,
+ HelpText<"Unnecessary option: initialization and termination are roots of the dead strip graph, so never dead stripped">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def noall_load : Flag<["-"], "noall_load">,
+ HelpText<"Unnecessary option: this is already the default">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+
+def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE">;
+
+def y : Joined<["-"], "y">,
+ MetaVarName<"<symbol>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def sectobjectsymbols : MultiArg<["-"], "sectobjectsymbols", 2>,
+ MetaVarName<"<segname> <sectname>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def nofixprebinding : Flag<["-"], "nofixprebinding">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def noprebind_all_twolevel_modules : Flag<["-"], "noprebind_all_twolevel_modules">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def prebind_all_twolevel_modules : Flag<["-"], "prebind_all_twolevel_modules">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def prebind_allow_overlap : Flag<["-"], "prebind_allow_overlap">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def noprebind : Flag<["-"], "noprebind">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def sect_diff_relocs : Separate<["-"], "sect_diff_relocs">,
+ MetaVarName<"<treatment>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def A : Separate<["-"], "A">,
+ MetaVarName<"<basefile>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def b : Flag<["-"], "b">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Sn : Flag<["-"], "Sn">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Si : Flag<["-"], "Si">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Sp : Flag<["-"], "Sp">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def X : Flag<["-"], "X">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def s : Flag<["-"], "s">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def m : Flag<["-"], "m">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Y : Separate<["-"], "Y">,
+ MetaVarName<"<number>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def nomultidefs : Flag<["-"], "nomultidefs">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def multiply_defined_unused : Separate<["-"], "multiply_defined_unused">,
+ MetaVarName<"<treatment>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def multiply_defined : Separate<["-"], "multiply_defined">,
+ MetaVarName<"<treatment>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def private_bundle : Flag<["-"], "private_bundle">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def seg_addr_table_filename : Separate<["-"], "seg_addr_table_filename">,
+ MetaVarName<"<path>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def sectorder_detail : Flag<["-"], "sectorder_detail">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def no_compact_linkedit : Flag<["-"], "no_compact_linkedit">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def dependent_dr_info : Flag<["-"], "dependent_dr_info">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def no_dependent_dr_info : Flag<["-"], "no_dependent_dr_info">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def seglinkedit : Flag<["-"], "seglinkedit">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def noseglinkedit : Flag<["-"], "noseglinkedit">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def fvmlib : Flag<["-"], "fvmlib">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def run_init_lazily : Flag<["-"], "run_init_lazily">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+
+def grp_undocumented : OptionGroup<"undocumented">, HelpText<"UNDOCUMENTED">;
+
+def add_ast_path : Flag<["-"], "add_ast_path">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def add_linker_option : Flag<["-"], "add_linker_option">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def add_source_version : Flag<["-"], "add_source_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_source_version : Flag<["-"], "no_source_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def add_split_seg_info : Flag<["-"], "add_split_seg_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def allow_dead_duplicates : Flag<["-"], "allow_dead_duplicates">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def allow_simulator_linking_to_macosx_dylibs : Flag<["-"], "allow_simulator_linking_to_macosx_dylibs">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def bitcode_process_mode : Flag<["-"], "bitcode_process_mode">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def bitcode_verify : Flag<["-"], "bitcode_verify">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def classic_linker : Flag<["-"], "classic_linker">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def data_const : Flag<["-"], "data_const">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_data_const : Flag<["-"], "no_data_const">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def data_in_code_info : Flag<["-"], "data_in_code_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_data_in_code_info : Flag<["-"], "no_data_in_code_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def debug_snapshot : Flag<["-"], "debug_snapshot">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def demangle : Flag<["-"], "demangle">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def dependency_info : Flag<["-"], "dependency_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def dyld_env : Flag<["-"], "dyld_env">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def encryptable : Flag<["-"], "encryptable">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def executable_path : Flag<["-"], "executable_path">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def fixup_chains : Flag<["-"], "fixup_chains">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def fixup_chains_section : Flag<["-"], "fixup_chains_section">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def flto_codegen_only : Flag<["-"], "flto-codegen-only">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_load_swift_libs : Flag<["-"], "force_load_swift_libs">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbol_not_weak : Flag<["-"], "force_symbol_not_weak">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbols_coalesce_list : Flag<["-"], "force_symbols_coalesce_list">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbols_not_weak_list : Flag<["-"], "force_symbols_not_weak_list">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbols_weak_list : Flag<["-"], "force_symbols_weak_list">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbol_weak : Flag<["-"], "force_symbol_weak">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def function_starts : Flag<["-"], "function_starts">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def i : Flag<["-"], "i">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def ignore_auto_link : Flag<["-"], "ignore_auto_link">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def ignore_optimization_hints : Flag<["-"], "ignore_optimization_hints">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def init_offsets : Flag<["-"], "init_offsets">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def keep_dwarf_unwind : Flag<["-"], "keep_dwarf_unwind">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_keep_dwarf_unwind : Flag<["-"], "no_keep_dwarf_unwind">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def kext : Flag<["-"], "kext">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def kext_objects_dir : Flag<["-"], "kext_objects_dir">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_kext_objects : Flag<["-"], "no_kext_objects">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def kexts_use_stubs : Flag<["-"], "kexts_use_stubs">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def maccatalyst_version_min : Flag<["-"], "maccatalyst_version_min">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def iosmac_version_min : Flag<["-"], "iosmac_version_min">,
+ Alias<maccatalyst_version_min>,
+ HelpText<"Alias for -maccatalyst_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def uikitformac_version_min : Flag<["-"], "uikitformac_version_min">,
+ Alias<maccatalyst_version_min>,
+ HelpText<"Alias for -maccatalyst_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def mcpu : Flag<["-"], "mcpu">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def mllvm : Flag<["-"], "mllvm">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_compact_unwind : Flag<["-"], "no_compact_unwind">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_dtrace_dof : Flag<["-"], "no_dtrace_dof">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_encryption : Flag<["-"], "no_encryption">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_new_main : Flag<["-"], "no_new_main">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def objc_abi_version : Flag<["-"], "objc_abi_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def pause : Flag<["-"], "pause">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def random_uuid : Flag<["-"], "random_uuid">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def save_temps : Flag<["-"], "save-temps">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def sdk_version : Flag<["-"], "sdk_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def simulator_support : Flag<["-"], "simulator_support">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def snapshot_dir : Flag<["-"], "snapshot_dir">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def source_version : Flag<["-"], "source_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def text_exec : Flag<["-"], "text_exec">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def threaded_starts_section : Flag<["-"], "threaded_starts_section">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def verbose_optimization_hints : Flag<["-"], "verbose_optimization_hints">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def version_load_command : Flag<["-"], "version_load_command">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+
+def grp_ignored : OptionGroup<"ignored">, HelpText<"IGNORED">;
+
+def M : Flag<["-"], "M">,
+ HelpText<"This option is ignored in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_ignored>;
+def new_linker : Flag<["-"], "new_linker">,
+ HelpText<"This option is ignored in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_ignored>;
diff --git a/contrib/llvm-project/lld/MachO/OutputSection.cpp b/contrib/llvm-project/lld/MachO/OutputSection.cpp
new file mode 100644
index 000000000000..c006828267cf
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/OutputSection.cpp
@@ -0,0 +1,18 @@
+//===- OutputSection.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSection.h"
+#include "OutputSegment.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+uint64_t OutputSection::getSegmentOffset() const {
+ return addr - parent->firstSection()->addr;
+}
diff --git a/contrib/llvm-project/lld/MachO/OutputSection.h b/contrib/llvm-project/lld/MachO/OutputSection.h
new file mode 100644
index 000000000000..07b53a04639f
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/OutputSection.h
@@ -0,0 +1,74 @@
+//===- OutputSection.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_MACHO_OUTPUT_SECTION_H
+#define LLD_MACHO_OUTPUT_SECTION_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class OutputSegment;
+
+// Output sections represent the finalized sections present within the final
+// linked executable. They can represent special sections (like the symbol
+// table), or represent coalesced sections from the various inputs given to the
+// linker with the same segment / section name.
+class OutputSection {
+public:
+ enum Kind {
+ MergedKind,
+ SyntheticKind,
+ };
+
+ OutputSection(Kind kind, StringRef name) : name(name), sectionKind(kind) {}
+ virtual ~OutputSection() = default;
+ Kind kind() const { return sectionKind; }
+
+ // These accessors will only be valid after finalizing the section.
+ uint64_t getSegmentOffset() const;
+
+ // How much space the section occupies in the address space.
+ virtual uint64_t getSize() const = 0;
+ // How much space the section occupies in the file. Most sections are copied
+ // as-is so their file size is the same as their address space size.
+ virtual uint64_t getFileSize() const { return getSize(); }
+
+ // Hidden sections omit header content, but body content may still be present.
+ virtual bool isHidden() const { return false; }
+ // Unneeded sections are omitted entirely (header and body).
+ virtual bool isNeeded() const { return true; }
+
+ // Specifically finalizes addresses and section size, not content.
+ virtual void finalize() {
+ // TODO investigate refactoring synthetic section finalization logic into
+ // overrides of this function.
+ }
+
+ virtual void writeTo(uint8_t *buf) const = 0;
+
+ StringRef name;
+ OutputSegment *parent = nullptr;
+
+ uint32_t index = 0;
+ uint64_t addr = 0;
+ uint64_t fileOff = 0;
+ uint32_t align = 1;
+ uint32_t flags = 0;
+
+private:
+ Kind sectionKind;
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/OutputSegment.cpp b/contrib/llvm-project/lld/MachO/OutputSegment.cpp
new file mode 100644
index 000000000000..5e57c49f5c0a
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/OutputSegment.cpp
@@ -0,0 +1,67 @@
+//===- OutputSegment.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSegment.h"
+#include "InputSection.h"
+#include "MergedOutputSection.h"
+#include "SyntheticSections.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+static uint32_t initProt(StringRef name) {
+ if (name == segment_names::text)
+ return VM_PROT_READ | VM_PROT_EXECUTE;
+ if (name == segment_names::pageZero)
+ return 0;
+ if (name == segment_names::linkEdit)
+ return VM_PROT_READ;
+ return VM_PROT_READ | VM_PROT_WRITE;
+}
+
+static uint32_t maxProt(StringRef name) {
+ if (name == segment_names::pageZero)
+ return 0;
+ return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+}
+
+size_t OutputSegment::numNonHiddenSections() const {
+ size_t count = 0;
+ for (const OutputSection *osec : sections) {
+ count += (!osec->isHidden() ? 1 : 0);
+ }
+ return count;
+}
+
+void OutputSegment::addOutputSection(OutputSection *osec) {
+ osec->parent = this;
+ sections.push_back(osec);
+}
+
+static llvm::DenseMap<StringRef, OutputSegment *> nameToOutputSegment;
+std::vector<OutputSegment *> macho::outputSegments;
+
+OutputSegment *macho::getOrCreateOutputSegment(StringRef name) {
+ OutputSegment *&segRef = nameToOutputSegment[name];
+ if (segRef != nullptr)
+ return segRef;
+
+ segRef = make<OutputSegment>();
+ segRef->name = name;
+ segRef->maxProt = maxProt(name);
+ segRef->initProt = initProt(name);
+
+ outputSegments.push_back(segRef);
+ return segRef;
+}
diff --git a/contrib/llvm-project/lld/MachO/OutputSegment.h b/contrib/llvm-project/lld/MachO/OutputSegment.h
new file mode 100644
index 000000000000..d977c281272f
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/OutputSegment.h
@@ -0,0 +1,62 @@
+//===- OutputSegment.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_MACHO_OUTPUT_SEGMENT_H
+#define LLD_MACHO_OUTPUT_SEGMENT_H
+
+#include "OutputSection.h"
+#include "lld/Common/LLVM.h"
+
+namespace lld {
+namespace macho {
+
+namespace segment_names {
+
+constexpr const char pageZero[] = "__PAGEZERO";
+constexpr const char text[] = "__TEXT";
+constexpr const char data[] = "__DATA";
+constexpr const char linkEdit[] = "__LINKEDIT";
+constexpr const char dataConst[] = "__DATA_CONST";
+
+} // namespace segment_names
+
+class OutputSection;
+class InputSection;
+
+class OutputSegment {
+public:
+ const OutputSection *firstSection() const { return sections.front(); }
+ const OutputSection *lastSection() const { return sections.back(); }
+
+ void addOutputSection(OutputSection *os);
+ void sortOutputSections(
+ llvm::function_ref<bool(OutputSection *, OutputSection *)> comparator) {
+ llvm::stable_sort(sections, comparator);
+ }
+
+ const std::vector<OutputSection *> &getSections() const { return sections; }
+ size_t numNonHiddenSections() const;
+
+ uint64_t fileOff = 0;
+ StringRef name;
+ uint32_t maxProt = 0;
+ uint32_t initProt = 0;
+ uint8_t index;
+
+private:
+ std::vector<OutputSection *> sections;
+};
+
+extern std::vector<OutputSegment *> outputSegments;
+
+OutputSegment *getOrCreateOutputSegment(StringRef name);
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/SymbolTable.cpp b/contrib/llvm-project/lld/MachO/SymbolTable.cpp
new file mode 100644
index 000000000000..80e870d79890
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/SymbolTable.cpp
@@ -0,0 +1,87 @@
+//===- SymbolTable.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolTable.h"
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+Symbol *SymbolTable::find(StringRef name) {
+ auto it = symMap.find(llvm::CachedHashStringRef(name));
+ if (it == symMap.end())
+ return nullptr;
+ return symVector[it->second];
+}
+
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
+ auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()});
+
+ // Name already present in the symbol table.
+ if (!p.second)
+ return {symVector[p.first->second], false};
+
+ // Name is a new symbol.
+ Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ symVector.push_back(sym);
+ return {sym, true};
+}
+
+Symbol *SymbolTable::addDefined(StringRef name, InputSection *isec,
+ uint32_t value) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (!wasInserted && isa<Defined>(s))
+ error("duplicate symbol: " + name);
+
+ replaceSymbol<Defined>(s, name, isec, value);
+ return s;
+}
+
+Symbol *SymbolTable::addUndefined(StringRef name) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (wasInserted)
+ replaceSymbol<Undefined>(s, name);
+ else if (LazySymbol *lazy = dyn_cast<LazySymbol>(s))
+ lazy->fetchArchiveMember();
+ return s;
+}
+
+Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (wasInserted || isa<Undefined>(s))
+ replaceSymbol<DylibSymbol>(s, file, name);
+ return s;
+}
+
+Symbol *SymbolTable::addLazy(StringRef name, ArchiveFile *file,
+ const llvm::object::Archive::Symbol &sym) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (wasInserted)
+ replaceSymbol<LazySymbol>(s, file, sym);
+ else if (isa<Undefined>(s))
+ file->fetch(sym);
+ return s;
+}
+
+SymbolTable *macho::symtab;
diff --git a/contrib/llvm-project/lld/MachO/SymbolTable.h b/contrib/llvm-project/lld/MachO/SymbolTable.h
new file mode 100644
index 000000000000..2379008db56d
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/SymbolTable.h
@@ -0,0 +1,50 @@
+//===- SymbolTable.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_MACHO_SYMBOL_TABLE_H
+#define LLD_MACHO_SYMBOL_TABLE_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Object/Archive.h"
+
+namespace lld {
+namespace macho {
+
+class ArchiveFile;
+class DylibFile;
+class InputSection;
+class Symbol;
+
+class SymbolTable {
+public:
+ Symbol *addDefined(StringRef name, InputSection *isec, uint32_t value);
+
+ Symbol *addUndefined(StringRef name);
+
+ Symbol *addDylib(StringRef name, DylibFile *file);
+
+ Symbol *addLazy(StringRef name, ArchiveFile *file,
+ const llvm::object::Archive::Symbol &sym);
+
+ ArrayRef<Symbol *> getSymbols() const { return symVector; }
+ Symbol *find(StringRef name);
+
+private:
+ std::pair<Symbol *, bool> insert(StringRef name);
+ llvm::DenseMap<llvm::CachedHashStringRef, int> symMap;
+ std::vector<Symbol *> symVector;
+};
+
+extern SymbolTable *symtab;
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/Symbols.cpp b/contrib/llvm-project/lld/MachO/Symbols.cpp
new file mode 100644
index 000000000000..fbafa8a92a4f
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Symbols.cpp
@@ -0,0 +1,23 @@
+//===- Symbols.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "Symbols.h"
+#include "InputFiles.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+void LazySymbol::fetchArchiveMember() { file->fetch(sym); }
+
+// Returns a symbol for an error message.
+std::string lld::toString(const Symbol &sym) {
+ if (Optional<std::string> s = demangleItanium(sym.getName()))
+ return *s;
+ return std::string(sym.getName());
+}
diff --git a/contrib/llvm-project/lld/MachO/Symbols.h b/contrib/llvm-project/lld/MachO/Symbols.h
new file mode 100644
index 000000000000..63748ee48324
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Symbols.h
@@ -0,0 +1,138 @@
+//===- Symbols.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_MACHO_SYMBOLS_H
+#define LLD_MACHO_SYMBOLS_H
+
+#include "InputSection.h"
+#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Strings.h"
+#include "llvm/Object/Archive.h"
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class DylibFile;
+class ArchiveFile;
+
+struct StringRefZ {
+ StringRefZ(const char *s) : data(s), size(-1) {}
+ StringRefZ(StringRef s) : data(s.data()), size(s.size()) {}
+
+ const char *data;
+ const uint32_t size;
+};
+
+class Symbol {
+public:
+ enum Kind {
+ DefinedKind,
+ UndefinedKind,
+ DylibKind,
+ LazyKind,
+ };
+
+ Kind kind() const { return static_cast<Kind>(symbolKind); }
+
+ StringRef getName() const { return {name.data, name.size}; }
+
+ uint64_t getVA() const;
+
+ uint64_t getFileOffset() const;
+
+ uint32_t gotIndex = UINT32_MAX;
+
+protected:
+ Symbol(Kind k, StringRefZ name) : symbolKind(k), name(name) {}
+
+ Kind symbolKind;
+ StringRefZ name;
+};
+
+class Defined : public Symbol {
+public:
+ Defined(StringRefZ name, InputSection *isec, uint32_t value)
+ : Symbol(DefinedKind, name), isec(isec), value(value) {}
+
+ InputSection *isec;
+ uint32_t value;
+
+ static bool classof(const Symbol *s) { return s->kind() == DefinedKind; }
+};
+
+class Undefined : public Symbol {
+public:
+ Undefined(StringRefZ name) : Symbol(UndefinedKind, name) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
+};
+
+class DylibSymbol : public Symbol {
+public:
+ DylibSymbol(DylibFile *file, StringRefZ name)
+ : Symbol(DylibKind, name), file(file) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == DylibKind; }
+
+ DylibFile *file;
+ uint32_t stubsIndex = UINT32_MAX;
+ uint32_t lazyBindOffset = UINT32_MAX;
+};
+
+class LazySymbol : public Symbol {
+public:
+ LazySymbol(ArchiveFile *file, const llvm::object::Archive::Symbol &sym)
+ : Symbol(LazyKind, sym.getName()), file(file), sym(sym) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
+
+ void fetchArchiveMember();
+
+private:
+ ArchiveFile *file;
+ const llvm::object::Archive::Symbol sym;
+};
+
+inline uint64_t Symbol::getVA() const {
+ if (auto *d = dyn_cast<Defined>(this))
+ return d->isec->getVA() + d->value;
+ return 0;
+}
+
+inline uint64_t Symbol::getFileOffset() const {
+ if (auto *d = dyn_cast<Defined>(this))
+ return d->isec->getFileOffset() + d->value;
+ llvm_unreachable("attempt to get an offset from an undefined symbol");
+}
+
+union SymbolUnion {
+ alignas(Defined) char a[sizeof(Defined)];
+ alignas(Undefined) char b[sizeof(Undefined)];
+ alignas(DylibSymbol) char c[sizeof(DylibSymbol)];
+ alignas(LazySymbol) char d[sizeof(LazySymbol)];
+};
+
+template <typename T, typename... ArgT>
+void replaceSymbol(Symbol *s, ArgT &&... arg) {
+ static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
+ static_assert(alignof(T) <= alignof(SymbolUnion),
+ "SymbolUnion not aligned enough");
+ assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
+ "Not a Symbol");
+
+ new (s) T(std::forward<ArgT>(arg)...);
+}
+
+} // namespace macho
+
+std::string toString(const macho::Symbol &);
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/SyntheticSections.cpp b/contrib/llvm-project/lld/MachO/SyntheticSections.cpp
new file mode 100644
index 000000000000..cc0d5a93c40d
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/SyntheticSections.cpp
@@ -0,0 +1,409 @@
+//===- SyntheticSections.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "SyntheticSections.h"
+#include "Config.h"
+#include "ExportTrie.h"
+#include "InputFiles.h"
+#include "MachOStructs.h"
+#include "MergedOutputSection.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Writer.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/LEB128.h"
+
+using namespace llvm;
+using namespace llvm::support;
+using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::macho;
+
+InStruct macho::in;
+std::vector<SyntheticSection *> macho::syntheticSections;
+
+SyntheticSection::SyntheticSection(const char *segname, const char *name)
+ : OutputSection(SyntheticKind, name), segname(segname) {
+ syntheticSections.push_back(this);
+}
+
+// dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts
+// from the beginning of the file (i.e. the header).
+MachHeaderSection::MachHeaderSection()
+ : SyntheticSection(segment_names::text, section_names::header) {}
+
+void MachHeaderSection::addLoadCommand(LoadCommand *lc) {
+ loadCommands.push_back(lc);
+ sizeOfCmds += lc->getSize();
+}
+
+uint64_t MachHeaderSection::getSize() const {
+ return sizeof(MachO::mach_header_64) + sizeOfCmds;
+}
+
+void MachHeaderSection::writeTo(uint8_t *buf) const {
+ auto *hdr = reinterpret_cast<MachO::mach_header_64 *>(buf);
+ hdr->magic = MachO::MH_MAGIC_64;
+ hdr->cputype = MachO::CPU_TYPE_X86_64;
+ hdr->cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL | MachO::CPU_SUBTYPE_LIB64;
+ hdr->filetype = config->outputType;
+ hdr->ncmds = loadCommands.size();
+ hdr->sizeofcmds = sizeOfCmds;
+ hdr->flags = MachO::MH_NOUNDEFS | MachO::MH_DYLDLINK | MachO::MH_TWOLEVEL;
+ if (config->outputType == MachO::MH_DYLIB && !config->hasReexports)
+ hdr->flags |= MachO::MH_NO_REEXPORTED_DYLIBS;
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(hdr + 1);
+ for (LoadCommand *lc : loadCommands) {
+ lc->writeTo(p);
+ p += lc->getSize();
+ }
+}
+
+PageZeroSection::PageZeroSection()
+ : SyntheticSection(segment_names::pageZero, section_names::pageZero) {}
+
+GotSection::GotSection()
+ : SyntheticSection(segment_names::dataConst, section_names::got) {
+ align = 8;
+ flags = MachO::S_NON_LAZY_SYMBOL_POINTERS;
+
+ // TODO: section_64::reserved1 should be an index into the indirect symbol
+ // table, which we do not currently emit
+}
+
+void GotSection::addEntry(Symbol &sym) {
+ if (entries.insert(&sym)) {
+ sym.gotIndex = entries.size() - 1;
+ }
+}
+
+void GotSection::writeTo(uint8_t *buf) const {
+ for (size_t i = 0, n = entries.size(); i < n; ++i)
+ if (auto *defined = dyn_cast<Defined>(entries[i]))
+ write64le(&buf[i * WordSize], defined->getVA());
+}
+
+BindingSection::BindingSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::binding) {}
+
+bool BindingSection::isNeeded() const {
+ return bindings.size() != 0 || in.got->isNeeded();
+}
+
+namespace {
+struct Binding {
+ OutputSegment *segment = nullptr;
+ uint64_t offset = 0;
+ int64_t addend = 0;
+ uint8_t ordinal = 0;
+};
+} // namespace
+
+// Encode a sequence of opcodes that tell dyld to write the address of dysym +
+// addend at osec->addr + outSecOff.
+//
+// The bind opcode "interpreter" remembers the values of each binding field, so
+// we only need to encode the differences between bindings. Hence the use of
+// lastBinding.
+static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
+ uint64_t outSecOff, int64_t addend,
+ Binding &lastBinding, raw_svector_ostream &os) {
+ using namespace llvm::MachO;
+ OutputSegment *seg = osec->parent;
+ uint64_t offset = osec->getSegmentOffset() + outSecOff;
+ if (lastBinding.segment != seg) {
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
+ seg->index);
+ encodeULEB128(offset, os);
+ lastBinding.segment = seg;
+ lastBinding.offset = offset;
+ } else if (lastBinding.offset != offset) {
+ assert(lastBinding.offset <= offset);
+ os << static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB);
+ encodeULEB128(offset - lastBinding.offset, os);
+ lastBinding.offset = offset;
+ }
+
+ if (lastBinding.ordinal != dysym.file->ordinal) {
+ if (dysym.file->ordinal <= BIND_IMMEDIATE_MASK) {
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ dysym.file->ordinal);
+ } else {
+ error("TODO: Support larger dylib symbol ordinals");
+ return;
+ }
+ lastBinding.ordinal = dysym.file->ordinal;
+ }
+
+ if (lastBinding.addend != addend) {
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_ADDEND_SLEB);
+ encodeSLEB128(addend, os);
+ lastBinding.addend = addend;
+ }
+
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ << dysym.getName() << '\0'
+ << static_cast<uint8_t>(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER)
+ << static_cast<uint8_t>(BIND_OPCODE_DO_BIND);
+ // DO_BIND causes dyld to both perform the binding and increment the offset
+ lastBinding.offset += WordSize;
+}
+
+// Emit bind opcodes, which are a stream of byte-sized opcodes that dyld
+// interprets to update a record with the following fields:
+// * segment index (of the segment to write the symbol addresses to, typically
+// the __DATA_CONST segment which contains the GOT)
+// * offset within the segment, indicating the next location to write a binding
+// * symbol type
+// * symbol library ordinal (the index of its library's LC_LOAD_DYLIB command)
+// * symbol name
+// * addend
+// When dyld sees BIND_OPCODE_DO_BIND, it uses the current record state to bind
+// a symbol in the GOT, and increments the segment offset to point to the next
+// entry. It does *not* clear the record state after doing the bind, so
+// subsequent opcodes only need to encode the differences between bindings.
+void BindingSection::finalizeContents() {
+ raw_svector_ostream os{contents};
+ Binding lastBinding;
+ bool didEncode = false;
+ size_t gotIdx = 0;
+ for (const Symbol *sym : in.got->getEntries()) {
+ if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
+ didEncode = true;
+ encodeBinding(*dysym, in.got, gotIdx * WordSize, 0, lastBinding, os);
+ }
+ ++gotIdx;
+ }
+
+ // Sorting the relocations by segment and address allows us to encode them
+ // more compactly.
+ llvm::sort(bindings, [](const BindingEntry &a, const BindingEntry &b) {
+ OutputSegment *segA = a.isec->parent->parent;
+ OutputSegment *segB = b.isec->parent->parent;
+ if (segA != segB)
+ return segA->fileOff < segB->fileOff;
+ OutputSection *osecA = a.isec->parent;
+ OutputSection *osecB = b.isec->parent;
+ if (osecA != osecB)
+ return osecA->addr < osecB->addr;
+ if (a.isec != b.isec)
+ return a.isec->outSecOff < b.isec->outSecOff;
+ return a.offset < b.offset;
+ });
+ for (const BindingEntry &b : bindings) {
+ didEncode = true;
+ encodeBinding(*b.dysym, b.isec->parent, b.isec->outSecOff + b.offset,
+ b.addend, lastBinding, os);
+ }
+ if (didEncode)
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_DONE);
+}
+
+void BindingSection::writeTo(uint8_t *buf) const {
+ memcpy(buf, contents.data(), contents.size());
+}
+
+StubsSection::StubsSection()
+ : SyntheticSection(segment_names::text, "__stubs") {}
+
+uint64_t StubsSection::getSize() const {
+ return entries.size() * target->stubSize;
+}
+
+void StubsSection::writeTo(uint8_t *buf) const {
+ size_t off = 0;
+ for (const DylibSymbol *sym : in.stubs->getEntries()) {
+ target->writeStub(buf + off, *sym);
+ off += target->stubSize;
+ }
+}
+
+void StubsSection::addEntry(DylibSymbol &sym) {
+ if (entries.insert(&sym))
+ sym.stubsIndex = entries.size() - 1;
+}
+
+StubHelperSection::StubHelperSection()
+ : SyntheticSection(segment_names::text, "__stub_helper") {}
+
+uint64_t StubHelperSection::getSize() const {
+ return target->stubHelperHeaderSize +
+ in.stubs->getEntries().size() * target->stubHelperEntrySize;
+}
+
+bool StubHelperSection::isNeeded() const {
+ return !in.stubs->getEntries().empty();
+}
+
+void StubHelperSection::writeTo(uint8_t *buf) const {
+ target->writeStubHelperHeader(buf);
+ size_t off = target->stubHelperHeaderSize;
+ for (const DylibSymbol *sym : in.stubs->getEntries()) {
+ target->writeStubHelperEntry(buf + off, *sym, addr + off);
+ off += target->stubHelperEntrySize;
+ }
+}
+
+void StubHelperSection::setup() {
+ stubBinder = dyn_cast_or_null<DylibSymbol>(symtab->find("dyld_stub_binder"));
+ if (stubBinder == nullptr) {
+ error("symbol dyld_stub_binder not found (normally in libSystem.dylib). "
+ "Needed to perform lazy binding.");
+ return;
+ }
+ in.got->addEntry(*stubBinder);
+
+ inputSections.push_back(in.imageLoaderCache);
+ symtab->addDefined("__dyld_private", in.imageLoaderCache, 0);
+}
+
+ImageLoaderCacheSection::ImageLoaderCacheSection() {
+ segname = segment_names::data;
+ name = "__data";
+ uint8_t *arr = bAlloc.Allocate<uint8_t>(WordSize);
+ memset(arr, 0, WordSize);
+ data = {arr, WordSize};
+}
+
+LazyPointerSection::LazyPointerSection()
+ : SyntheticSection(segment_names::data, "__la_symbol_ptr") {
+ align = 8;
+ flags = MachO::S_LAZY_SYMBOL_POINTERS;
+}
+
+uint64_t LazyPointerSection::getSize() const {
+ return in.stubs->getEntries().size() * WordSize;
+}
+
+bool LazyPointerSection::isNeeded() const {
+ return !in.stubs->getEntries().empty();
+}
+
+void LazyPointerSection::writeTo(uint8_t *buf) const {
+ size_t off = 0;
+ for (const DylibSymbol *sym : in.stubs->getEntries()) {
+ uint64_t stubHelperOffset = target->stubHelperHeaderSize +
+ sym->stubsIndex * target->stubHelperEntrySize;
+ write64le(buf + off, in.stubHelper->addr + stubHelperOffset);
+ off += WordSize;
+ }
+}
+
+LazyBindingSection::LazyBindingSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::lazyBinding) {}
+
+bool LazyBindingSection::isNeeded() const { return in.stubs->isNeeded(); }
+
+void LazyBindingSection::finalizeContents() {
+ // TODO: Just precompute output size here instead of writing to a temporary
+ // buffer
+ for (DylibSymbol *sym : in.stubs->getEntries())
+ sym->lazyBindOffset = encode(*sym);
+}
+
+void LazyBindingSection::writeTo(uint8_t *buf) const {
+ memcpy(buf, contents.data(), contents.size());
+}
+
+// Unlike the non-lazy binding section, the bind opcodes in this section aren't
+// interpreted all at once. Rather, dyld will start interpreting opcodes at a
+// given offset, typically only binding a single symbol before it finds a
+// BIND_OPCODE_DONE terminator. As such, unlike in the non-lazy-binding case,
+// we cannot encode just the differences between symbols; we have to emit the
+// complete bind information for each symbol.
+uint32_t LazyBindingSection::encode(const DylibSymbol &sym) {
+ uint32_t opstreamOffset = contents.size();
+ OutputSegment *dataSeg = in.lazyPointers->parent;
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
+ dataSeg->index);
+ uint64_t offset = in.lazyPointers->addr - dataSeg->firstSection()->addr +
+ sym.stubsIndex * WordSize;
+ encodeULEB128(offset, os);
+ if (sym.file->ordinal <= MachO::BIND_IMMEDIATE_MASK)
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ sym.file->ordinal);
+ else
+ fatal("TODO: Support larger dylib symbol ordinals");
+
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ << sym.getName() << '\0'
+ << static_cast<uint8_t>(MachO::BIND_OPCODE_DO_BIND)
+ << static_cast<uint8_t>(MachO::BIND_OPCODE_DONE);
+ return opstreamOffset;
+}
+
+ExportSection::ExportSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::export_) {}
+
+void ExportSection::finalizeContents() {
+ // TODO: We should check symbol visibility.
+ for (const Symbol *sym : symtab->getSymbols())
+ if (auto *defined = dyn_cast<Defined>(sym))
+ trieBuilder.addSymbol(*defined);
+ size = trieBuilder.build();
+}
+
+void ExportSection::writeTo(uint8_t *buf) const { trieBuilder.writeTo(buf); }
+
+SymtabSection::SymtabSection(StringTableSection &stringTableSection)
+ : SyntheticSection(segment_names::linkEdit, section_names::symbolTable),
+ stringTableSection(stringTableSection) {
+ // TODO: When we introduce the SyntheticSections superclass, we should make
+ // all synthetic sections aligned to WordSize by default.
+ align = WordSize;
+}
+
+uint64_t SymtabSection::getSize() const {
+ return symbols.size() * sizeof(structs::nlist_64);
+}
+
+void SymtabSection::finalizeContents() {
+ // TODO support other symbol types
+ for (Symbol *sym : symtab->getSymbols())
+ if (isa<Defined>(sym))
+ symbols.push_back({sym, stringTableSection.addString(sym->getName())});
+}
+
+void SymtabSection::writeTo(uint8_t *buf) const {
+ auto *nList = reinterpret_cast<structs::nlist_64 *>(buf);
+ for (const SymtabEntry &entry : symbols) {
+ nList->n_strx = entry.strx;
+ // TODO support other symbol types
+ // TODO populate n_desc
+ if (auto *defined = dyn_cast<Defined>(entry.sym)) {
+ nList->n_type = MachO::N_EXT | MachO::N_SECT;
+ nList->n_sect = defined->isec->parent->index;
+ // For the N_SECT symbol type, n_value is the address of the symbol
+ nList->n_value = defined->value + defined->isec->getVA();
+ }
+ ++nList;
+ }
+}
+
+StringTableSection::StringTableSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::stringTable) {}
+
+uint32_t StringTableSection::addString(StringRef str) {
+ uint32_t strx = size;
+ strings.push_back(str);
+ size += str.size() + 1; // account for null terminator
+ return strx;
+}
+
+void StringTableSection::writeTo(uint8_t *buf) const {
+ uint32_t off = 0;
+ for (StringRef str : strings) {
+ memcpy(buf + off, str.data(), str.size());
+ off += str.size() + 1; // account for null terminator
+ }
+}
diff --git a/contrib/llvm-project/lld/MachO/SyntheticSections.h b/contrib/llvm-project/lld/MachO/SyntheticSections.h
new file mode 100644
index 000000000000..a8fbf6c8a265
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/SyntheticSections.h
@@ -0,0 +1,290 @@
+//===- SyntheticSections.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_MACHO_SYNTHETIC_SECTIONS_H
+#define LLD_MACHO_SYNTHETIC_SECTIONS_H
+
+#include "Config.h"
+#include "ExportTrie.h"
+#include "InputSection.h"
+#include "OutputSection.h"
+#include "Target.h"
+
+#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace lld {
+namespace macho {
+
+namespace section_names {
+
+constexpr const char pageZero[] = "__pagezero";
+constexpr const char header[] = "__mach_header";
+constexpr const char binding[] = "__binding";
+constexpr const char lazyBinding[] = "__lazy_binding";
+constexpr const char export_[] = "__export";
+constexpr const char symbolTable[] = "__symbol_table";
+constexpr const char stringTable[] = "__string_table";
+constexpr const char got[] = "__got";
+
+} // namespace section_names
+
+class DylibSymbol;
+class LoadCommand;
+
+class SyntheticSection : public OutputSection {
+public:
+ SyntheticSection(const char *segname, const char *name);
+ virtual ~SyntheticSection() = default;
+
+ static bool classof(const OutputSection *sec) {
+ return sec->kind() == SyntheticKind;
+ }
+
+ const StringRef segname;
+};
+
+// The header of the Mach-O file, which must have a file offset of zero.
+class MachHeaderSection : public SyntheticSection {
+public:
+ MachHeaderSection();
+ void addLoadCommand(LoadCommand *);
+ bool isHidden() const override { return true; }
+ uint64_t getSize() const override;
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ std::vector<LoadCommand *> loadCommands;
+ uint32_t sizeOfCmds = 0;
+};
+
+// A hidden section that exists solely for the purpose of creating the
+// __PAGEZERO segment, which is used to catch null pointer dereferences.
+class PageZeroSection : public SyntheticSection {
+public:
+ PageZeroSection();
+ bool isHidden() const override { return true; }
+ uint64_t getSize() const override { return PageZeroSize; }
+ uint64_t getFileSize() const override { return 0; }
+ void writeTo(uint8_t *buf) const override {}
+};
+
+// This section will be populated by dyld with addresses to non-lazily-loaded
+// dylib symbols.
+class GotSection : public SyntheticSection {
+public:
+ GotSection();
+
+ const llvm::SetVector<const Symbol *> &getEntries() const { return entries; }
+
+ bool isNeeded() const override { return !entries.empty(); }
+
+ uint64_t getSize() const override { return entries.size() * WordSize; }
+
+ void writeTo(uint8_t *buf) const override;
+
+ void addEntry(Symbol &sym);
+
+private:
+ llvm::SetVector<const Symbol *> entries;
+};
+
+struct BindingEntry {
+ const DylibSymbol *dysym;
+ const InputSection *isec;
+ uint64_t offset;
+ int64_t addend;
+ BindingEntry(const DylibSymbol *dysym, const InputSection *isec,
+ uint64_t offset, int64_t addend)
+ : dysym(dysym), isec(isec), offset(offset), addend(addend) {}
+};
+
+// Stores bind opcodes for telling dyld which symbols to load non-lazily.
+class BindingSection : public SyntheticSection {
+public:
+ BindingSection();
+ void finalizeContents();
+ uint64_t getSize() const override { return contents.size(); }
+ // Like other sections in __LINKEDIT, the binding section is special: its
+ // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
+ // section headers.
+ bool isHidden() const override { return true; }
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+
+ void addEntry(const DylibSymbol *dysym, const InputSection *isec,
+ uint64_t offset, int64_t addend) {
+ bindings.emplace_back(dysym, isec, offset, addend);
+ }
+
+private:
+ std::vector<BindingEntry> bindings;
+ SmallVector<char, 128> contents;
+};
+
+// The following sections implement lazy symbol binding -- very similar to the
+// PLT mechanism in ELF.
+//
+// ELF's .plt section is broken up into two sections in Mach-O: StubsSection and
+// StubHelperSection. Calls to functions in dylibs will end up calling into
+// StubsSection, which contains indirect jumps to addresses stored in the
+// LazyPointerSection (the counterpart to ELF's .plt.got).
+//
+// Initially, the LazyPointerSection contains addresses that point into one of
+// the entry points in the middle of the StubHelperSection. The code in
+// StubHelperSection will push on the stack an offset into the
+// LazyBindingSection. The push is followed by a jump to the beginning of the
+// StubHelperSection (similar to PLT0), which then calls into dyld_stub_binder.
+// dyld_stub_binder is a non-lazily-bound symbol, so this call looks it up in
+// the GOT.
+//
+// The stub binder will look up the bind opcodes in the LazyBindingSection at
+// the given offset. The bind opcodes will tell the binder to update the address
+// in the LazyPointerSection to point to the symbol, so that subsequent calls
+// don't have to redo the symbol resolution. The binder will then jump to the
+// resolved symbol.
+
+class StubsSection : public SyntheticSection {
+public:
+ StubsSection();
+ uint64_t getSize() const override;
+ bool isNeeded() const override { return !entries.empty(); }
+ void writeTo(uint8_t *buf) const override;
+
+ const llvm::SetVector<DylibSymbol *> &getEntries() const { return entries; }
+
+ void addEntry(DylibSymbol &sym);
+
+private:
+ llvm::SetVector<DylibSymbol *> entries;
+};
+
+class StubHelperSection : public SyntheticSection {
+public:
+ StubHelperSection();
+ uint64_t getSize() const override;
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+
+ void setup();
+
+ DylibSymbol *stubBinder = nullptr;
+};
+
+// This section contains space for just a single word, and will be used by dyld
+// to cache an address to the image loader it uses. Note that unlike the other
+// synthetic sections, which are OutputSections, the ImageLoaderCacheSection is
+// an InputSection that gets merged into the __data OutputSection.
+class ImageLoaderCacheSection : public InputSection {
+public:
+ ImageLoaderCacheSection();
+ uint64_t getSize() const override { return WordSize; }
+};
+
+class LazyPointerSection : public SyntheticSection {
+public:
+ LazyPointerSection();
+ uint64_t getSize() const override;
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+};
+
+class LazyBindingSection : public SyntheticSection {
+public:
+ LazyBindingSection();
+ void finalizeContents();
+ uint64_t getSize() const override { return contents.size(); }
+ uint32_t encode(const DylibSymbol &);
+ // Like other sections in __LINKEDIT, the lazy binding section is special: its
+ // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
+ // section headers.
+ bool isHidden() const override { return true; }
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ SmallVector<char, 128> contents;
+ llvm::raw_svector_ostream os{contents};
+};
+
+// Stores a trie that describes the set of exported symbols.
+class ExportSection : public SyntheticSection {
+public:
+ ExportSection();
+ void finalizeContents();
+ uint64_t getSize() const override { return size; }
+ // Like other sections in __LINKEDIT, the export section is special: its
+ // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
+ // section headers.
+ bool isHidden() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ TrieBuilder trieBuilder;
+ size_t size = 0;
+};
+
+// Stores the strings referenced by the symbol table.
+class StringTableSection : public SyntheticSection {
+public:
+ StringTableSection();
+ // Returns the start offset of the added string.
+ uint32_t addString(StringRef);
+ uint64_t getSize() const override { return size; }
+ // Like other sections in __LINKEDIT, the string table section is special: its
+ // offsets are recorded in the LC_SYMTAB load command, instead of in section
+ // headers.
+ bool isHidden() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ // An n_strx value of 0 always indicates the empty string, so we must locate
+ // our non-empty string values at positive offsets in the string table.
+ // Therefore we insert a dummy value at position zero.
+ std::vector<StringRef> strings{"\0"};
+ size_t size = 1;
+};
+
+struct SymtabEntry {
+ Symbol *sym;
+ size_t strx;
+};
+
+class SymtabSection : public SyntheticSection {
+public:
+ SymtabSection(StringTableSection &);
+ void finalizeContents();
+ size_t getNumSymbols() const { return symbols.size(); }
+ uint64_t getSize() const override;
+ // Like other sections in __LINKEDIT, the symtab section is special: its
+ // offsets are recorded in the LC_SYMTAB load command, instead of in section
+ // headers.
+ bool isHidden() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ StringTableSection &stringTableSection;
+ std::vector<SymtabEntry> symbols;
+};
+
+struct InStruct {
+ BindingSection *binding = nullptr;
+ GotSection *got = nullptr;
+ LazyPointerSection *lazyPointers = nullptr;
+ StubsSection *stubs = nullptr;
+ StubHelperSection *stubHelper = nullptr;
+ ImageLoaderCacheSection *imageLoaderCache = nullptr;
+};
+
+extern InStruct in;
+extern std::vector<SyntheticSection *> syntheticSections;
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/Common/Threads.cpp b/contrib/llvm-project/lld/MachO/Target.cpp
index af04972a3760..0f70776a507f 100644
--- a/contrib/llvm-project/lld/Common/Threads.cpp
+++ b/contrib/llvm-project/lld/MachO/Target.cpp
@@ -1,4 +1,4 @@
-//===- Threads.cpp --------------------------------------------------------===//
+//===- Target.cpp ---------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,6 +6,9 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Common/Threads.h"
+#include "Target.h"
-bool lld::threadsEnabled = true;
+using namespace lld;
+using namespace lld::macho;
+
+TargetInfo *macho::target = nullptr;
diff --git a/contrib/llvm-project/lld/MachO/Target.h b/contrib/llvm-project/lld/MachO/Target.h
new file mode 100644
index 000000000000..8ea1bde12307
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Target.h
@@ -0,0 +1,75 @@
+//===- Target.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_MACHO_TARGET_H
+#define LLD_MACHO_TARGET_H
+
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include <cstddef>
+#include <cstdint>
+
+namespace lld {
+namespace macho {
+
+class Symbol;
+class DylibSymbol;
+class InputSection;
+struct Reloc;
+
+enum : uint64_t {
+ // We are currently only supporting 64-bit targets since macOS and iOS are
+ // deprecating 32-bit apps.
+ WordSize = 8,
+ PageSize = 4096,
+ PageZeroSize = 1ull << 32, // XXX should be 4096 for 32-bit targets
+ MaxAlignmentPowerOf2 = 32,
+};
+
+class TargetInfo {
+public:
+ virtual ~TargetInfo() = default;
+
+ // Validate the relocation structure and get its addend.
+ virtual uint64_t
+ getImplicitAddend(llvm::MemoryBufferRef, const llvm::MachO::section_64 &,
+ const llvm::MachO::relocation_info &) const = 0;
+ virtual void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const = 0;
+
+ // Write code for lazy binding. See the comments on StubsSection for more
+ // details.
+ virtual void writeStub(uint8_t *buf, const DylibSymbol &) const = 0;
+ virtual void writeStubHelperHeader(uint8_t *buf) const = 0;
+ virtual void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+ uint64_t entryAddr) const = 0;
+
+ // Symbols may be referenced via either the GOT or the stubs section,
+ // depending on the relocation type. prepareSymbolRelocation() will set up the
+ // GOT/stubs entries, and getSymbolVA() will return the addresses of those
+ // entries.
+ virtual void prepareSymbolRelocation(Symbol &, const InputSection *,
+ const Reloc &) = 0;
+ virtual uint64_t getSymbolVA(const Symbol &, uint8_t type) const = 0;
+
+ uint32_t cpuType;
+ uint32_t cpuSubtype;
+
+ size_t stubSize;
+ size_t stubHelperHeaderSize;
+ size_t stubHelperEntrySize;
+};
+
+TargetInfo *createX86_64TargetInfo();
+
+extern TargetInfo *target;
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/MachO/Writer.cpp b/contrib/llvm-project/lld/MachO/Writer.cpp
new file mode 100644
index 000000000000..03000a7f437e
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Writer.cpp
@@ -0,0 +1,542 @@
+//===- Writer.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+#include "Config.h"
+#include "InputFiles.h"
+#include "InputSection.h"
+#include "MergedOutputSection.h"
+#include "OutputSection.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+namespace {
+class LCLinkEdit;
+class LCDyldInfo;
+class LCSymtab;
+
+class Writer {
+public:
+ Writer() : buffer(errorHandler().outputBuffer) {}
+
+ void scanRelocations();
+ void createOutputSections();
+ void createLoadCommands();
+ void assignAddresses(OutputSegment *);
+ void createSymtabContents();
+
+ void openFile();
+ void writeSections();
+
+ void run();
+
+ std::unique_ptr<FileOutputBuffer> &buffer;
+ uint64_t addr = 0;
+ uint64_t fileOff = 0;
+ MachHeaderSection *headerSection = nullptr;
+ LazyBindingSection *lazyBindingSection = nullptr;
+ ExportSection *exportSection = nullptr;
+ StringTableSection *stringTableSection = nullptr;
+ SymtabSection *symtabSection = nullptr;
+};
+
+// LC_DYLD_INFO_ONLY stores the offsets of symbol import/export information.
+class LCDyldInfo : public LoadCommand {
+public:
+ LCDyldInfo(BindingSection *bindingSection,
+ LazyBindingSection *lazyBindingSection,
+ ExportSection *exportSection)
+ : bindingSection(bindingSection), lazyBindingSection(lazyBindingSection),
+ exportSection(exportSection) {}
+
+ uint32_t getSize() const override { return sizeof(dyld_info_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dyld_info_command *>(buf);
+ c->cmd = LC_DYLD_INFO_ONLY;
+ c->cmdsize = getSize();
+ if (bindingSection->isNeeded()) {
+ c->bind_off = bindingSection->fileOff;
+ c->bind_size = bindingSection->getFileSize();
+ }
+ if (lazyBindingSection->isNeeded()) {
+ c->lazy_bind_off = lazyBindingSection->fileOff;
+ c->lazy_bind_size = lazyBindingSection->getFileSize();
+ }
+ if (exportSection->isNeeded()) {
+ c->export_off = exportSection->fileOff;
+ c->export_size = exportSection->getFileSize();
+ }
+ }
+
+ BindingSection *bindingSection;
+ LazyBindingSection *lazyBindingSection;
+ ExportSection *exportSection;
+};
+
+class LCDysymtab : public LoadCommand {
+public:
+ uint32_t getSize() const override { return sizeof(dysymtab_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dysymtab_command *>(buf);
+ c->cmd = LC_DYSYMTAB;
+ c->cmdsize = getSize();
+ }
+};
+
+class LCSegment : public LoadCommand {
+public:
+ LCSegment(StringRef name, OutputSegment *seg) : name(name), seg(seg) {}
+
+ uint32_t getSize() const override {
+ return sizeof(segment_command_64) +
+ seg->numNonHiddenSections() * sizeof(section_64);
+ }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<segment_command_64 *>(buf);
+ buf += sizeof(segment_command_64);
+
+ c->cmd = LC_SEGMENT_64;
+ c->cmdsize = getSize();
+ memcpy(c->segname, name.data(), name.size());
+ c->fileoff = seg->fileOff;
+ c->maxprot = seg->maxProt;
+ c->initprot = seg->initProt;
+
+ if (seg->getSections().empty())
+ return;
+
+ c->vmaddr = seg->firstSection()->addr;
+ c->vmsize =
+ seg->lastSection()->addr + seg->lastSection()->getSize() - c->vmaddr;
+ c->nsects = seg->numNonHiddenSections();
+
+ for (OutputSection *osec : seg->getSections()) {
+ c->filesize += osec->getFileSize();
+
+ if (osec->isHidden())
+ continue;
+
+ auto *sectHdr = reinterpret_cast<section_64 *>(buf);
+ buf += sizeof(section_64);
+
+ memcpy(sectHdr->sectname, osec->name.data(), osec->name.size());
+ memcpy(sectHdr->segname, name.data(), name.size());
+
+ sectHdr->addr = osec->addr;
+ sectHdr->offset = osec->fileOff;
+ sectHdr->align = Log2_32(osec->align);
+ sectHdr->flags = osec->flags;
+ sectHdr->size = osec->getSize();
+ }
+ }
+
+private:
+ StringRef name;
+ OutputSegment *seg;
+};
+
+class LCMain : public LoadCommand {
+ uint32_t getSize() const override { return sizeof(entry_point_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<entry_point_command *>(buf);
+ c->cmd = LC_MAIN;
+ c->cmdsize = getSize();
+ c->entryoff = config->entry->getFileOffset();
+ c->stacksize = 0;
+ }
+};
+
+class LCSymtab : public LoadCommand {
+public:
+ LCSymtab(SymtabSection *symtabSection, StringTableSection *stringTableSection)
+ : symtabSection(symtabSection), stringTableSection(stringTableSection) {}
+
+ uint32_t getSize() const override { return sizeof(symtab_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<symtab_command *>(buf);
+ c->cmd = LC_SYMTAB;
+ c->cmdsize = getSize();
+ c->symoff = symtabSection->fileOff;
+ c->nsyms = symtabSection->getNumSymbols();
+ c->stroff = stringTableSection->fileOff;
+ c->strsize = stringTableSection->getFileSize();
+ }
+
+ SymtabSection *symtabSection = nullptr;
+ StringTableSection *stringTableSection = nullptr;
+};
+
+// There are several dylib load commands that share the same structure:
+// * LC_LOAD_DYLIB
+// * LC_ID_DYLIB
+// * LC_REEXPORT_DYLIB
+class LCDylib : public LoadCommand {
+public:
+ LCDylib(LoadCommandType type, StringRef path) : type(type), path(path) {}
+
+ uint32_t getSize() const override {
+ return alignTo(sizeof(dylib_command) + path.size() + 1, 8);
+ }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dylib_command *>(buf);
+ buf += sizeof(dylib_command);
+
+ c->cmd = type;
+ c->cmdsize = getSize();
+ c->dylib.name = sizeof(dylib_command);
+
+ memcpy(buf, path.data(), path.size());
+ buf[path.size()] = '\0';
+ }
+
+private:
+ LoadCommandType type;
+ StringRef path;
+};
+
+class LCLoadDylinker : public LoadCommand {
+public:
+ uint32_t getSize() const override {
+ return alignTo(sizeof(dylinker_command) + path.size() + 1, 8);
+ }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dylinker_command *>(buf);
+ buf += sizeof(dylinker_command);
+
+ c->cmd = LC_LOAD_DYLINKER;
+ c->cmdsize = getSize();
+ c->name = sizeof(dylinker_command);
+
+ memcpy(buf, path.data(), path.size());
+ buf[path.size()] = '\0';
+ }
+
+private:
+ // Recent versions of Darwin won't run any binary that has dyld at a
+ // different location.
+ const StringRef path = "/usr/lib/dyld";
+};
+} // namespace
+
+void Writer::scanRelocations() {
+ for (InputSection *isec : inputSections) {
+ for (Reloc &r : isec->relocs) {
+ if (auto *s = r.target.dyn_cast<lld::macho::Symbol *>()) {
+ if (isa<Undefined>(s))
+ error("undefined symbol " + s->getName() + ", referenced from " +
+ sys::path::filename(isec->file->getName()));
+ else
+ target->prepareSymbolRelocation(*s, isec, r);
+ }
+ }
+ }
+}
+
+void Writer::createLoadCommands() {
+ headerSection->addLoadCommand(
+ make<LCDyldInfo>(in.binding, lazyBindingSection, exportSection));
+ headerSection->addLoadCommand(
+ make<LCSymtab>(symtabSection, stringTableSection));
+ headerSection->addLoadCommand(make<LCDysymtab>());
+
+ switch (config->outputType) {
+ case MH_EXECUTE:
+ headerSection->addLoadCommand(make<LCMain>());
+ headerSection->addLoadCommand(make<LCLoadDylinker>());
+ break;
+ case MH_DYLIB:
+ headerSection->addLoadCommand(
+ make<LCDylib>(LC_ID_DYLIB, config->installName));
+ break;
+ default:
+ llvm_unreachable("unhandled output file type");
+ }
+
+ uint8_t segIndex = 0;
+ for (OutputSegment *seg : outputSegments) {
+ headerSection->addLoadCommand(make<LCSegment>(seg->name, seg));
+ seg->index = segIndex++;
+ }
+
+ uint64_t dylibOrdinal = 1;
+ for (InputFile *file : inputFiles) {
+ if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
+ headerSection->addLoadCommand(
+ make<LCDylib>(LC_LOAD_DYLIB, dylibFile->dylibName));
+ dylibFile->ordinal = dylibOrdinal++;
+
+ if (dylibFile->reexport)
+ headerSection->addLoadCommand(
+ make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->dylibName));
+ }
+ }
+}
+
+static size_t getSymbolPriority(const SymbolPriorityEntry &entry,
+ const InputFile &file) {
+ return std::max(entry.objectFiles.lookup(sys::path::filename(file.getName())),
+ entry.anyObjectFile);
+}
+
+// Each section gets assigned the priority of the highest-priority symbol it
+// contains.
+static DenseMap<const InputSection *, size_t> buildInputSectionPriorities() {
+ DenseMap<const InputSection *, size_t> sectionPriorities;
+
+ if (config->priorities.empty())
+ return sectionPriorities;
+
+ auto addSym = [&](Defined &sym) {
+ auto it = config->priorities.find(sym.getName());
+ if (it == config->priorities.end())
+ return;
+
+ SymbolPriorityEntry &entry = it->second;
+ size_t &priority = sectionPriorities[sym.isec];
+ priority = std::max(priority, getSymbolPriority(entry, *sym.isec->file));
+ };
+
+ // TODO: Make sure this handles weak symbols correctly.
+ for (InputFile *file : inputFiles)
+ if (isa<ObjFile>(file) || isa<ArchiveFile>(file))
+ for (lld::macho::Symbol *sym : file->symbols)
+ if (auto *d = dyn_cast<Defined>(sym))
+ addSym(*d);
+
+ return sectionPriorities;
+}
+
+static int segmentOrder(OutputSegment *seg) {
+ return StringSwitch<int>(seg->name)
+ .Case(segment_names::pageZero, -2)
+ .Case(segment_names::text, -1)
+ // Make sure __LINKEDIT is the last segment (i.e. all its hidden
+ // sections must be ordered after other sections).
+ .Case(segment_names::linkEdit, std::numeric_limits<int>::max())
+ .Default(0);
+}
+
+static int sectionOrder(OutputSection *osec) {
+ StringRef segname = osec->parent->name;
+ // Sections are uniquely identified by their segment + section name.
+ if (segname == segment_names::text) {
+ if (osec->name == section_names::header)
+ return -1;
+ } else if (segname == segment_names::linkEdit) {
+ return StringSwitch<int>(osec->name)
+ .Case(section_names::binding, -4)
+ .Case(section_names::export_, -3)
+ .Case(section_names::symbolTable, -2)
+ .Case(section_names::stringTable, -1)
+ .Default(0);
+ }
+ // ZeroFill sections must always be the at the end of their segments,
+ // otherwise subsequent sections may get overwritten with zeroes at runtime.
+ if (isZeroFill(osec->flags))
+ return std::numeric_limits<int>::max();
+ return 0;
+}
+
+template <typename T, typename F>
+static std::function<bool(T, T)> compareByOrder(F ord) {
+ return [=](T a, T b) { return ord(a) < ord(b); };
+}
+
+// Sorting only can happen once all outputs have been collected. Here we sort
+// segments, output sections within each segment, and input sections within each
+// output segment.
+static void sortSegmentsAndSections() {
+ llvm::stable_sort(outputSegments,
+ compareByOrder<OutputSegment *>(segmentOrder));
+
+ DenseMap<const InputSection *, size_t> isecPriorities =
+ buildInputSectionPriorities();
+
+ uint32_t sectionIndex = 0;
+ for (OutputSegment *seg : outputSegments) {
+ seg->sortOutputSections(compareByOrder<OutputSection *>(sectionOrder));
+ for (auto *osec : seg->getSections()) {
+ // Now that the output sections are sorted, assign the final
+ // output section indices.
+ if (!osec->isHidden())
+ osec->index = ++sectionIndex;
+
+ if (!isecPriorities.empty()) {
+ if (auto *merged = dyn_cast<MergedOutputSection>(osec)) {
+ llvm::stable_sort(merged->inputs,
+ [&](InputSection *a, InputSection *b) {
+ return isecPriorities[a] > isecPriorities[b];
+ });
+ }
+ }
+ }
+ }
+}
+
+void Writer::createOutputSections() {
+ // First, create hidden sections
+ headerSection = make<MachHeaderSection>();
+ lazyBindingSection = make<LazyBindingSection>();
+ stringTableSection = make<StringTableSection>();
+ symtabSection = make<SymtabSection>(*stringTableSection);
+ exportSection = make<ExportSection>();
+
+ switch (config->outputType) {
+ case MH_EXECUTE:
+ make<PageZeroSection>();
+ break;
+ case MH_DYLIB:
+ break;
+ default:
+ llvm_unreachable("unhandled output file type");
+ }
+
+ // Then merge input sections into output sections.
+ MapVector<std::pair<StringRef, StringRef>, MergedOutputSection *>
+ mergedOutputSections;
+ for (InputSection *isec : inputSections) {
+ MergedOutputSection *&osec =
+ mergedOutputSections[{isec->segname, isec->name}];
+ if (osec == nullptr)
+ osec = make<MergedOutputSection>(isec->name);
+ osec->mergeInput(isec);
+ }
+
+ for (const auto &it : mergedOutputSections) {
+ StringRef segname = it.first.first;
+ MergedOutputSection *osec = it.second;
+ getOrCreateOutputSegment(segname)->addOutputSection(osec);
+ }
+
+ for (SyntheticSection *ssec : syntheticSections) {
+ auto it = mergedOutputSections.find({ssec->segname, ssec->name});
+ if (it == mergedOutputSections.end()) {
+ if (ssec->isNeeded())
+ getOrCreateOutputSegment(ssec->segname)->addOutputSection(ssec);
+ } else {
+ error("section from " + it->second->firstSection()->file->getName() +
+ " conflicts with synthetic section " + ssec->segname + "," +
+ ssec->name);
+ }
+ }
+}
+
+void Writer::assignAddresses(OutputSegment *seg) {
+ addr = alignTo(addr, PageSize);
+ fileOff = alignTo(fileOff, PageSize);
+ seg->fileOff = fileOff;
+
+ for (auto *osec : seg->getSections()) {
+ addr = alignTo(addr, osec->align);
+ fileOff = alignTo(fileOff, osec->align);
+ osec->addr = addr;
+ osec->fileOff = isZeroFill(osec->flags) ? 0 : fileOff;
+ osec->finalize();
+
+ addr += osec->getSize();
+ fileOff += osec->getFileSize();
+ }
+}
+
+void Writer::openFile() {
+ Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr =
+ FileOutputBuffer::create(config->outputFile, fileOff,
+ FileOutputBuffer::F_executable);
+
+ if (!bufferOrErr)
+ error("failed to open " + config->outputFile + ": " +
+ llvm::toString(bufferOrErr.takeError()));
+ else
+ buffer = std::move(*bufferOrErr);
+}
+
+void Writer::writeSections() {
+ uint8_t *buf = buffer->getBufferStart();
+ for (OutputSegment *seg : outputSegments)
+ for (OutputSection *osec : seg->getSections())
+ osec->writeTo(buf + osec->fileOff);
+}
+
+void Writer::run() {
+ // dyld requires __LINKEDIT segment to always exist (even if empty).
+ OutputSegment *linkEditSegment =
+ getOrCreateOutputSegment(segment_names::linkEdit);
+
+ scanRelocations();
+ if (in.stubHelper->isNeeded())
+ in.stubHelper->setup();
+
+ // Sort and assign sections to their respective segments. No more sections nor
+ // segments may be created after these methods run.
+ createOutputSections();
+ sortSegmentsAndSections();
+
+ createLoadCommands();
+
+ // Ensure that segments (and the sections they contain) are allocated
+ // addresses in ascending order, which dyld requires.
+ //
+ // Note that at this point, __LINKEDIT sections are empty, but we need to
+ // determine addresses of other segments/sections before generating its
+ // contents.
+ for (OutputSegment *seg : outputSegments)
+ if (seg != linkEditSegment)
+ assignAddresses(seg);
+
+ // Fill __LINKEDIT contents.
+ in.binding->finalizeContents();
+ lazyBindingSection->finalizeContents();
+ exportSection->finalizeContents();
+ symtabSection->finalizeContents();
+
+ // Now that __LINKEDIT is filled out, do a proper calculation of its
+ // addresses and offsets.
+ assignAddresses(linkEditSegment);
+
+ openFile();
+ if (errorCount())
+ return;
+
+ writeSections();
+
+ if (auto e = buffer->commit())
+ error("failed to write to the output file: " + toString(std::move(e)));
+}
+
+void macho::writeResult() { Writer().run(); }
+
+void macho::createSyntheticSections() {
+ in.binding = make<BindingSection>();
+ in.got = make<GotSection>();
+ in.lazyPointers = make<LazyPointerSection>();
+ in.stubs = make<StubsSection>();
+ in.stubHelper = make<StubHelperSection>();
+ in.imageLoaderCache = make<ImageLoaderCacheSection>();
+}
diff --git a/contrib/llvm-project/lld/MachO/Writer.h b/contrib/llvm-project/lld/MachO/Writer.h
new file mode 100644
index 000000000000..7f846233107a
--- /dev/null
+++ b/contrib/llvm-project/lld/MachO/Writer.h
@@ -0,0 +1,31 @@
+//===- Writer.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_MACHO_WRITER_H
+#define LLD_MACHO_WRITER_H
+
+#include <cstdint>
+
+namespace lld {
+namespace macho {
+
+class LoadCommand {
+public:
+ virtual ~LoadCommand() = default;
+ virtual uint32_t getSize() const = 0;
+ virtual void writeTo(uint8_t *buf) const = 0;
+};
+
+void writeResult();
+
+void createSyntheticSections();
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/contrib/llvm-project/lld/docs/AtomLLD.rst b/contrib/llvm-project/lld/docs/AtomLLD.rst
index 614e568d1997..2766094696e0 100644
--- a/contrib/llvm-project/lld/docs/AtomLLD.rst
+++ b/contrib/llvm-project/lld/docs/AtomLLD.rst
@@ -59,4 +59,4 @@ Indices and tables
* :ref:`genindex`
* :ref:`search`
-__ http://llvm.org/docs/DeveloperPolicy.html#license
+__ https://llvm.org/docs/DeveloperPolicy.html#license
diff --git a/contrib/llvm-project/lld/docs/ELF/linker_script.rst b/contrib/llvm-project/lld/docs/ELF/linker_script.rst
new file mode 100644
index 000000000000..0f409b2020ac
--- /dev/null
+++ b/contrib/llvm-project/lld/docs/ELF/linker_script.rst
@@ -0,0 +1,77 @@
+Linker Script implementation notes and policy
+=============================================
+
+LLD implements a large subset of the GNU ld linker script notation. The LLD
+implementation policy is to implement linker script features as they are
+documented in the ld `manual <https://sourceware.org/binutils/docs/ld/Scripts.html>`_
+We consider it a bug if the lld implementation does not agree with the manual
+and it is not mentioned in the exceptions below.
+
+The ld manual is not a complete specification, and is not sufficient to build
+an implementation. In particular some features are only defined by the
+implementation and have changed over time.
+
+The lld implementation policy for properties of linker scripts that are not
+defined by the documentation is to follow the GNU ld implementation wherever
+possible. We reserve the right to make different implementation choices where
+it is appropriate for LLD. Intentional deviations will be documented in this
+file.
+
+Output section description
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The description of an output section looks like:
+
+::
+
+ section [address] [(type)] : [AT(lma)] [ALIGN(section_align)] [SUBALIGN](subsection_align)] {
+ output-section-command
+ ...
+ } [>region] [AT>lma_region] [:phdr ...] [=fillexp] [,]
+
+Output section address
+----------------------
+
+When an *OutputSection* *S* has ``address``, LLD will set sh_addr to ``address``.
+
+The ELF specification says:
+
+> The value of sh_addr must be congruent to 0, modulo the value of sh_addralign.
+
+The presence of ``address`` can cause the condition unsatisfied. LLD will warn.
+GNU ld from Binutils 2.35 onwards will reduce sh_addralign so that
+sh_addr=0 (modulo sh_addralign).
+
+Output section alignment
+------------------------
+
+sh_addralign of an *OutputSection* *S* is the maximum of
+``ALIGN(section_align)`` and the maximum alignment of the input sections in
+*S*.
+
+When an *OutputSection* *S* has both ``address`` and ``ALIGN(section_align)``,
+GNU ld will set sh_addralign to ``ALIGN(section_align)``.
+
+Output section LMA
+------------------
+
+A load address (LMA) can be specified by ``AT(lma)`` or ``AT>lma_region``.
+
+- ``AT(lma)`` specifies the exact load address. If the linker script does not
+ have a PHDRS command, then a new loadable segment will be generated.
+- ``AT>lma_region`` specifies the LMA region. The lack of ``AT>lma_region``
+ means the default region is used. Note, GNU ld propagates the previous LMA
+ memory region when ``address`` is not specified. The LMA is set to the
+ current location of the memory region aligned to the section alignment.
+ If the linker script does not have a PHDRS command, then if
+ ``lma_region`` is different from the ``lma_region`` for
+ the previous OutputSection a new loadable segment will be generated.
+
+The two keywords cannot be specified at the same time.
+
+If neither ``AT(lma)`` nor ``AT>lma_region`` is specified:
+
+- If the previous section is also in the default LMA region, and the two
+ section have the same memory regions, the difference between the LMA and the
+ VMA is computed to be the same as the previous difference.
+- Otherwise, the LMA is set to the VMA.
diff --git a/contrib/llvm-project/lld/docs/NewLLD.rst b/contrib/llvm-project/lld/docs/NewLLD.rst
index 59a8f87ea86a..1b1c87067f51 100644
--- a/contrib/llvm-project/lld/docs/NewLLD.rst
+++ b/contrib/llvm-project/lld/docs/NewLLD.rst
@@ -248,7 +248,7 @@ Finally, the linker replaces bitcode symbols with ELF/COFF symbols,
so that they are linked as if they were in the native format from the beginning.
The details are described in this document.
-http://llvm.org/docs/LinkTimeOptimization.html
+https://llvm.org/docs/LinkTimeOptimization.html
Glossary
--------
diff --git a/contrib/llvm-project/lld/docs/ReleaseNotes.rst b/contrib/llvm-project/lld/docs/ReleaseNotes.rst
index 724c0097a949..fe3de8306cd8 100644
--- a/contrib/llvm-project/lld/docs/ReleaseNotes.rst
+++ b/contrib/llvm-project/lld/docs/ReleaseNotes.rst
@@ -1,15 +1,19 @@
========================
-lld 10.0.0 Release Notes
+lld 11.0.0 Release Notes
========================
.. contents::
:local:
+.. warning::
+ These are in-progress notes for the upcoming LLVM 11.0.0 release.
+ Release notes for previous releases can be found on
+ `the Download Page <https://releases.llvm.org/download.html>`_.
Introduction
============
-This document contains the release notes for the lld linker, release 10.0.0.
+This document contains the release notes for the lld linker, release 11.0.0.
Here we describe the status of lld, including major improvements
from the previous release. All lld releases may be downloaded
from the `LLVM releases web site <https://llvm.org/releases/>`_.
@@ -20,110 +24,34 @@ Non-comprehensive list of changes in this release
ELF Improvements
----------------
-* Glob pattern, which you can use in linker scripts or version scripts,
- now supports `\` and `[!...]`. Except character classes
- (e.g. `[[:digit:]]`), lld's glob pattern should be fully compatible
- with GNU now. (`r375051
- <https://github.com/llvm/llvm-project/commit/48993d5ab9413f0e5b94dfa292a233ce55b09e3e>`_)
-
-* New ``elf32btsmipn32_fbsd`` and ``elf32ltsmipn32_fbsd`` emulations
- are supported.
-
-* Relax MIPS ``jalr`` and ``jr`` instructions marked by the ``R_MIPS_JALR``
- relocation.
-* For certain "undefined symbol" errors, a definition with a close spelling will be suggested.
- (`D67039 <https://reviews.llvm.org/D67039>`_)
-* ``extern "C"`` is suggested if an undefined reference is mangled(unmangled) while there
- is a likely unmangled(mangled) definition.
- (`D69592 <https://reviews.llvm.org/D69592>`_ `D69650 <https://reviews.llvm.org/D69650>`_)
-* New ``-z noseparate-code``, ``-z separate-code`` and ``-z separate-loadable-segments``.
- ``-z noseparate-code`` is the default, which can reduce sizes of linked binaries by up to
- 3 times maxpagesize.
- (`D64903 <https://reviews.llvm.org/D64903>`_ `D67481 <https://reviews.llvm.org/D67481>`_)
-* ``-z force-bti`` and ``-z pac-plt`` are added for AArch64 Branch Target Identification and Pointer Authentication.
- (`D62609 <https://reviews.llvm.org/D62609>`_)
-* ``--fix-cortex-a8`` is added to fix erratum 657417.
- (`D67284 <https://reviews.llvm.org/D67284>`_)
-* ``-z force-ibt`` and ``-z shstk`` are added for Intel Control-flow Enforcement Technology.
- (`D59780 <https://reviews.llvm.org/D59780>`_)
-* ``PT_GNU_PROPERTY`` is added to help loaders locate the ``.note.gnu.property`` section.
- It may be used by a future Linux kernel.
- (`D70961 <https://reviews.llvm.org/D70961>`_)
-* For ``--compress-debug-sections=zlib``, ``-O0`` and ``-O1`` enable compression level 1
- while ``-O2`` enables compression level 6. ``-O1`` (default) is faster than before.
- (`D70658 <https://reviews.llvm.org/D70658>`_)
-* Range extension thunks with addends are implemented for AArch64, PowerPC32 and PowerPC64.
- (`D70637 <https://reviews.llvm.org/D70637>`_ `D70937 <https://reviews.llvm.org/D70937>`_
- `D73424 <https://reviews.llvm.org/D73424>`_)
-* ``R_RISCV_ALIGN`` will be errored because linker relaxation for RISC-V is not supported.
- Pass ``-mno-relax`` to disable ``R_RISCV_ALIGN``.
- (`D71820 <https://reviews.llvm.org/D71820>`_)
-* The ARM port will no longer insert interworking thunks for non STT_FUNC symbols.
- (`D73474 <https://reviews.llvm.org/D73474>`_)
-* The quality of PowerPC32 port has been greatly improved (canonical PLT, copy
- relocations, non-preemptible IFUNC, range extension thunks with addends).
- It can link FreeBSD 13.0 userland.
-* The PowerPC64 port supports non-preemptible IFUNC.
- (`D71509 <https://reviews.llvm.org/D71509>`_)
-* lld creates a RO PT_LOAD and a RX PT_LOAD without a linker script.
- lld creates a unified RX PT_LOAD with a linker script.
- A future release will eliminate this difference and use a RO PT_LOAD and a RX PT_LOAD by default.
- The linker script case will require ``--no-rosegment`` to restore the current behavior.
-* GNU style compressed debug sections ``.zdebug`` (obsoleted by ``SHF_COMPRESSED``)
- are supported for input files, but not for the output.
- A future release may drop ``.zdebug`` support.
+* New ``--time-trace`` option records a time trace file that can be viewed in
+ chrome://tracing. The file can be specified with ``--time-trace-file``.
+ Trace granularity can be specified with ``--time-trace-granularity``.
+ (`D71060 <https://reviews.llvm.org/D71060>`_)
+* ...
Breaking changes
----------------
-* ``-Ttext=$base`` (base is usually 0) is no longer supported.
- If PT_PHDR is needed, use ``--image-base=$base`` instead.
- If PT_PHDR is not needed, use a linker script with `.text 0 : { *(.text*) }` as the first
- output section description.
- See https://bugs.llvm.org/show_bug.cgi?id=44715 for more information.
- (`D67325 <https://reviews.llvm.org/D67325>`_)
-* ``-Ttext-segment`` is no longer supported. Its meaning was different from GNU ld's and
- could cause subtle bugs.
- (`D70468 <https://reviews.llvm.org/D70468>`_)
+* One-dash form of some long option (``--thinlto-*``, ``--lto-*``, ``--shuffle-sections=``)
+ are no longer supported.
+* ``--export-dynamic-symbol`` no longer implies ``-u``.
+COFF Improvements
+-----------------
+
+* ...
MinGW Improvements
------------------
-* Allow using custom .edata sections from input object files (for use
- by Wine)
- (`dadc6f248868 <https://reviews.llvm.org/rGdadc6f248868>`_)
-
-* Don't implicitly create import libraries unless requested
- (`6540e55067e3 <https://reviews.llvm.org/rG6540e55067e3>`_)
-
-* Support merging multiple resource object files
- (`3d3a9b3b413d <https://reviews.llvm.org/rG3d3a9b3b413d>`_)
- and properly handle the default manifest object files that GCC can pass
- (`d581dd501381 <https://reviews.llvm.org/rGd581dd501381>`_)
-
-* Demangle itanium symbol names in warnings/error messages
- (`a66fc1c99f3e <https://reviews.llvm.org/rGa66fc1c99f3e>`_)
+* ...
-* Print source locations for undefined references and duplicate symbols,
- if possible
- (`1d06d48bb346 <https://reviews.llvm.org/rG1d06d48bb346>`_)
- and
- (`b38f577c015c <https://reviews.llvm.org/rGb38f577c015c>`_)
-
-* Look for more filename patterns when resolving ``-l`` options
- (`0226c35262df <https://reviews.llvm.org/rG0226c35262df>`_)
-
-* Don't error out on duplicate absolute symbols with the same value
- (which can happen for the default-null symbol for weak symbols)
- (`1737cc750c46 <https://reviews.llvm.org/rG1737cc750c46>`_)
+MachO Improvements
+------------------
+* Item 1.
WebAssembly Improvements
------------------------
-* `__data_end` and `__heap_base` are no longer exported by default,
- as it's best to keep them internal when possible. They can be
- explicitly exported with `--export=__data_end` and
- `--export=__heap_base`, respectively.
-* wasm-ld now elides .bss sections when the memory is not imported
diff --git a/contrib/llvm-project/lld/docs/WebAssembly.rst b/contrib/llvm-project/lld/docs/WebAssembly.rst
index 1b01cab5c253..b23f2cd462b4 100644
--- a/contrib/llvm-project/lld/docs/WebAssembly.rst
+++ b/contrib/llvm-project/lld/docs/WebAssembly.rst
@@ -112,8 +112,8 @@ The default behaviour is to generate these stub function and to produce
a warning. The ``--fatal-warnings`` flag can be used to disable this behaviour
and error out if mismatched are found.
-Imports and Exports
-~~~~~~~~~~~~~~~~~~~
+Exports
+~~~~~~~
When building a shared library any symbols marked as ``visibility=default`` will
be exported.
@@ -130,6 +130,17 @@ Finally, just like with native ELF linker the ``--export-dynamic`` flag can be
used to export symbols in the executable which are marked as
``visibility=default``.
+Imports
+~~~~~~~
+
+By default no undefined symbols are allowed in the final binary. The flag
+``--allow-undefined`` results in a WebAssembly import being defined for each
+undefined symbol. It is then up to the runtime to provide such symbols.
+
+Alternatively symbols can be marked in the source code as with the
+``import_name`` and/or ``import_module`` clang attributes which signals that
+they are expected to be undefined at static link time.
+
Garbage Collection
~~~~~~~~~~~~~~~~~~
diff --git a/contrib/llvm-project/lld/docs/conf.py b/contrib/llvm-project/lld/docs/conf.py
index 0f03b1275a7c..7d4fc0c5ad75 100644
--- a/contrib/llvm-project/lld/docs/conf.py
+++ b/contrib/llvm-project/lld/docs/conf.py
@@ -48,9 +48,9 @@ copyright = u'2011-%d, LLVM Project' % date.today().year
# built documents.
#
# The short version.
-version = '10'
+version = '11'
# The full version, including alpha/beta/rc tags.
-release = '10'
+release = '11'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/contrib/llvm-project/lld/docs/design.rst b/contrib/llvm-project/lld/docs/design.rst
index 1e111f979bb5..20d8fe78a641 100644
--- a/contrib/llvm-project/lld/docs/design.rst
+++ b/contrib/llvm-project/lld/docs/design.rst
@@ -326,7 +326,7 @@ Testing
The lld project contains a test suite which is being built up as new code is
added to lld. All new lld functionality should have a tests added to the test
-suite. The test suite is `lit <http://llvm.org/cmds/lit.html/>`_ driven. Each
+suite. The test suite is `lit <https://llvm.org/cmds/lit.html/>`_ driven. Each
test is a text file with comments telling lit how to run the test and check the
result To facilitate testing, the lld project builds a tool called lld-core.
This tool reads a YAML file (default from stdin), parses it into one or more
diff --git a/contrib/llvm-project/lld/docs/development.rst b/contrib/llvm-project/lld/docs/development.rst
index ce91341d665f..81b826f64835 100644
--- a/contrib/llvm-project/lld/docs/development.rst
+++ b/contrib/llvm-project/lld/docs/development.rst
@@ -6,7 +6,7 @@ Development
Note: this document discuss Mach-O port of LLD. For ELF and COFF,
see :doc:`index`.
-lld is developed as part of the `LLVM <http://llvm.org>`_ project.
+lld is developed as part of the `LLVM <https://llvm.org>`_ project.
Creating a Reader
-----------------
diff --git a/contrib/llvm-project/lld/docs/getting_started.rst b/contrib/llvm-project/lld/docs/getting_started.rst
index a174f652e736..506cb24dde84 100644
--- a/contrib/llvm-project/lld/docs/getting_started.rst
+++ b/contrib/llvm-project/lld/docs/getting_started.rst
@@ -6,7 +6,7 @@ Getting Started: Building and Running lld
This page gives you the shortest path to checking out and building lld. If you
run into problems, please file bugs in the `LLVM Bugzilla`__
-__ http://llvm.org/bugs/
+__ https://bugs.llvm.org/
Building lld
------------
@@ -84,4 +84,4 @@ More Information
For more information on using CMake see the `LLVM CMake guide`_.
-.. _LLVM CMake guide: http://llvm.org/docs/CMake.html
+.. _LLVM CMake guide: https://llvm.org/docs/CMake.html
diff --git a/contrib/llvm-project/lld/docs/index.rst b/contrib/llvm-project/lld/docs/index.rst
index 68604c27f95a..b820d57e3d35 100644
--- a/contrib/llvm-project/lld/docs/index.rst
+++ b/contrib/llvm-project/lld/docs/index.rst
@@ -33,10 +33,10 @@ Features
machine, you can expect that LLD runs more than twice as fast as the GNU
gold linker. Your mileage may vary, though.
-- It supports various CPUs/ABIs including x86-64, x86, x32, AArch64,
- ARM, MIPS 32/64 big/little-endian, PowerPC, PowerPC 64 and AMDGPU.
- Among these, x86-64, AArch64, and ARM (>= v6) are production quality.
- MIPS seems decent too. x86 should be OK but is not well tested yet.
+- It supports various CPUs/ABIs including AArch64, AMDGPU, ARM, Hexagon, MIPS
+ 32/64 big/little-endian, PowerPC, PowerPC64, RISC-V, SPARC V9, x86-32 and
+ x86-64. Among these, AArch64, ARM (>= v6), PowerPC, PowerPC64, x86-32 and
+ x86-64 have production quality. MIPS seems decent too.
- It is always a cross-linker, meaning that it always supports all the
above targets however it was built. In fact, we don't provide a
@@ -98,7 +98,7 @@ Build
If you have already checked out LLVM using SVN, you can check out LLD
under ``tools`` directory just like you probably did for clang. For the
details, see `Getting Started with the LLVM System
-<http://llvm.org/docs/GettingStarted.html>`_.
+<https://llvm.org/docs/GettingStarted.html>`_.
If you haven't checked out LLVM, the easiest way to build LLD is to
check out the entire LLVM projects/sub-projects from a git mirror and
@@ -176,3 +176,4 @@ document soon.
missingkeyfunction
Partitions
ReleaseNotes
+ ELF/linker_script
diff --git a/contrib/llvm-project/lld/docs/ld.lld.1 b/contrib/llvm-project/lld/docs/ld.lld.1
index 92d67b17e24e..5edeaf85f93f 100644
--- a/contrib/llvm-project/lld/docs/ld.lld.1
+++ b/contrib/llvm-project/lld/docs/ld.lld.1
@@ -157,6 +157,10 @@ This is recorded in an ELF segment of type
.It Fl -dynamic-list Ns = Ns Ar file
Read a list of dynamic symbols from
.Ar file .
+(executable) Put matched non-local defined symbols to the dynamic symbol table.
+(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object. Implies
+.Cm -Bsymbolic
+but does not set DF_SYMBOLIC
.It Fl -eh-frame-hdr
Request creation of
.Li .eh_frame_hdr
@@ -184,10 +188,14 @@ This option is currently only supported on AArch64.
Exclude static libraries from automatic export.
.It Fl -export-dynamic , Fl E
Put symbols in the dynamic symbol table.
-.It Fl -export-dynamic-symbol Ns = Ns Ar symbol
-Include
-.Ar symbol
-in the dynamic symbol table.
+.It Fl -export-dynamic-symbol Ns = Ns Ar glob
+(executable) Put matched non-local defined symbols to the dynamic symbol table.
+(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object even if they would otherwise be due to
+.Cm -Bsymbolic
+,
+.Cm -Bsymbolic-functions
+or
+.Cm --dynamic-list
.It Fl -fatal-warnings
Treat warnings as errors.
.It Fl -filter Ns = Ns Ar value , Fl F Ar value
@@ -300,10 +308,10 @@ Disable merging .ARM.exidx entries.
Page align sections.
.It Fl -no-omagic
Do not set the text data sections to be writable, page align sections.
+.It Fl -no-relax
+Disable target-specific relaxations. This is currently a no-op.
.It Fl -no-rosegment
Do not put read-only non-executable sections in their own segment.
-.It Fl -no-threads
-Do not run the linker multi-threaded.
.It Fl -no-undefined-version
Report version scripts that refer undefined symbols.
.It Fl -no-undefined
@@ -416,6 +424,9 @@ List removed unused sections.
List identical folded sections.
.It Fl -print-map
Print a link map to the standard output.
+.It Fl -print-archive-stats Ns = Ns Ar file
+Write archive usage statistics to the specified file.
+Print the numbers of members and fetched members for each archive.
.It Fl -push-state
Save the current state of
.Fl -as-needed ,
@@ -456,6 +467,8 @@ were concatenated in the order they appeared on the command line.
Set address of section.
.It Fl -shared , Fl -Bsharable
Build a shared object.
+.It Fl -shuffle-sections Ns = Ns Ar seed
+Shuffle input sections using the given seed. If 0, use a random seed.
.It Fl -soname Ns = Ns Ar value , Fl h Ar value
Set
.Dv DT_SONAME
@@ -523,9 +536,19 @@ Path to ThinLTO cached object file directory.
Pruning policy for the ThinLTO cache.
.It Fl -thinlto-jobs Ns = Ns Ar value
Number of ThinLTO jobs.
-.It Fl -threads
-Run the linker multi-threaded.
-This option is enabled by default.
+.It Fl -threads Ns = Ns Ar N
+Number of threads.
+.Cm all
+(default) means all of concurrent threads supported.
+.Cm 1
+disables multi-threading.
+.It Fl -time-trace
+Record time trace.
+.It Fl -time-trace-file Ns = Ns Ar file
+Write time trace output to
+.Ar file .
+.It Fl -time-trace-granularity Ns = Ns Ar value
+Minimum time granularity (in microseconds) traced by time profiler.
.It Fl -trace
Print the names of the input files.
.It Fl -trace-symbol Ns = Ns Ar symbol , Fl y Ar symbol
@@ -551,6 +574,8 @@ matches the characters within brackets.
All symbols that match
a given pattern are handled as if they were given as arguments of
.Fl -undefined .
+.It Fl -unique
+Creates a separate output section for every orphan input section.
.It Fl -unresolved-symbols Ns = Ns Ar value
Determine how to handle unresolved symbols.
.It Fl -use-android-relr-tags
@@ -569,6 +594,10 @@ Read version script from
Warn about reverse or cyclic dependencies to or between static archives.
This can be used to ensure linker invocation remains compatible with
traditional Unix-like linkers.
+.It Fl -warn-backrefs-exclude Ns = Ns Ar glob
+Glob describing an archive (or an object file within --start-lib)
+which should be ignored for
+.Fl -warn-backrefs
.It Fl -warn-common
Warn about duplicate common symbols.
.It Fl -warn-ifunc-textrel
@@ -596,6 +625,13 @@ Use wrapper functions for symbol.
Linker option extensions.
.Bl -tag -width indent -compact
.Pp
+.It Cm dead-reloc-in-nonalloc Ns = Ns Ar section_glob=value
+Resolve a relocation in a matched non-SHF_ALLOC section referencing a discarded symbol to
+.Ar value
+Accepts globs, in the event of a section matching more than one option, the last
+option takes precedence. An order of least specific to most specific match is
+recommended.
+.Pp
.It Cm execstack
Make the main stack executable.
Stack permissions are recorded in the
@@ -701,6 +737,12 @@ processing.
.It Cm pac-plt
AArch64 only, use pointer authentication in PLT.
.Pp
+.It Cm rel
+Use REL format for dynamic relocations.
+.Pp
+.It Cm rela
+Use RELA format for dynamic relocations.
+.Pp
.It Cm retpolineplt
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
.Pp
diff --git a/contrib/llvm-project/lld/include/lld/Common/DWARF.h b/contrib/llvm-project/lld/include/lld/Common/DWARF.h
index f0d3d2fbda77..b77985a6919d 100644
--- a/contrib/llvm-project/lld/include/lld/Common/DWARF.h
+++ b/contrib/llvm-project/lld/include/lld/Common/DWARF.h
@@ -31,6 +31,8 @@ public:
llvm::Optional<std::pair<std::string, unsigned>>
getVariableLoc(StringRef name);
+ llvm::DWARFContext *getContext() { return dwarf.get(); }
+
private:
std::unique_ptr<llvm::DWARFContext> dwarf;
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
diff --git a/contrib/llvm-project/lld/include/lld/Common/Driver.h b/contrib/llvm-project/lld/include/lld/Common/Driver.h
index 0a358d8aff6b..6db3d234eb56 100644
--- a/contrib/llvm-project/lld/include/lld/Common/Driver.h
+++ b/contrib/llvm-project/lld/include/lld/Common/Driver.h
@@ -33,6 +33,11 @@ bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
}
+namespace macho {
+bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
+ llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
+}
+
namespace wasm {
bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
diff --git a/contrib/llvm-project/lld/include/lld/Common/Memory.h b/contrib/llvm-project/lld/include/lld/Common/Memory.h
index 41d8f15d7b34..f516a327cfb2 100644
--- a/contrib/llvm-project/lld/include/lld/Common/Memory.h
+++ b/contrib/llvm-project/lld/include/lld/Common/Memory.h
@@ -47,11 +47,20 @@ template <class T> struct SpecificAlloc : public SpecificAllocBase {
llvm::SpecificBumpPtrAllocator<T> alloc;
};
+// Use a static local for these singletons so they are only registered if an
+// object of this instance is ever constructed. Otherwise we will create and
+// register ELF allocators for COFF and the reverse.
+template <typename T>
+inline llvm::SpecificBumpPtrAllocator<T> &getSpecificAllocSingleton() {
+ static SpecificAlloc<T> instance;
+ return instance.alloc;
+}
+
// Use this arena if your object has a destructor.
// Your destructor will be invoked from freeArena().
template <typename T, typename... U> T *make(U &&... args) {
- static SpecificAlloc<T> alloc;
- return new (alloc.alloc.Allocate()) T(std::forward<U>(args)...);
+ return new (getSpecificAllocSingleton<T>().Allocate())
+ T(std::forward<U>(args)...);
}
} // namespace lld
diff --git a/contrib/llvm-project/lld/include/lld/Common/Strings.h b/contrib/llvm-project/lld/include/lld/Common/Strings.h
index 9d002bf336de..3940d2443cd4 100644
--- a/contrib/llvm-project/lld/include/lld/Common/Strings.h
+++ b/contrib/llvm-project/lld/include/lld/Common/Strings.h
@@ -27,16 +27,52 @@ bool isValidCIdentifier(llvm::StringRef s);
// Write the contents of the a buffer to a file
void saveBuffer(llvm::StringRef buffer, const llvm::Twine &path);
-// This class represents multiple glob patterns.
-class StringMatcher {
+// A single pattern to match against. A pattern can either be double-quoted
+// text that should be matched exactly after removing the quoting marks or a
+// glob pattern in the sense of GlobPattern.
+class SingleStringMatcher {
public:
- StringMatcher() = default;
- explicit StringMatcher(llvm::ArrayRef<llvm::StringRef> pat);
+ // Create a StringPattern from Pattern to be matched exactly irregardless
+ // of globbing characters if ExactMatch is true.
+ SingleStringMatcher(llvm::StringRef Pattern);
+ // Match s against this pattern, exactly if ExactMatch is true.
bool match(llvm::StringRef s) const;
private:
- std::vector<llvm::GlobPattern> patterns;
+ // Whether to do an exact match irregardless of the presence of wildcard
+ // character.
+ bool ExactMatch;
+
+ // GlobPattern object if not doing an exact match.
+ llvm::GlobPattern GlobPatternMatcher;
+
+ // StringRef to match exactly if doing an exact match.
+ llvm::StringRef ExactPattern;
+};
+
+// This class represents multiple patterns to match against. A pattern can
+// either be a double-quoted text that should be matched exactly after removing
+// the quoted marks or a glob pattern.
+class StringMatcher {
+private:
+ // Patterns to match against.
+ std::vector<SingleStringMatcher> patterns;
+
+public:
+ StringMatcher() = default;
+
+ // Matcher for a single pattern.
+ StringMatcher(llvm::StringRef Pattern)
+ : patterns({SingleStringMatcher(Pattern)}) {}
+
+ // Add a new pattern to the existing ones to match against.
+ void addPattern(SingleStringMatcher Matcher) { patterns.push_back(Matcher); }
+
+ bool empty() { return patterns.empty(); }
+
+ // Match s against the patterns.
+ bool match(llvm::StringRef s) const;
};
} // namespace lld
diff --git a/contrib/llvm-project/lld/include/lld/Common/Threads.h b/contrib/llvm-project/lld/include/lld/Common/Threads.h
deleted file mode 100644
index 7834130fdf72..000000000000
--- a/contrib/llvm-project/lld/include/lld/Common/Threads.h
+++ /dev/null
@@ -1,92 +0,0 @@
-//===- Threads.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
-//
-//===----------------------------------------------------------------------===//
-//
-// LLD supports threads to distribute workloads to multiple cores. Using
-// multicore is most effective when more than one core are idle. At the
-// last step of a build, it is often the case that a linker is the only
-// active process on a computer. So, we are naturally interested in using
-// threads wisely to reduce latency to deliver results to users.
-//
-// That said, we don't want to do "too clever" things using threads.
-// Complex multi-threaded algorithms are sometimes extremely hard to
-// reason about and can easily mess up the entire design.
-//
-// Fortunately, when a linker links large programs (when the link time is
-// most critical), it spends most of the time to work on massive number of
-// small pieces of data of the same kind, and there are opportunities for
-// large parallelism there. Here are examples:
-//
-// - We have hundreds of thousands of input sections that need to be
-// copied to a result file at the last step of link. Once we fix a file
-// layout, each section can be copied to its destination and its
-// relocations can be applied independently.
-//
-// - We have tens of millions of small strings when constructing a
-// mergeable string section.
-//
-// For the cases such as the former, we can just use parallelForEach
-// instead of std::for_each (or a plain for loop). Because tasks are
-// completely independent from each other, we can run them in parallel
-// without any coordination between them. That's very easy to understand
-// and reason about.
-//
-// For the cases such as the latter, we can use parallel algorithms to
-// deal with massive data. We have to write code for a tailored algorithm
-// for each problem, but the complexity of multi-threading is isolated in
-// a single pass and doesn't affect the linker's overall design.
-//
-// The above approach seems to be working fairly well. As an example, when
-// linking Chromium (output size 1.6 GB), using 4 cores reduces latency to
-// 75% compared to single core (from 12.66 seconds to 9.55 seconds) on my
-// Ivy Bridge Xeon 2.8 GHz machine. Using 40 cores reduces it to 63% (from
-// 12.66 seconds to 7.95 seconds). Because of the Amdahl's law, the
-// speedup is not linear, but as you add more cores, it gets faster.
-//
-// On a final note, if you are trying to optimize, keep the axiom "don't
-// guess, measure!" in mind. Some important passes of the linker are not
-// that slow. For example, resolving all symbols is not a very heavy pass,
-// although it would be very hard to parallelize it. You want to first
-// identify a slow pass and then optimize it.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_COMMON_THREADS_H
-#define LLD_COMMON_THREADS_H
-
-#include "llvm/Support/Parallel.h"
-#include <functional>
-
-namespace lld {
-
-extern bool threadsEnabled;
-
-template <typename R, class FuncTy> void parallelForEach(R &&range, FuncTy fn) {
- if (threadsEnabled)
- for_each(llvm::parallel::par, std::begin(range), std::end(range), fn);
- else
- for_each(llvm::parallel::seq, std::begin(range), std::end(range), fn);
-}
-
-inline void parallelForEachN(size_t begin, size_t end,
- llvm::function_ref<void(size_t)> fn) {
- if (threadsEnabled)
- for_each_n(llvm::parallel::par, begin, end, fn);
- else
- for_each_n(llvm::parallel::seq, begin, end, fn);
-}
-
-template <typename R, class FuncTy> void parallelSort(R &&range, FuncTy fn) {
- if (threadsEnabled)
- sort(llvm::parallel::par, std::begin(range), std::end(range), fn);
- else
- sort(llvm::parallel::seq, std::begin(range), std::end(range), fn);
-}
-
-} // namespace lld
-
-#endif
diff --git a/contrib/llvm-project/lld/include/lld/Common/Timer.h b/contrib/llvm-project/lld/include/lld/Common/Timer.h
index 4a298b04a30b..95e811a2f9f0 100644
--- a/contrib/llvm-project/lld/include/lld/Common/Timer.h
+++ b/contrib/llvm-project/lld/include/lld/Common/Timer.h
@@ -12,6 +12,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include <assert.h>
+#include <atomic>
#include <chrono>
#include <map>
#include <memory>
@@ -27,6 +28,8 @@ struct ScopedTimer {
void stop();
+ std::chrono::time_point<std::chrono::high_resolution_clock> startTime;
+
Timer *t = nullptr;
};
@@ -36,8 +39,7 @@ public:
static Timer &root();
- void start();
- void stop();
+ void addToTotal(std::chrono::nanoseconds time) { total += time.count(); }
void print();
double millis() const;
@@ -46,11 +48,9 @@ private:
explicit Timer(llvm::StringRef name);
void print(int depth, double totalDuration, bool recurse = true) const;
- std::chrono::time_point<std::chrono::high_resolution_clock> startTime;
- std::chrono::nanoseconds total;
+ std::atomic<std::chrono::nanoseconds::rep> total;
std::vector<Timer *> children;
std::string name;
- Timer *parent;
};
} // namespace lld
diff --git a/contrib/llvm-project/lld/include/lld/Common/Version.inc.in b/contrib/llvm-project/lld/include/lld/Common/Version.inc.in
index 2789a5c46089..5cb48a546e11 100644
--- a/contrib/llvm-project/lld/include/lld/Common/Version.inc.in
+++ b/contrib/llvm-project/lld/include/lld/Common/Version.inc.in
@@ -1,6 +1 @@
-#define LLD_VERSION @LLD_VERSION@
#define LLD_VERSION_STRING "@LLD_VERSION@"
-#define LLD_VERSION_MAJOR @LLD_VERSION_MAJOR@
-#define LLD_VERSION_MINOR @LLD_VERSION_MINOR@
-#define LLD_REVISION_STRING "@LLD_REVISION@"
-#define LLD_REPOSITORY_STRING "@LLD_REPOSITORY@"
diff --git a/contrib/llvm-project/lld/include/lld/Core/File.h b/contrib/llvm-project/lld/include/lld/Core/File.h
index df014669eb62..bb4ca9cf5859 100644
--- a/contrib/llvm-project/lld/include/lld/Core/File.h
+++ b/contrib/llvm-project/lld/include/lld/Core/File.h
@@ -78,7 +78,7 @@ public:
/// Returns the path of the archive file name if this file is instantiated
/// from an archive file. Otherwise returns the empty string.
StringRef archivePath() const { return _archivePath; }
- void setArchivePath(StringRef path) { _archivePath = path; }
+ void setArchivePath(StringRef path) { _archivePath = std::string(path); }
/// Returns the path name of this file. It doesn't include archive file name.
StringRef memberPath() const { return _path; }
diff --git a/contrib/llvm-project/lld/include/lld/Core/Reference.h b/contrib/llvm-project/lld/include/lld/Core/Reference.h
index 4769882cde50..b104f8495474 100644
--- a/contrib/llvm-project/lld/include/lld/Core/Reference.h
+++ b/contrib/llvm-project/lld/include/lld/Core/Reference.h
@@ -91,7 +91,7 @@ public:
/// Some relocations require a symbol and a value (e.g. foo + 4).
virtual Addend addend() const = 0;
- /// During linking, some optimzations may change addend value.
+ /// During linking, some optimizations may change addend value.
virtual void setAddend(Addend) = 0;
/// Returns target specific attributes of the reference.
diff --git a/contrib/llvm-project/lld/include/lld/ReaderWriter/MachOLinkingContext.h b/contrib/llvm-project/lld/include/lld/ReaderWriter/MachOLinkingContext.h
index cd57604fa17d..974f323bc612 100644
--- a/contrib/llvm-project/lld/include/lld/ReaderWriter/MachOLinkingContext.h
+++ b/contrib/llvm-project/lld/include/lld/ReaderWriter/MachOLinkingContext.h
@@ -55,8 +55,8 @@ public:
enum class ExportMode {
globals, // Default, all global symbols exported.
- whiteList, // -exported_symbol[s_list], only listed symbols exported.
- blackList // -unexported_symbol[s_list], no listed symbol exported.
+ exported, // -exported_symbol[s_list], only listed symbols exported.
+ unexported // -unexported_symbol[s_list], no listed symbol exported.
};
enum class DebugInfoMode {
@@ -423,8 +423,6 @@ public:
private:
Writer &writer() const override;
mach_o::MachODylibFile* loadIndirectDylib(StringRef path);
- void checkExportWhiteList(const DefinedAtom *atom) const;
- void checkExportBlackList(const DefinedAtom *atom) const;
struct ArchInfo {
StringRef archName;
MachOLinkingContext::Arch arch;
diff --git a/contrib/llvm-project/lld/lib/Core/Error.cpp b/contrib/llvm-project/lld/lib/Core/Error.cpp
index f138a81efaab..a4f4b1b8af48 100644
--- a/contrib/llvm-project/lld/lib/Core/Error.cpp
+++ b/contrib/llvm-project/lld/lib/Core/Error.cpp
@@ -62,7 +62,7 @@ public:
int add(std::string msg) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
- // Value zero is always the successs value.
+ // Value zero is always the success value.
if (_messages.empty())
_messages.push_back("Success");
_messages.push_back(msg);
@@ -78,7 +78,8 @@ private:
static dynamic_error_category categorySingleton;
std::error_code make_dynamic_error_code(StringRef msg) {
- return std::error_code(categorySingleton.add(msg), categorySingleton);
+ return std::error_code(categorySingleton.add(std::string(msg)),
+ categorySingleton);
}
char GenericError::ID = 0;
diff --git a/contrib/llvm-project/lld/lib/Core/Resolver.cpp b/contrib/llvm-project/lld/lib/Core/Resolver.cpp
index d1c3d8159f58..17a46056f00c 100644
--- a/contrib/llvm-project/lld/lib/Core/Resolver.cpp
+++ b/contrib/llvm-project/lld/lib/Core/Resolver.cpp
@@ -298,7 +298,7 @@ void Resolver::updateReferences() {
for (const OwningAtomPtr<Atom> &atom : _atoms) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get())) {
for (const Reference *ref : *defAtom) {
- // A reference of type kindAssociate should't be updated.
+ // A reference of type kindAssociate shouldn't be updated.
// Instead, an atom having such reference will be removed
// if the target atom is coalesced away, so that they will
// go away as a group.
diff --git a/contrib/llvm-project/lld/lib/Driver/DarwinLdDriver.cpp b/contrib/llvm-project/lld/lib/Driver/DarwinLdDriver.cpp
index 062e945bbd83..1a57def4ebbe 100644
--- a/contrib/llvm-project/lld/lib/Driver/DarwinLdDriver.cpp
+++ b/contrib/llvm-project/lld/lib/Driver/DarwinLdDriver.cpp
@@ -147,11 +147,11 @@ std::vector<std::unique_ptr<File>> loadFile(MachOLinkingContext &ctx,
static std::string canonicalizePath(StringRef path) {
char sep = llvm::sys::path::get_separator().front();
if (sep != '/') {
- std::string fixedPath = path;
+ std::string fixedPath = std::string(path);
std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
return fixedPath;
} else {
- return path;
+ return std::string(path);
}
}
@@ -650,12 +650,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
// Handle -exported_symbols_list <file>
for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
error("-exported_symbols_list cannot be combined with "
"-unexported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
error(ec.message() + ", processing '-exported_symbols_list " +
expFile->getValue());
@@ -665,23 +665,23 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
// Handle -exported_symbol <symbol>
for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
error("-exported_symbol cannot be combined with "
"-unexported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
ctx.addExportSymbol(symbol->getValue());
}
// Handle -unexported_symbols_list <file>
for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
error("-unexported_symbols_list cannot be combined with "
"-exported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
error(ec.message() + ", processing '-unexported_symbols_list " +
expFile->getValue());
@@ -691,12 +691,12 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
// Handle -unexported_symbol <symbol>
for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
error("-unexported_symbol cannot be combined with "
"-exported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
ctx.addExportSymbol(symbol->getValue());
}
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
index a424edf4985a..bee081aec067 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
@@ -61,7 +61,7 @@ public:
/// Used by GOTPass to update GOT References.
void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
- // If GOT slot was instanciated, transform:
+ // If GOT slot was instantiated, transform:
// gotPage21/gotOffset12 -> page21/offset12scale8
// If GOT slot optimized away, transform:
// gotPage21/gotOffset12 -> page21/addOffset12
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/Atoms.h b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/Atoms.h
index b8bca1959cfb..c61aaa88e8df 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/Atoms.h
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/Atoms.h
@@ -112,8 +112,8 @@ class MachOTentativeDefAtom : public SimpleDefinedAtom {
public:
MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope,
uint64_t size, DefinedAtom::Alignment align)
- : SimpleDefinedAtom(f), _name(name), _scope(scope), _size(size),
- _align(align) {}
+ : SimpleDefinedAtom(f), _name(std::string(name)), _scope(scope),
+ _size(size), _align(align) {}
~MachOTentativeDefAtom() override = default;
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
index 94a105a6f159..f3636feb217b 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
@@ -576,5 +576,5 @@ void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
pm.add(std::make_unique<CompactUnwindPass>(ctx));
}
-} // end namesapce mach_o
-} // end namesapce lld
+} // end namespace mach_o
+} // end namespace lld
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/File.h b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/File.h
index 072702973f81..8d59656beab5 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/File.h
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/File.h
@@ -17,6 +17,8 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Format.h"
+#include "llvm/TextAPI/MachO/InterfaceFile.h"
+#include "llvm/TextAPI/MachO/TextAPIReader.h"
#include <unordered_map>
namespace lld {
@@ -322,7 +324,8 @@ public:
void loadReExportedDylibs(FindDylib find) {
for (ReExportedDylib &entry : _reExportedDylibs) {
- entry.file = find(entry.path);
+ if (!entry.file)
+ entry.file = find(entry.path);
}
}
@@ -339,7 +342,7 @@ public:
return std::error_code();
}
-private:
+protected:
OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
StringRef installName) const {
// First, check if requested symbol is directly implemented by this dylib.
@@ -373,6 +376,7 @@ private:
struct ReExportedDylib {
ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
+ ReExportedDylib(StringRef p, MachODylibFile *file) : path(p), file(file) { }
StringRef path;
MachODylibFile *file;
};
@@ -393,6 +397,70 @@ private:
mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
};
+class TAPIFile : public MachODylibFile {
+public:
+
+ TAPIFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
+ : MachODylibFile(std::move(mb), ctx) {}
+
+ std::error_code doParse() override {
+
+ llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
+ llvm::MachO::TextAPIReader::get(*_mb);
+ if (!result)
+ return std::make_error_code(std::errc::invalid_argument);
+
+ std::unique_ptr<llvm::MachO::InterfaceFile> interface{std::move(*result)};
+ return loadFromInterface(*interface);
+ }
+
+private:
+ std::error_code loadFromInterface(llvm::MachO::InterfaceFile &interface) {
+ llvm::MachO::Architecture arch;
+ switch(_ctx->arch()) {
+ case MachOLinkingContext::arch_x86:
+ arch = llvm::MachO::AK_i386;
+ break;
+ case MachOLinkingContext::arch_x86_64:
+ arch = llvm::MachO::AK_x86_64;
+ break;
+ case MachOLinkingContext::arch_arm64:
+ arch = llvm::MachO::AK_arm64;
+ break;
+ default:
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+
+ setInstallName(interface.getInstallName().copy(allocator()));
+ // TODO(compnerd) filter out symbols based on the target platform
+ for (const auto symbol : interface.symbols())
+ if (symbol->getArchitectures().has(arch))
+ addExportedSymbol(symbol->getName(), symbol->isWeakDefined(), true);
+
+ for (const llvm::MachO::InterfaceFileRef &reexport :
+ interface.reexportedLibraries())
+ addReExportedDylib(reexport.getInstallName().copy(allocator()));
+
+ for (const auto& document : interface.documents()) {
+ for (auto& reexport : _reExportedDylibs) {
+ if (reexport.path != document->getInstallName())
+ continue;
+ assert(!reexport.file);
+ _ownedFiles.push_back(std::make_unique<TAPIFile>(
+ MemoryBuffer::getMemBuffer("", _mb->getBufferIdentifier()), _ctx));
+ reexport.file = _ownedFiles.back().get();
+ std::error_code err = _ownedFiles.back()->loadFromInterface(*document);
+ if (err)
+ return err;
+ }
+ }
+
+ return std::error_code();
+ }
+
+ std::vector<std::unique_ptr<TAPIFile>> _ownedFiles;
+};
+
} // end namespace mach_o
} // end namespace lld
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/GOTPass.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/GOTPass.cpp
index 0f80dfa19d09..10e611c1bd2b 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/GOTPass.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/GOTPass.cpp
@@ -179,5 +179,5 @@ void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
pm.add(std::make_unique<GOTPass>(ctx));
}
-} // end namesapce mach_o
-} // end namesapce lld
+} // end namespace mach_o
+} // end namespace lld
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/LayoutPass.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/LayoutPass.cpp
index 775d3b0bad3a..e92fdf1b4913 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/LayoutPass.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/LayoutPass.cpp
@@ -461,10 +461,11 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
});
std::vector<LayoutPass::SortKey> vec = decorate(atomRange);
- sort(llvm::parallel::par, vec.begin(), vec.end(),
- [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
- return compareAtoms(l, r, _customSorter);
- });
+ llvm::parallelSort(
+ vec,
+ [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
+ return compareAtoms(l, r, _customSorter);
+ });
LLVM_DEBUG(checkTransitivity(vec, _customSorter));
undecorate(atomRange, vec);
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
index 0be1c10a7ab0..acd919e4d411 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
@@ -540,6 +540,12 @@ MachOLinkingContext::searchDirForLibrary(StringRef path,
return llvm::None;
}
+ // Search for stub library
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, Twine("lib") + libName + ".tbd");
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+
// Search for dynamic library
fullPath.assign(path);
llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib");
@@ -604,7 +610,7 @@ bool MachOLinkingContext::validateImpl() {
}
// If -exported_symbols_list used, all exported symbols must be defined.
- if (_exportMode == ExportMode::whiteList) {
+ if (_exportMode == ExportMode::exported) {
for (const auto &symbol : _exportedSymbols)
addInitialUndefinedSymbol(symbol.getKey());
}
@@ -618,7 +624,7 @@ bool MachOLinkingContext::validateImpl() {
if (needsStubsPass())
addDeadStripRoot(binderSymbolName());
// If using -exported_symbols_list, make all exported symbols live.
- if (_exportMode == ExportMode::whiteList) {
+ if (_exportMode == ExportMode::exported) {
setGlobalsAreDeadStripRoots(false);
for (const auto &symbol : _exportedSymbols)
addDeadStripRoot(symbol.getKey());
@@ -852,9 +858,9 @@ bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const {
case ExportMode::globals:
llvm_unreachable("exportSymbolNamed() should not be called in this mode");
break;
- case ExportMode::whiteList:
+ case ExportMode::exported:
return _exportedSymbols.count(sym);
- case ExportMode::blackList:
+ case ExportMode::unexported:
return !_exportedSymbols.count(sym);
}
llvm_unreachable("_exportMode unknown enum value");
@@ -863,11 +869,11 @@ bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const {
std::string MachOLinkingContext::demangle(StringRef symbolName) const {
// Only try to demangle symbols if -demangle on command line
if (!demangleSymbols())
- return symbolName;
+ return std::string(symbolName);
// Only try to demangle symbols that look like C++ symbols
if (!symbolName.startswith("__Z"))
- return symbolName;
+ return std::string(symbolName);
SmallString<256> symBuff;
StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
@@ -882,7 +888,7 @@ std::string MachOLinkingContext::demangle(StringRef symbolName) const {
return result;
}
- return symbolName;
+ return std::string(symbolName);
}
static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index 963f1227fa44..87601ca1be8b 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
@@ -576,6 +576,26 @@ private:
MachOLinkingContext &_ctx;
};
+class MachOTAPIReader : public Reader {
+public:
+ MachOTAPIReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ return magic == file_magic::tapi_file;
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry &registry) const override {
+ std::unique_ptr<File> ret =
+ std::make_unique<TAPIFile>(std::move(mb), &_ctx);
+ return std::move(ret);
+ }
+
+private:
+ MachOLinkingContext &_ctx;
+};
+
} // namespace normalized
} // namespace mach_o
@@ -583,6 +603,7 @@ void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) {
MachOLinkingContext::Arch arch = ctx.arch();
add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx)));
add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx)));
+ add(std::unique_ptr<Reader>(new mach_o::normalized::MachOTAPIReader(ctx)));
addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
ctx.archHandler().kindStrings());
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index db11f73748d8..42ac711bc9dc 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
@@ -990,7 +990,7 @@ llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom,
inGlobalsRegion = false;
return llvm::Error::success();
case Atom::scopeLinkageUnit:
- if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) &&
+ if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::exported) &&
_ctx.exportSymbolNamed(atom->name())) {
return llvm::make_error<GenericError>(
Twine("cannot export hidden symbol ") + atom->name());
@@ -1037,7 +1037,7 @@ llvm::Error Util::addSymbols(const lld::File &atomFile,
// Add all stabs.
for (auto &stab : _stabs) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.type = static_cast<NListType>(stab.type);
sym.scope = 0;
sym.sect = stab.other;
@@ -1066,7 +1066,7 @@ llvm::Error Util::addSymbols(const lld::File &atomFile,
AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope };
globals.push_back(ai);
} else {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = atom->name();
sym.type = N_SECT;
sym.scope = symbolScope;
@@ -1082,7 +1082,7 @@ llvm::Error Util::addSymbols(const lld::File &atomFile,
char tmpName[16];
sprintf(tmpName, "L%04u", tempNum++);
StringRef tempRef(tmpName);
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = tempRef.copy(file.ownedAllocations);
sym.type = N_SECT;
sym.scope = 0;
@@ -1099,7 +1099,7 @@ llvm::Error Util::addSymbols(const lld::File &atomFile,
std::sort(globals.begin(), globals.end(), AtomSorter());
const uint32_t globalStartIndex = file.localSymbols.size();
for (AtomAndIndex &ai : globals) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = ai.atom->name();
sym.type = N_SECT;
sym.scope = ai.scope;
@@ -1124,7 +1124,7 @@ llvm::Error Util::addSymbols(const lld::File &atomFile,
std::sort(undefs.begin(), undefs.end(), AtomSorter());
const uint32_t start = file.globalSymbols.size() + file.localSymbols.size();
for (AtomAndIndex &ai : undefs) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
uint16_t desc = 0;
if (!rMode) {
uint8_t ordinal = 0;
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 3347bb13508e..0a59e24c47a8 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -223,10 +223,11 @@ Atom::Scope atomScope(uint8_t scope) {
llvm_unreachable("unknown scope value!");
}
-void appendSymbolsInSection(const std::vector<Symbol> &inSymbols,
- uint32_t sectionIndex,
- SmallVector<const Symbol *, 64> &outSyms) {
- for (const Symbol &sym : inSymbols) {
+void appendSymbolsInSection(
+ const std::vector<lld::mach_o::normalized::Symbol> &inSymbols,
+ uint32_t sectionIndex,
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> &outSyms) {
+ for (const lld::mach_o::normalized::Symbol &sym : inSymbols) {
// Only look at definition symbols.
if ((sym.type & N_TYPE) != N_SECT)
continue;
@@ -286,13 +287,14 @@ llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
}
// Find all symbols in this section.
- SmallVector<const Symbol *, 64> symbols;
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> symbols;
appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols);
appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols);
// Sort symbols.
std::sort(symbols.begin(), symbols.end(),
- [](const Symbol *lhs, const Symbol *rhs) -> bool {
+ [](const lld::mach_o::normalized::Symbol *lhs,
+ const lld::mach_o::normalized::Symbol *rhs) -> bool {
if (lhs == rhs)
return false;
// First by address.
@@ -300,7 +302,7 @@ llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
uint64_t rhsAddr = rhs->value;
if (lhsAddr != rhsAddr)
return lhsAddr < rhsAddr;
- // If same address, one is an alias so sort by scope.
+ // If same address, one is an alias so sort by scope.
Atom::Scope lScope = atomScope(lhs->scope);
Atom::Scope rScope = atomScope(rhs->scope);
if (lScope != rScope)
@@ -339,8 +341,8 @@ llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
scatterable, copyRefs);
}
- const Symbol *lastSym = nullptr;
- for (const Symbol *sym : symbols) {
+ const lld::mach_o::normalized::Symbol *lastSym = nullptr;
+ for (const lld::mach_o::normalized::Symbol *sym : symbols) {
if (lastSym != nullptr) {
// Ignore any assembler added "ltmpNNN" symbol at start of section
// if there is another symbol at the start.
@@ -550,7 +552,7 @@ llvm::Error convertRelocs(const Section &section,
auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result)
-> llvm::Error {
// Find symbol from index.
- const Symbol *sym = nullptr;
+ const lld::mach_o::normalized::Symbol *sym = nullptr;
uint32_t numStabs = normalizedFile.stabsSymbols.size();
uint32_t numLocal = normalizedFile.localSymbols.size();
uint32_t numGlobal = normalizedFile.globalSymbols.size();
@@ -1401,7 +1403,7 @@ llvm::Error parseObjCImageInfo(const Section &sect,
llvm::Expected<std::unique_ptr<lld::File>>
objectToAtoms(const NormalizedFile &normalizedFile, StringRef path,
bool copyRefs) {
- std::unique_ptr<MachOFile> file(new MachOFile(path));
+ auto file = std::make_unique<MachOFile>(path);
if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs))
return std::move(ec);
return std::unique_ptr<File>(std::move(file));
@@ -1411,7 +1413,7 @@ llvm::Expected<std::unique_ptr<lld::File>>
dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path,
bool copyRefs) {
// Instantiate SharedLibraryFile object.
- std::unique_ptr<MachODylibFile> file(new MachODylibFile(path));
+ auto file = std::make_unique<MachODylibFile>(path);
if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs))
return std::move(ec);
return std::unique_ptr<File>(std::move(file));
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/TLVPass.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/TLVPass.cpp
index 5f457b863d90..e0a031cfb07b 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/MachO/TLVPass.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/MachO/TLVPass.cpp
@@ -136,5 +136,5 @@ void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
pm.add(std::make_unique<TLVPass>(ctx));
}
-} // end namesapce mach_o
-} // end namesapce lld
+} // end namespace mach_o
+} // end namespace lld
diff --git a/contrib/llvm-project/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/contrib/llvm-project/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
index 23c20aa2ba1c..279ffe5e2505 100644
--- a/contrib/llvm-project/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
+++ b/contrib/llvm-project/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
@@ -89,7 +89,7 @@ public:
llvm::raw_string_ostream buffer(storage);
buffer << llvm::format("L%03d", _unnamedCounter++);
StringRef newName = copyString(buffer.str());
- _refNames[target] = newName;
+ _refNames[target] = std::string(newName);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "unnamed atom: creating ref-name: '"
<< newName << "' ("
@@ -119,7 +119,7 @@ public:
llvm::raw_string_ostream buffer(Storage);
buffer << atom.name() << llvm::format(".%03d", ++_collisionCount);
StringRef newName = copyString(buffer.str());
- _refNames[&atom] = newName;
+ _refNames[&atom] = std::string(newName);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "name collision: creating ref-name: '"
<< newName << "' ("
@@ -133,7 +133,7 @@ public:
llvm::raw_string_ostream buffer2(Storage2);
buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount);
StringRef newName2 = copyString(buffer2.str());
- _refNames[prevAtom] = newName2;
+ _refNames[prevAtom] = std::string(newName2);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "name collision: creating ref-name: '"
<< newName2 << "' ("
diff --git a/contrib/llvm-project/lld/tools/lld/lld.cpp b/contrib/llvm-project/lld/tools/lld/lld.cpp
index 9f123e76c984..c1a117e9cc3e 100644
--- a/contrib/llvm-project/lld/tools/lld/lld.cpp
+++ b/contrib/llvm-project/lld/tools/lld/lld.cpp
@@ -36,6 +36,7 @@
#include "llvm/Support/Host.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/PluginLoader.h"
#include <cstdlib>
using namespace lld;
@@ -44,10 +45,11 @@ using namespace llvm::sys;
enum Flavor {
Invalid,
- Gnu, // -flavor gnu
- WinLink, // -flavor link
- Darwin, // -flavor darwin
- Wasm, // -flavor wasm
+ Gnu, // -flavor gnu
+ WinLink, // -flavor link
+ Darwin, // -flavor darwin
+ DarwinNew, // -flavor darwinnew
+ Wasm, // -flavor wasm
};
LLVM_ATTRIBUTE_NORETURN static void die(const Twine &s) {
@@ -61,6 +63,7 @@ static Flavor getFlavor(StringRef s) {
.CasesLower("wasm", "ld-wasm", Wasm)
.CaseLower("link", WinLink)
.CasesLower("ld64", "ld64.lld", "darwin", Darwin)
+ .CaseLower("darwinnew", DarwinNew)
.Default(Invalid);
}
@@ -93,17 +96,9 @@ static bool isPETarget(std::vector<const char *> &v) {
}
static Flavor parseProgname(StringRef progname) {
-#if __APPLE__
- // Use Darwin driver for "ld" on Darwin.
- if (progname == "ld")
- return Darwin;
-#endif
-
-#if LLVM_ON_UNIX
- // Use GNU driver for "ld" on other Unix-like system.
+ // Use GNU driver for "ld" by default.
if (progname == "ld")
return Gnu;
-#endif
// Progname may be something like "lld-gnu". Parse it.
SmallVector<StringRef, 3> v;
@@ -159,6 +154,8 @@ int main(int argc, const char **argv) {
return !coff::link(args, canExitEarly(), llvm::outs(), llvm::errs());
case Darwin:
return !mach_o::link(args, canExitEarly(), llvm::outs(), llvm::errs());
+ case DarwinNew:
+ return !macho::link(args, canExitEarly(), llvm::outs(), llvm::errs());
case Wasm:
return !wasm::link(args, canExitEarly(), llvm::outs(), llvm::errs());
default: