summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-08-20 20:51:32 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-08-20 20:51:32 +0000
commitf1e1c239e31b467e17f1648b1f524fc9ab5b431a (patch)
treea855e7a2a8808555da60e6aa9601d6867eb23bac
parent7d6988fdd2aee0e033034e147f16fe05594a60e4 (diff)
downloadsrc-test2-f1e1c239e31b467e17f1648b1f524fc9ab5b431a.tar.gz
src-test2-f1e1c239e31b467e17f1648b1f524fc9ab5b431a.zip
Notes
-rw-r--r--COFF/CMakeLists.txt1
-rw-r--r--COFF/Chunks.cpp855
-rw-r--r--COFF/Chunks.h574
-rw-r--r--COFF/Config.h241
-rw-r--r--COFF/DLL.cpp703
-rw-r--r--COFF/DLL.h60
-rw-r--r--COFF/DebugTypes.cpp268
-rw-r--r--COFF/DebugTypes.h60
-rw-r--r--COFF/Driver.cpp1922
-rw-r--r--COFF/Driver.h111
-rw-r--r--COFF/DriverUtils.cpp965
-rw-r--r--COFF/ICF.cpp283
-rw-r--r--COFF/ICF.h9
-rw-r--r--COFF/InputFiles.cpp915
-rw-r--r--COFF/InputFiles.h209
-rw-r--r--COFF/LTO.cpp188
-rw-r--r--COFF/LTO.h19
-rw-r--r--COFF/MapFile.cpp115
-rw-r--r--COFF/MapFile.h9
-rw-r--r--COFF/MarkLive.cpp65
-rw-r--r--COFF/MarkLive.h11
-rw-r--r--COFF/MinGW.cpp160
-rw-r--r--COFF/MinGW.h25
-rw-r--r--COFF/Options.td65
-rw-r--r--COFF/PDB.cpp2035
-rw-r--r--COFF/PDB.h19
-rw-r--r--COFF/SymbolTable.cpp729
-rw-r--r--COFF/SymbolTable.h88
-rw-r--r--COFF/Symbols.cpp106
-rw-r--r--COFF/Symbols.h243
-rw-r--r--COFF/TypeMerger.h65
-rw-r--r--COFF/Writer.cpp2044
-rw-r--r--COFF/Writer.h62
-rw-r--r--Common/Args.cpp87
-rw-r--r--Common/CMakeLists.txt27
-rw-r--r--Common/ErrorHandler.cpp160
-rw-r--r--Common/Filesystem.cpp (renamed from ELF/Filesystem.cpp)55
-rw-r--r--Common/Memory.cpp19
-rw-r--r--Common/Reproduce.cpp67
-rw-r--r--Common/Strings.cpp99
-rw-r--r--Common/TargetOptionsCommandFlags.cpp18
-rw-r--r--Common/Threads.cpp9
-rw-r--r--Common/Timer.cpp61
-rw-r--r--Common/Version.cpp42
-rw-r--r--ELF/AArch64ErrataFix.cpp495
-rw-r--r--ELF/AArch64ErrataFix.h19
-rw-r--r--ELF/Arch/AArch64.cpp466
-rw-r--r--ELF/Arch/AMDGPU.cpp70
-rw-r--r--ELF/Arch/ARM.cpp462
-rw-r--r--ELF/Arch/AVR.cpp37
-rw-r--r--ELF/Arch/Hexagon.cpp195
-rw-r--r--ELF/Arch/MSP430.cpp49
-rw-r--r--ELF/Arch/Mips.cpp607
-rw-r--r--ELF/Arch/MipsArchTree.cpp239
-rw-r--r--ELF/Arch/PPC.cpp407
-rw-r--r--ELF/Arch/PPC64.cpp730
-rw-r--r--ELF/Arch/RISCV.cpp401
-rw-r--r--ELF/Arch/SPARCV9.cpp102
-rw-r--r--ELF/Arch/X86.cpp442
-rw-r--r--ELF/Arch/X86_64.cpp651
-rw-r--r--ELF/Bits.h35
-rw-r--r--ELF/CMakeLists.txt1
-rw-r--r--ELF/CallGraphSort.cpp185
-rw-r--r--ELF/CallGraphSort.h7
-rw-r--r--ELF/Config.h356
-rw-r--r--ELF/DWARF.cpp132
-rw-r--r--ELF/DWARF.h65
-rw-r--r--ELF/Driver.cpp1899
-rw-r--r--ELF/Driver.h42
-rw-r--r--ELF/DriverUtils.cpp195
-rw-r--r--ELF/EhFrame.cpp137
-rw-r--r--ELF/EhFrame.h11
-rw-r--r--ELF/Filesystem.h23
-rw-r--r--ELF/ICF.cpp337
-rw-r--r--ELF/ICF.h7
-rw-r--r--ELF/InputFiles.cpp1706
-rw-r--r--ELF/InputFiles.h358
-rw-r--r--ELF/InputSection.cpp1113
-rw-r--r--ELF/InputSection.h278
-rw-r--r--ELF/LTO.cpp327
-rw-r--r--ELF/LTO.h21
-rw-r--r--ELF/LinkerScript.cpp1018
-rw-r--r--ELF/LinkerScript.h222
-rw-r--r--ELF/MapFile.cpp234
-rw-r--r--ELF/MapFile.h7
-rw-r--r--ELF/MarkLive.cpp385
-rw-r--r--ELF/MarkLive.h7
-rw-r--r--ELF/Options.td60
-rw-r--r--ELF/OutputSections.cpp442
-rw-r--r--ELF/OutputSections.h122
-rw-r--r--ELF/Relocations.cpp1663
-rw-r--r--ELF/Relocations.h178
-rw-r--r--ELF/ScriptLexer.cpp224
-rw-r--r--ELF/ScriptLexer.h29
-rw-r--r--ELF/ScriptParser.cpp1313
-rw-r--r--ELF/ScriptParser.h17
-rw-r--r--ELF/SymbolTable.cpp835
-rw-r--r--ELF/SymbolTable.h98
-rw-r--r--ELF/Symbols.cpp627
-rw-r--r--ELF/Symbols.h461
-rw-r--r--ELF/SyntheticSections.cpp3698
-rw-r--r--ELF/SyntheticSections.h893
-rw-r--r--ELF/Target.cpp101
-rw-r--r--ELF/Target.h233
-rw-r--r--ELF/Thunks.cpp809
-rw-r--r--ELF/Thunks.h42
-rw-r--r--ELF/Writer.cpp2668
-rw-r--r--ELF/Writer.h29
-rw-r--r--LICENSE.TXT256
-rw-r--r--docs/NewLLD.rst8
-rw-r--r--docs/Partitions.rst116
-rw-r--r--docs/ReleaseNotes.rst63
-rw-r--r--docs/WebAssembly.rst78
-rw-r--r--docs/conf.py4
-rw-r--r--docs/getting_started.rst35
-rw-r--r--docs/index.rst4
-rw-r--r--docs/ld.lld.1119
-rw-r--r--docs/missingkeyfunction.rst35
-rw-r--r--docs/partitions.dot22
-rw-r--r--docs/partitions.svg110
-rw-r--r--docs/sphinx_intro.rst4
-rw-r--r--include/lld/Common/Args.h23
-rw-r--r--include/lld/Common/Driver.h27
-rw-r--r--include/lld/Common/ErrorHandler.h98
-rw-r--r--include/lld/Common/Filesystem.h20
-rw-r--r--include/lld/Common/LLVM.h9
-rw-r--r--include/lld/Common/Memory.h25
-rw-r--r--include/lld/Common/Reproduce.h17
-rw-r--r--include/lld/Common/Strings.h23
-rw-r--r--include/lld/Common/TargetOptionsCommandFlags.h15
-rw-r--r--include/lld/Common/Threads.h34
-rw-r--r--include/lld/Common/Timer.h29
-rw-r--r--include/lld/Common/Version.h7
-rw-r--r--include/lld/Core/AbsoluteAtom.h7
-rw-r--r--include/lld/Core/ArchiveLibraryFile.h7
-rw-r--r--include/lld/Core/Atom.h7
-rw-r--r--include/lld/Core/DefinedAtom.h7
-rw-r--r--include/lld/Core/Error.h7
-rw-r--r--include/lld/Core/File.h7
-rw-r--r--include/lld/Core/Instrumentation.h7
-rw-r--r--include/lld/Core/LinkingContext.h7
-rw-r--r--include/lld/Core/Node.h7
-rw-r--r--include/lld/Core/Pass.h7
-rw-r--r--include/lld/Core/PassManager.h7
-rw-r--r--include/lld/Core/Reader.h7
-rw-r--r--include/lld/Core/Reference.h7
-rw-r--r--include/lld/Core/Resolver.h7
-rw-r--r--include/lld/Core/SharedLibraryAtom.h7
-rw-r--r--include/lld/Core/SharedLibraryFile.h7
-rw-r--r--include/lld/Core/Simple.h7
-rw-r--r--include/lld/Core/SymbolTable.h7
-rw-r--r--include/lld/Core/UndefinedAtom.h7
-rw-r--r--include/lld/Core/Writer.h7
-rw-r--r--include/lld/ReaderWriter/MachOLinkingContext.h7
-rw-r--r--include/lld/ReaderWriter/YamlContext.h7
-rw-r--r--lib/Core/DefinedAtom.cpp7
-rw-r--r--lib/Core/Error.cpp7
-rw-r--r--lib/Core/File.cpp7
-rw-r--r--lib/Core/LinkingContext.cpp7
-rw-r--r--lib/Core/Reader.cpp7
-rw-r--r--lib/Core/Resolver.cpp7
-rw-r--r--lib/Core/SymbolTable.cpp7
-rw-r--r--lib/Core/Writer.cpp7
-rw-r--r--lib/Driver/DarwinLdDriver.cpp31
-rw-r--r--lib/ReaderWriter/FileArchive.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler.h7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm64.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp7
-rw-r--r--lib/ReaderWriter/MachO/Atoms.h7
-rw-r--r--lib/ReaderWriter/MachO/CompactUnwindPass.cpp19
-rw-r--r--lib/ReaderWriter/MachO/DebugInfo.h7
-rw-r--r--lib/ReaderWriter/MachO/ExecutableAtoms.h7
-rw-r--r--lib/ReaderWriter/MachO/File.h7
-rw-r--r--lib/ReaderWriter/MachO/FlatNamespaceFile.h7
-rw-r--r--lib/ReaderWriter/MachO/GOTPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/LayoutPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/LayoutPass.h7
-rw-r--r--lib/ReaderWriter/MachO/MachOLinkingContext.cpp19
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFile.h10
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp34
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp10
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachOPasses.h7
-rw-r--r--lib/ReaderWriter/MachO/ObjCPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/SectCreateFile.h7
-rw-r--r--lib/ReaderWriter/MachO/ShimPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/StubsPass.cpp12
-rw-r--r--lib/ReaderWriter/MachO/TLVPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/WriterMachO.cpp7
-rw-r--r--lib/ReaderWriter/YAML/ReaderWriterYAML.cpp7
-rw-r--r--tools/lld/CMakeLists.txt1
-rw-r--r--tools/lld/lld.cpp107
198 files changed, 24846 insertions, 19860 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index bb241e788c19..c7ef7c47fea1 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -8,6 +8,7 @@ endif()
add_lld_library(lldCOFF
Chunks.cpp
+ DebugTypes.cpp
DLL.cpp
Driver.cpp
DriverUtils.cpp
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 29131d7eb8db..0e43d2b478b4 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -1,9 +1,8 @@
//===- Chunks.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -30,203 +29,201 @@ using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
-SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
- : Chunk(SectionKind), Repl(this), Header(H), File(F),
- Relocs(File->getCOFFObj()->getRelocations(Header)) {
- // Initialize SectionName.
- File->getCOFFObj()->getSectionName(Header, SectionName);
+SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
+ : Chunk(SectionKind), file(f), header(h), repl(this) {
+ // Initialize relocs.
+ setRelocs(file->getCOFFObj()->getRelocations(header));
+
+ // Initialize sectionName.
+ StringRef sectionName;
+ if (Expected<StringRef> e = file->getCOFFObj()->getSectionName(header))
+ sectionName = *e;
+ sectionNameData = sectionName.data();
+ sectionNameSize = sectionName.size();
- Alignment = Header->getAlignment();
+ setAlignment(header->getAlignment());
+
+ hasData = !(header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
// If linker GC is disabled, every chunk starts out alive. If linker GC is
// enabled, treat non-comdat sections as roots. Generally optimized object
// files will be built with -ffunction-sections or /Gy, so most things worth
// stripping will be in a comdat.
- Live = !Config->DoGC || !isCOMDAT();
-}
-
-// Initialize the RelocTargets vector, to allow redirecting certain relocations
-// to a thunk instead of the actual symbol the relocation's symbol table index
-// indicates.
-void SectionChunk::readRelocTargets() {
- assert(RelocTargets.empty());
- RelocTargets.reserve(Relocs.size());
- for (const coff_relocation &Rel : Relocs)
- RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex));
+ live = !config->doGC || !isCOMDAT();
}
-// Reset RelocTargets to their original targets before thunks were added.
-void SectionChunk::resetRelocTargets() {
- for (size_t I = 0, E = Relocs.size(); I < E; ++I)
- RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex);
-}
+// SectionChunk is one of the most frequently allocated classes, so it is
+// important to keep it as compact as possible. As of this writing, the number
+// below is the size of this class on x64 platforms.
+static_assert(sizeof(SectionChunk) <= 88, "SectionChunk grew unexpectedly");
-static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
-static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
-static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
-static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); }
-static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); }
+static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); }
+static void add32(uint8_t *p, int32_t v) { write32le(p, read32le(p) + v); }
+static void add64(uint8_t *p, int64_t v) { write64le(p, read64le(p) + v); }
+static void or16(uint8_t *p, uint16_t v) { write16le(p, read16le(p) | v); }
+static void or32(uint8_t *p, uint32_t v) { write32le(p, read32le(p) | v); }
// Verify that given sections are appropriate targets for SECREL
// relocations. This check is relaxed because unfortunately debug
// sections have section-relative relocations against absolute symbols.
-static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) {
- if (OS)
+static bool checkSecRel(const SectionChunk *sec, OutputSection *os) {
+ if (os)
return true;
- if (Sec->isCodeView())
+ if (sec->isCodeView())
return false;
error("SECREL relocation cannot be applied to absolute symbols");
return false;
}
-static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (!checkSecRel(Sec, OS))
+static void applySecRel(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (!checkSecRel(sec, os))
return;
- uint64_t SecRel = S - OS->getRVA();
- if (SecRel > UINT32_MAX) {
- error("overflow in SECREL relocation in section: " + Sec->getSectionName());
+ uint64_t secRel = s - os->getRVA();
+ if (secRel > UINT32_MAX) {
+ error("overflow in SECREL relocation in section: " + sec->getSectionName());
return;
}
- add32(Off, SecRel);
+ add32(off, secRel);
}
-static void applySecIdx(uint8_t *Off, OutputSection *OS) {
+static void applySecIdx(uint8_t *off, OutputSection *os) {
// Absolute symbol doesn't have section index, but section index relocation
// against absolute symbol should be resolved to one plus the last output
// section index. This is required for compatibility with MSVC.
- if (OS)
- add16(Off, OS->SectionIndex);
+ if (os)
+ add16(off, os->sectionIndex);
else
- add16(Off, DefinedAbsolute::NumOutputSections + 1);
-}
-
-void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
- switch (Type) {
- case IMAGE_REL_AMD64_ADDR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_AMD64_ADDR64: add64(Off, S + Config->ImageBase); break;
- case IMAGE_REL_AMD64_ADDR32NB: add32(Off, S); break;
- case IMAGE_REL_AMD64_REL32: add32(Off, S - P - 4); break;
- case IMAGE_REL_AMD64_REL32_1: add32(Off, S - P - 5); break;
- case IMAGE_REL_AMD64_REL32_2: add32(Off, S - P - 6); break;
- case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break;
- case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
- case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
- case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break;
- case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break;
+ add16(off, DefinedAbsolute::numOutputSections + 1);
+}
+
+void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
+ switch (type) {
+ case IMAGE_REL_AMD64_ADDR32: add32(off, s + config->imageBase); break;
+ case IMAGE_REL_AMD64_ADDR64: add64(off, s + config->imageBase); break;
+ case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break;
+ case IMAGE_REL_AMD64_REL32: add32(off, s - p - 4); break;
+ case IMAGE_REL_AMD64_REL32_1: add32(off, s - p - 5); break;
+ case IMAGE_REL_AMD64_REL32_2: add32(off, s - p - 6); break;
+ case IMAGE_REL_AMD64_REL32_3: add32(off, s - p - 7); break;
+ case IMAGE_REL_AMD64_REL32_4: add32(off, s - p - 8); break;
+ case IMAGE_REL_AMD64_REL32_5: add32(off, s - p - 9); break;
+ case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
-void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
- switch (Type) {
+void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
+ switch (type) {
case IMAGE_REL_I386_ABSOLUTE: break;
- case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
- case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
- case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break;
- case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break;
+ case IMAGE_REL_I386_DIR32: add32(off, s + config->imageBase); break;
+ case IMAGE_REL_I386_DIR32NB: add32(off, s); break;
+ case IMAGE_REL_I386_REL32: add32(off, s - p - 4); break;
+ case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
-static void applyMOV(uint8_t *Off, uint16_t V) {
- write16le(Off, (read16le(Off) & 0xfbf0) | ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
- write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff));
+static void applyMOV(uint8_t *off, uint16_t v) {
+ write16le(off, (read16le(off) & 0xfbf0) | ((v & 0x800) >> 1) | ((v >> 12) & 0xf));
+ write16le(off + 2, (read16le(off + 2) & 0x8f00) | ((v & 0x700) << 4) | (v & 0xff));
}
-static uint16_t readMOV(uint8_t *Off, bool MOVT) {
- uint16_t Op1 = read16le(Off);
- if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240))
- error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
+static uint16_t readMOV(uint8_t *off, bool movt) {
+ uint16_t op1 = read16le(off);
+ if ((op1 & 0xfbf0) != (movt ? 0xf2c0 : 0xf240))
+ error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") +
" instruction in MOV32T relocation");
- uint16_t Op2 = read16le(Off + 2);
- if ((Op2 & 0x8000) != 0)
- error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
+ uint16_t op2 = read16le(off + 2);
+ if ((op2 & 0x8000) != 0)
+ error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") +
" instruction in MOV32T relocation");
- return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) |
- ((Op1 & 0x000f) << 12);
+ return (op2 & 0x00ff) | ((op2 >> 4) & 0x0700) | ((op1 << 1) & 0x0800) |
+ ((op1 & 0x000f) << 12);
}
-void applyMOV32T(uint8_t *Off, uint32_t V) {
- uint16_t ImmW = readMOV(Off, false); // read MOVW operand
- uint16_t ImmT = readMOV(Off + 4, true); // read MOVT operand
- uint32_t Imm = ImmW | (ImmT << 16);
- V += Imm; // add the immediate offset
- applyMOV(Off, V); // set MOVW operand
- applyMOV(Off + 4, V >> 16); // set MOVT operand
+void applyMOV32T(uint8_t *off, uint32_t v) {
+ uint16_t immW = readMOV(off, false); // read MOVW operand
+ uint16_t immT = readMOV(off + 4, true); // read MOVT operand
+ uint32_t imm = immW | (immT << 16);
+ v += imm; // add the immediate offset
+ applyMOV(off, v); // set MOVW operand
+ applyMOV(off + 4, v >> 16); // set MOVT operand
}
-static void applyBranch20T(uint8_t *Off, int32_t V) {
- if (!isInt<21>(V))
+static void applyBranch20T(uint8_t *off, int32_t v) {
+ if (!isInt<21>(v))
error("relocation out of range");
- uint32_t S = V < 0 ? 1 : 0;
- uint32_t J1 = (V >> 19) & 1;
- uint32_t J2 = (V >> 18) & 1;
- or16(Off, (S << 10) | ((V >> 12) & 0x3f));
- or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
+ uint32_t s = v < 0 ? 1 : 0;
+ uint32_t j1 = (v >> 19) & 1;
+ uint32_t j2 = (v >> 18) & 1;
+ or16(off, (s << 10) | ((v >> 12) & 0x3f));
+ or16(off + 2, (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff));
}
-void applyBranch24T(uint8_t *Off, int32_t V) {
- if (!isInt<25>(V))
+void applyBranch24T(uint8_t *off, int32_t v) {
+ if (!isInt<25>(v))
error("relocation out of range");
- uint32_t S = V < 0 ? 1 : 0;
- uint32_t J1 = ((~V >> 23) & 1) ^ S;
- uint32_t J2 = ((~V >> 22) & 1) ^ S;
- or16(Off, (S << 10) | ((V >> 12) & 0x3ff));
+ uint32_t s = v < 0 ? 1 : 0;
+ uint32_t j1 = ((~v >> 23) & 1) ^ s;
+ uint32_t j2 = ((~v >> 22) & 1) ^ s;
+ or16(off, (s << 10) | ((v >> 12) & 0x3ff));
// Clear out the J1 and J2 bits which may be set.
- write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
+ write16le(off + 2, (read16le(off + 2) & 0xd000) | (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff));
}
-void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
+void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
// Pointer to thumb code must have the LSB set.
- uint64_t SX = S;
- if (OS && (OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
- SX |= 1;
- switch (Type) {
- case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break;
- case IMAGE_REL_ARM_ADDR32NB: add32(Off, SX); break;
- case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, SX + Config->ImageBase); break;
- case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, SX - P - 4); break;
- case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, SX - P - 4); break;
- case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, SX - P - 4); break;
- case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break;
- case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break;
+ uint64_t sx = s;
+ if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
+ sx |= 1;
+ switch (type) {
+ case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break;
+ case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break;
+ case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->imageBase); break;
+ case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break;
+ case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break;
+ case IMAGE_REL_ARM_BLX23T: applyBranch24T(off, sx - p - 4); break;
+ case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break;
+ case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
// Interpret the existing immediate value as a byte offset to the
// target symbol, then update the instruction with the immediate as
// the page offset from the current instruction to the target.
-void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
- uint32_t Orig = read32le(Off);
- uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC);
- S += Imm;
- Imm = (S >> Shift) - (P >> Shift);
- uint32_t ImmLo = (Imm & 0x3) << 29;
- uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
- uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
- write32le(Off, (Orig & ~Mask) | ImmLo | ImmHi);
+void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift) {
+ uint32_t orig = read32le(off);
+ uint64_t imm = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);
+ s += imm;
+ imm = (s >> shift) - (p >> shift);
+ uint32_t immLo = (imm & 0x3) << 29;
+ uint32_t immHi = (imm & 0x1FFFFC) << 3;
+ uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
+ write32le(off, (orig & ~mask) | immLo | immHi);
}
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
// Optionally limit the range of the written immediate by one or more bits
-// (RangeLimit).
-void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
- uint32_t Orig = read32le(Off);
- Imm += (Orig >> 10) & 0xFFF;
- Orig &= ~(0xFFF << 10);
- write32le(Off, Orig | ((Imm & (0xFFF >> RangeLimit)) << 10));
+// (rangeLimit).
+void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit) {
+ uint32_t orig = read32le(off);
+ imm += (orig >> 10) & 0xFFF;
+ orig &= ~(0xFFF << 10);
+ write32le(off, orig | ((imm & (0xFFF >> rangeLimit)) << 10));
}
// Add the 12 bit page offset to the existing immediate.
@@ -237,171 +234,178 @@ void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
// Even if larger loads/stores have a larger range, limit the
// effective offset to 12 bit, since it is intended to be a
// page offset.
-static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
- uint32_t Orig = read32le(Off);
- uint32_t Size = Orig >> 30;
+static void applyArm64Ldr(uint8_t *off, uint64_t imm) {
+ uint32_t orig = read32le(off);
+ uint32_t size = orig >> 30;
// 0x04000000 indicates SIMD/FP registers
// 0x00800000 indicates 128 bit
- if ((Orig & 0x4800000) == 0x4800000)
- Size += 4;
- if ((Imm & ((1 << Size) - 1)) != 0)
+ if ((orig & 0x4800000) == 0x4800000)
+ size += 4;
+ if ((imm & ((1 << size) - 1)) != 0)
error("misaligned ldr/str offset");
- applyArm64Imm(Off, Imm >> Size, Size);
+ applyArm64Imm(off, imm >> size, size);
}
-static void applySecRelLow12A(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (checkSecRel(Sec, OS))
- applyArm64Imm(Off, (S - OS->getRVA()) & 0xfff, 0);
+static void applySecRelLow12A(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (checkSecRel(sec, os))
+ applyArm64Imm(off, (s - os->getRVA()) & 0xfff, 0);
}
-static void applySecRelHigh12A(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (!checkSecRel(Sec, OS))
+static void applySecRelHigh12A(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (!checkSecRel(sec, os))
return;
- uint64_t SecRel = (S - OS->getRVA()) >> 12;
- if (0xfff < SecRel) {
+ uint64_t secRel = (s - os->getRVA()) >> 12;
+ if (0xfff < secRel) {
error("overflow in SECREL_HIGH12A relocation in section: " +
- Sec->getSectionName());
+ sec->getSectionName());
return;
}
- applyArm64Imm(Off, SecRel & 0xfff, 0);
+ applyArm64Imm(off, secRel & 0xfff, 0);
}
-static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (checkSecRel(Sec, OS))
- applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff);
+static void applySecRelLdr(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (checkSecRel(sec, os))
+ applyArm64Ldr(off, (s - os->getRVA()) & 0xfff);
}
-void applyArm64Branch26(uint8_t *Off, int64_t V) {
- if (!isInt<28>(V))
+void applyArm64Branch26(uint8_t *off, int64_t v) {
+ if (!isInt<28>(v))
error("relocation out of range");
- or32(Off, (V & 0x0FFFFFFC) >> 2);
+ or32(off, (v & 0x0FFFFFFC) >> 2);
}
-static void applyArm64Branch19(uint8_t *Off, int64_t V) {
- if (!isInt<21>(V))
+static void applyArm64Branch19(uint8_t *off, int64_t v) {
+ if (!isInt<21>(v))
error("relocation out of range");
- or32(Off, (V & 0x001FFFFC) << 3);
+ or32(off, (v & 0x001FFFFC) << 3);
}
-static void applyArm64Branch14(uint8_t *Off, int64_t V) {
- if (!isInt<16>(V))
+static void applyArm64Branch14(uint8_t *off, int64_t v) {
+ if (!isInt<16>(v))
error("relocation out of range");
- or32(Off, (V & 0x0000FFFC) << 3);
-}
-
-void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
- switch (Type) {
- case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P, 12); break;
- case IMAGE_REL_ARM64_REL21: applyArm64Addr(Off, S, P, 0); break;
- case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break;
- case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break;
- case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(Off, S - P); break;
- case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(Off, S - P); break;
- case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(Off, S - P); break;
- case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break;
- case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECTION: applySecIdx(Off, OS); break;
+ or32(off, (v & 0x0000FFFC) << 3);
+}
+
+void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
+ switch (type) {
+ case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break;
+ case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break;
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(off, s & 0xfff, 0); break;
+ case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(off, s & 0xfff); break;
+ case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(off, s - p); break;
+ case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(off, s - p); break;
+ case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(off, s - p); break;
+ case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break;
+ case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break;
+ case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break;
+ case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
-static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk,
- Defined *Sym,
- const coff_relocation &Rel) {
+static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
+ Defined *sym,
+ const coff_relocation &rel) {
// Don't report these errors when the relocation comes from a debug info
// section or in mingw mode. MinGW mode object files (built by GCC) can
// have leftover sections with relocations against discarded comdat
// sections. Such sections are left as is, with relocations untouched.
- if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW)
+ if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw)
return;
// Get the name of the symbol. If it's null, it was discarded early, so we
// have to go back to the object file.
- ObjFile *File = FromChunk->File;
- StringRef Name;
- if (Sym) {
- Name = Sym->getName();
+ ObjFile *file = fromChunk->file;
+ StringRef name;
+ if (sym) {
+ name = sym->getName();
} else {
- COFFSymbolRef COFFSym =
- check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
- File->getCOFFObj()->getSymbolName(COFFSym, Name);
+ COFFSymbolRef coffSym =
+ check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex));
+ file->getCOFFObj()->getSymbolName(coffSym, name);
}
- error("relocation against symbol in discarded section: " + Name +
- getSymbolLocations(File, Rel.SymbolTableIndex));
+ std::vector<std::string> symbolLocations =
+ getSymbolLocations(file, rel.SymbolTableIndex);
+
+ std::string out;
+ llvm::raw_string_ostream os(out);
+ os << "relocation against symbol in discarded section: " + name;
+ for (const std::string &s : symbolLocations)
+ os << s;
+ error(os.str());
}
-void SectionChunk::writeTo(uint8_t *Buf) const {
- if (!hasData())
+void SectionChunk::writeTo(uint8_t *buf) const {
+ if (!hasData)
return;
// Copy section contents from source object file to output file.
- ArrayRef<uint8_t> A = getContents();
- if (!A.empty())
- memcpy(Buf + OutputSectionOff, A.data(), A.size());
+ ArrayRef<uint8_t> a = getContents();
+ if (!a.empty())
+ memcpy(buf, a.data(), a.size());
// Apply relocations.
- size_t InputSize = getSize();
- for (size_t I = 0, E = Relocs.size(); I < E; I++) {
- const coff_relocation &Rel = Relocs[I];
+ size_t inputSize = getSize();
+ for (size_t i = 0, e = relocsSize; i < e; i++) {
+ const coff_relocation &rel = relocsData[i];
// Check for an invalid relocation offset. This check isn't perfect, because
// we don't have the relocation size, which is only known after checking the
// machine and relocation type. As a result, a relocation may overwrite the
// beginning of the following input section.
- if (Rel.VirtualAddress >= InputSize) {
+ if (rel.VirtualAddress >= inputSize) {
error("relocation points beyond the end of its parent section");
continue;
}
- uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
+ uint8_t *off = buf + rel.VirtualAddress;
- // Use the potentially remapped Symbol instead of the one that the
- // relocation points to.
- auto *Sym = dyn_cast_or_null<Defined>(RelocTargets[I]);
+ auto *sym =
+ dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
// Get the output section of the symbol for this relocation. The output
// section is needed to compute SECREL and SECTION relocations used in debug
// info.
- Chunk *C = Sym ? Sym->getChunk() : nullptr;
- OutputSection *OS = C ? C->getOutputSection() : nullptr;
+ Chunk *c = sym ? sym->getChunk() : nullptr;
+ OutputSection *os = c ? c->getOutputSection() : nullptr;
// Skip the relocation if it refers to a discarded section, and diagnose it
// as an error if appropriate. If a symbol was discarded early, it may be
// null. If it was discarded late, the output section will be null, unless
// it was an absolute or synthetic symbol.
- if (!Sym ||
- (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym))) {
- maybeReportRelocationToDiscarded(this, Sym, Rel);
+ if (!sym ||
+ (!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(sym))) {
+ maybeReportRelocationToDiscarded(this, sym, rel);
continue;
}
- uint64_t S = Sym->getRVA();
+ uint64_t s = sym->getRVA();
// Compute the RVA of the relocation for relative relocations.
- uint64_t P = RVA + Rel.VirtualAddress;
- switch (Config->Machine) {
+ uint64_t p = rva + rel.VirtualAddress;
+ switch (config->machine) {
case AMD64:
- applyRelX64(Off, Rel.Type, OS, S, P);
+ applyRelX64(off, rel.Type, os, s, p);
break;
case I386:
- applyRelX86(Off, Rel.Type, OS, S, P);
+ applyRelX86(off, rel.Type, os, s, p);
break;
case ARMNT:
- applyRelARM(Off, Rel.Type, OS, S, P);
+ applyRelARM(off, rel.Type, os, s, p);
break;
case ARM64:
- applyRelARM64(Off, Rel.Type, OS, S, P);
+ applyRelARM64(off, rel.Type, os, s, p);
break;
default:
llvm_unreachable("unknown machine type");
@@ -409,28 +413,32 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
}
}
-void SectionChunk::addAssociative(SectionChunk *Child) {
- AssocChildren.push_back(Child);
+void SectionChunk::addAssociative(SectionChunk *child) {
+ // Insert this child at the head of the list.
+ assert(child->assocChildren == nullptr &&
+ "associated sections cannot have their own associated children");
+ child->assocChildren = assocChildren;
+ assocChildren = child;
}
-static uint8_t getBaserelType(const coff_relocation &Rel) {
- switch (Config->Machine) {
+static uint8_t getBaserelType(const coff_relocation &rel) {
+ switch (config->machine) {
case AMD64:
- if (Rel.Type == IMAGE_REL_AMD64_ADDR64)
+ if (rel.Type == IMAGE_REL_AMD64_ADDR64)
return IMAGE_REL_BASED_DIR64;
return IMAGE_REL_BASED_ABSOLUTE;
case I386:
- if (Rel.Type == IMAGE_REL_I386_DIR32)
+ if (rel.Type == IMAGE_REL_I386_DIR32)
return IMAGE_REL_BASED_HIGHLOW;
return IMAGE_REL_BASED_ABSOLUTE;
case ARMNT:
- if (Rel.Type == IMAGE_REL_ARM_ADDR32)
+ if (rel.Type == IMAGE_REL_ARM_ADDR32)
return IMAGE_REL_BASED_HIGHLOW;
- if (Rel.Type == IMAGE_REL_ARM_MOV32T)
+ if (rel.Type == IMAGE_REL_ARM_MOV32T)
return IMAGE_REL_BASED_ARM_MOV32T;
return IMAGE_REL_BASED_ABSOLUTE;
case ARM64:
- if (Rel.Type == IMAGE_REL_ARM64_ADDR64)
+ if (rel.Type == IMAGE_REL_ARM64_ADDR64)
return IMAGE_REL_BASED_DIR64;
return IMAGE_REL_BASED_ABSOLUTE;
default:
@@ -442,18 +450,16 @@ static uint8_t getBaserelType(const coff_relocation &Rel) {
// Collect all locations that contain absolute addresses, which need to be
// fixed by the loader if load-time relocation is needed.
// Only called when base relocation is enabled.
-void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
- for (size_t I = 0, E = Relocs.size(); I < E; I++) {
- const coff_relocation &Rel = Relocs[I];
- uint8_t Ty = getBaserelType(Rel);
- if (Ty == IMAGE_REL_BASED_ABSOLUTE)
+void SectionChunk::getBaserels(std::vector<Baserel> *res) {
+ for (size_t i = 0, e = relocsSize; i < e; i++) {
+ const coff_relocation &rel = relocsData[i];
+ uint8_t ty = getBaserelType(rel);
+ if (ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
- // Use the potentially remapped Symbol instead of the one that the
- // relocation points to.
- Symbol *Target = RelocTargets[I];
- if (!Target || isa<DefinedAbsolute>(Target))
+ Symbol *target = file->getSymbol(rel.SymbolTableIndex);
+ if (!target || isa<DefinedAbsolute>(target))
continue;
- Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
+ res->emplace_back(rva + rel.VirtualAddress, ty);
}
}
@@ -464,7 +470,7 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
// another DLL) This returns the size the relocation is supposed to update,
// in bits, or 0 if the relocation cannot be handled as a runtime pseudo
// relocation.
-static int getRuntimePseudoRelocSize(uint16_t Type) {
+static int getRuntimePseudoRelocSize(uint16_t type) {
// Relocations that either contain an absolute address, or a plain
// relative offset, since the runtime pseudo reloc implementation
// adds 8/16/32/64 bit values to a memory address.
@@ -490,9 +496,9 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
// the image, or temporarily changed at runtime with VirtualProtect.
// Since this only operates on direct address values, it doesn't work for
// ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations.
- switch (Config->Machine) {
+ switch (config->machine) {
case AMD64:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_AMD64_ADDR64:
return 64;
case IMAGE_REL_AMD64_ADDR32:
@@ -507,7 +513,7 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
return 0;
}
case I386:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_I386_DIR32:
case IMAGE_REL_I386_REL32:
return 32;
@@ -515,14 +521,14 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
return 0;
}
case ARMNT:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_ARM_ADDR32:
return 32;
default:
return 0;
}
case ARM64:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_ARM64_ADDR64:
return 64;
case IMAGE_REL_ARM64_ADDR32:
@@ -541,75 +547,106 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
// to a module local variable, which turned out to actually need to be
// imported from another DLL).
void SectionChunk::getRuntimePseudoRelocs(
- std::vector<RuntimePseudoReloc> &Res) {
- for (const coff_relocation &Rel : Relocs) {
- auto *Target =
- dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
- if (!Target || !Target->IsRuntimePseudoReloc)
+ std::vector<RuntimePseudoReloc> &res) {
+ for (const coff_relocation &rel : getRelocs()) {
+ auto *target =
+ dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
+ if (!target || !target->isRuntimePseudoReloc)
continue;
- int SizeInBits = getRuntimePseudoRelocSize(Rel.Type);
- if (SizeInBits == 0) {
- error("unable to automatically import from " + Target->getName() +
+ int sizeInBits = getRuntimePseudoRelocSize(rel.Type);
+ if (sizeInBits == 0) {
+ error("unable to automatically import from " + target->getName() +
" with relocation type " +
- File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " +
- toString(File));
+ file->getCOFFObj()->getRelocationTypeName(rel.Type) + " in " +
+ toString(file));
continue;
}
- // SizeInBits is used to initialize the Flags field; currently no
+ // sizeInBits is used to initialize the Flags field; currently no
// other flags are defined.
- Res.emplace_back(
- RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits));
+ res.emplace_back(
+ RuntimePseudoReloc(target, this, rel.VirtualAddress, sizeInBits));
}
}
-bool SectionChunk::hasData() const {
- return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
-}
-
-uint32_t SectionChunk::getOutputCharacteristics() const {
- return Header->Characteristics & (PermMask | TypeMask);
-}
-
bool SectionChunk::isCOMDAT() const {
- return Header->Characteristics & IMAGE_SCN_LNK_COMDAT;
+ return header->Characteristics & IMAGE_SCN_LNK_COMDAT;
}
void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
- if (Sym && this == Repl)
- message("Discarded " + Sym->getName());
+ if (sym && this == repl)
+ message("Discarded " + sym->getName());
}
-StringRef SectionChunk::getDebugName() {
- if (Sym)
- return Sym->getName();
+StringRef SectionChunk::getDebugName() const {
+ if (sym)
+ return sym->getName();
return "";
}
ArrayRef<uint8_t> SectionChunk::getContents() const {
- ArrayRef<uint8_t> A;
- File->getCOFFObj()->getSectionContents(Header, A);
- return A;
+ ArrayRef<uint8_t> a;
+ cantFail(file->getCOFFObj()->getSectionContents(header, a));
+ return a;
}
-void SectionChunk::replace(SectionChunk *Other) {
- Alignment = std::max(Alignment, Other->Alignment);
- Other->Repl = Repl;
- Other->Live = false;
+ArrayRef<uint8_t> SectionChunk::consumeDebugMagic() {
+ assert(isCodeView());
+ return consumeDebugMagic(getContents(), getSectionName());
+}
+
+ArrayRef<uint8_t> SectionChunk::consumeDebugMagic(ArrayRef<uint8_t> data,
+ StringRef sectionName) {
+ if (data.empty())
+ return {};
+
+ // First 4 bytes are section magic.
+ if (data.size() < 4)
+ fatal("the section is too short: " + sectionName);
+
+ if (!sectionName.startswith(".debug$"))
+ fatal("invalid section: " + sectionName);
+
+ uint32_t magic = support::endian::read32le(data.data());
+ uint32_t expectedMagic = sectionName == ".debug$H"
+ ? DEBUG_HASHES_SECTION_MAGIC
+ : DEBUG_SECTION_MAGIC;
+ if (magic != expectedMagic) {
+ warn("ignoring section " + sectionName + " with unrecognized magic 0x" +
+ utohexstr(magic));
+ return {};
+ }
+ return data.slice(4);
+}
+
+SectionChunk *SectionChunk::findByName(ArrayRef<SectionChunk *> sections,
+ StringRef name) {
+ for (SectionChunk *c : sections)
+ if (c->getSectionName() == name)
+ return c;
+ return nullptr;
+}
+
+void SectionChunk::replace(SectionChunk *other) {
+ p2Align = std::max(p2Align, other->p2Align);
+ other->repl = repl;
+ other->live = false;
}
uint32_t SectionChunk::getSectionNumber() const {
- DataRefImpl R;
- R.p = reinterpret_cast<uintptr_t>(Header);
- SectionRef S(R, File->getCOFFObj());
- return S.getIndex() + 1;
+ DataRefImpl r;
+ r.p = reinterpret_cast<uintptr_t>(header);
+ SectionRef s(r, file->getCOFFObj());
+ return s.getIndex() + 1;
}
-CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
- // Common symbols are aligned on natural boundaries up to 32 bytes.
+CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) {
+ // The value of a common symbol is its size. Align all common symbols smaller
+ // than 32 bytes naturally, i.e. round the size up to the next power of two.
// This is what MSVC link.exe does.
- Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
+ setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(sym.getValue()))));
+ hasData = false;
}
uint32_t CommonChunk::getOutputCharacteristics() const {
@@ -617,119 +654,139 @@ uint32_t CommonChunk::getOutputCharacteristics() const {
IMAGE_SCN_MEM_WRITE;
}
-void StringChunk::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, Str.data(), Str.size());
- Buf[OutputSectionOff + Str.size()] = '\0';
+void StringChunk::writeTo(uint8_t *buf) const {
+ memcpy(buf, str.data(), str.size());
+ buf[str.size()] = '\0';
}
-ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
+ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) {
// Intel Optimization Manual says that all branch targets
// should be 16-byte aligned. MSVC linker does this too.
- Alignment = 16;
+ setAlignment(16);
}
-void ImportThunkChunkX64::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
+void ImportThunkChunkX64::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkX86, sizeof(importThunkX86));
// The first two bytes is a JMP instruction. Fill its operand.
- write32le(Buf + OutputSectionOff + 2, ImpSymbol->getRVA() - RVA - getSize());
+ write32le(buf + 2, impSymbol->getRVA() - rva - getSize());
}
-void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *Res) {
- Res->emplace_back(getRVA() + 2);
+void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *res) {
+ res->emplace_back(getRVA() + 2);
}
-void ImportThunkChunkX86::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
+void ImportThunkChunkX86::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkX86, sizeof(importThunkX86));
// The first two bytes is a JMP instruction. Fill its operand.
- write32le(Buf + OutputSectionOff + 2,
- ImpSymbol->getRVA() + Config->ImageBase);
+ write32le(buf + 2,
+ impSymbol->getRVA() + config->imageBase);
}
-void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *Res) {
- Res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T);
+void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
+ res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T);
}
-void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, ImportThunkARM, sizeof(ImportThunkARM));
+void ImportThunkChunkARM::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkARM, sizeof(importThunkARM));
// Fix mov.w and mov.t operands.
- applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase);
+ applyMOV32T(buf, impSymbol->getRVA() + config->imageBase);
}
-void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
- int64_t Off = ImpSymbol->getRVA() & 0xfff;
- memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
- applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA, 12);
- applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
+void ImportThunkChunkARM64::writeTo(uint8_t *buf) const {
+ int64_t off = impSymbol->getRVA() & 0xfff;
+ memcpy(buf, importThunkARM64, sizeof(importThunkARM64));
+ applyArm64Addr(buf, impSymbol->getRVA(), rva, 12);
+ applyArm64Ldr(buf + 4, off);
}
// A Thumb2, PIC, non-interworking range extension thunk.
-const uint8_t ArmThunk[] = {
+const uint8_t armThunk[] = {
0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
0xe7, 0x44, // L1: add pc, ip
};
-size_t RangeExtensionThunk::getSize() const {
- assert(Config->Machine == ARMNT);
- return sizeof(ArmThunk);
+size_t RangeExtensionThunkARM::getSize() const {
+ assert(config->machine == ARMNT);
+ return sizeof(armThunk);
+}
+
+void RangeExtensionThunkARM::writeTo(uint8_t *buf) const {
+ assert(config->machine == ARMNT);
+ uint64_t offset = target->getRVA() - rva - 12;
+ memcpy(buf, armThunk, sizeof(armThunk));
+ applyMOV32T(buf, uint32_t(offset));
+}
+
+// A position independent ARM64 adrp+add thunk, with a maximum range of
+// +/- 4 GB, which is enough for any PE-COFF.
+const uint8_t arm64Thunk[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+};
+
+size_t RangeExtensionThunkARM64::getSize() const {
+ assert(config->machine == ARM64);
+ return sizeof(arm64Thunk);
}
-void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
- assert(Config->Machine == ARMNT);
- uint64_t Offset = Target->getRVA() - RVA - 12;
- memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
- applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
+void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
+ assert(config->machine == ARM64);
+ memcpy(buf, arm64Thunk, sizeof(arm64Thunk));
+ applyArm64Addr(buf + 0, target->getRVA(), rva, 12);
+ applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);
}
-void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
- Res->emplace_back(getRVA());
+void LocalImportChunk::getBaserels(std::vector<Baserel> *res) {
+ res->emplace_back(getRVA());
}
-size_t LocalImportChunk::getSize() const { return Config->Wordsize; }
+size_t LocalImportChunk::getSize() const { return config->wordsize; }
-void LocalImportChunk::writeTo(uint8_t *Buf) const {
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
+void LocalImportChunk::writeTo(uint8_t *buf) const {
+ if (config->is64()) {
+ write64le(buf, sym->getRVA() + config->imageBase);
} else {
- write32le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
+ write32le(buf, sym->getRVA() + config->imageBase);
}
}
-void RVATableChunk::writeTo(uint8_t *Buf) const {
- ulittle32_t *Begin = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
- size_t Cnt = 0;
- for (const ChunkAndOffset &CO : Syms)
- Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset;
- std::sort(Begin, Begin + Cnt);
- assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt &&
+void RVATableChunk::writeTo(uint8_t *buf) const {
+ ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(buf);
+ size_t cnt = 0;
+ for (const ChunkAndOffset &co : syms)
+ begin[cnt++] = co.inputChunk->getRVA() + co.offset;
+ std::sort(begin, begin + cnt);
+ assert(std::unique(begin, begin + cnt) == begin + cnt &&
"RVA tables should be de-duplicated");
}
// MinGW specific, for the "automatic import of variables from DLLs" feature.
size_t PseudoRelocTableChunk::getSize() const {
- if (Relocs.empty())
+ if (relocs.empty())
return 0;
- return 12 + 12 * Relocs.size();
+ return 12 + 12 * relocs.size();
}
// MinGW specific.
-void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const {
- if (Relocs.empty())
+void PseudoRelocTableChunk::writeTo(uint8_t *buf) const {
+ if (relocs.empty())
return;
- ulittle32_t *Table = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
+ ulittle32_t *table = reinterpret_cast<ulittle32_t *>(buf);
// This is the list header, to signal the runtime pseudo relocation v2
// format.
- Table[0] = 0;
- Table[1] = 0;
- Table[2] = 1;
-
- size_t Idx = 3;
- for (const RuntimePseudoReloc &RPR : Relocs) {
- Table[Idx + 0] = RPR.Sym->getRVA();
- Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset;
- Table[Idx + 2] = RPR.Flags;
- Idx += 3;
+ table[0] = 0;
+ table[1] = 0;
+ table[2] = 1;
+
+ size_t idx = 3;
+ for (const RuntimePseudoReloc &rpr : relocs) {
+ table[idx + 0] = rpr.sym->getRVA();
+ table[idx + 1] = rpr.target->getRVA() + rpr.targetOffset;
+ table[idx + 2] = rpr.flags;
+ idx += 3;
}
}
@@ -772,26 +829,26 @@ void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const {
//
// Usually we have a lot of relocations for each page, so the number of
// bytes for one .reloc entry is close to 2 bytes on average.
-BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) {
+BaserelChunk::BaserelChunk(uint32_t page, Baserel *begin, Baserel *end) {
// Block header consists of 4 byte page RVA and 4 byte block size.
// Each entry is 2 byte. Last entry may be padding.
- Data.resize(alignTo((End - Begin) * 2 + 8, 4));
- uint8_t *P = Data.data();
- write32le(P, Page);
- write32le(P + 4, Data.size());
- P += 8;
- for (Baserel *I = Begin; I != End; ++I) {
- write16le(P, (I->Type << 12) | (I->RVA - Page));
- P += 2;
+ data.resize(alignTo((end - begin) * 2 + 8, 4));
+ uint8_t *p = data.data();
+ write32le(p, page);
+ write32le(p + 4, data.size());
+ p += 8;
+ for (Baserel *i = begin; i != end; ++i) {
+ write16le(p, (i->type << 12) | (i->rva - page));
+ p += 2;
}
}
-void BaserelChunk::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, Data.data(), Data.size());
+void BaserelChunk::writeTo(uint8_t *buf) const {
+ memcpy(buf, data.data(), data.size());
}
uint8_t Baserel::getDefaultType() {
- switch (Config->Machine) {
+ switch (config->machine) {
case AMD64:
case ARM64:
return IMAGE_REL_BASED_DIR64;
@@ -803,36 +860,38 @@ uint8_t Baserel::getDefaultType() {
}
}
-std::map<uint32_t, MergeChunk *> MergeChunk::Instances;
+MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {};
-MergeChunk::MergeChunk(uint32_t Alignment)
- : Builder(StringTableBuilder::RAW, Alignment) {
- this->Alignment = Alignment;
+MergeChunk::MergeChunk(uint32_t alignment)
+ : builder(StringTableBuilder::RAW, alignment) {
+ setAlignment(alignment);
}
-void MergeChunk::addSection(SectionChunk *C) {
- auto *&MC = Instances[C->Alignment];
- if (!MC)
- MC = make<MergeChunk>(C->Alignment);
- MC->Sections.push_back(C);
+void MergeChunk::addSection(SectionChunk *c) {
+ assert(isPowerOf2_32(c->getAlignment()));
+ uint8_t p2Align = llvm::Log2_32(c->getAlignment());
+ assert(p2Align < array_lengthof(instances));
+ auto *&mc = instances[p2Align];
+ if (!mc)
+ mc = make<MergeChunk>(c->getAlignment());
+ mc->sections.push_back(c);
}
void MergeChunk::finalizeContents() {
- if (!Finalized) {
- for (SectionChunk *C : Sections)
- if (C->Live)
- Builder.add(toStringRef(C->getContents()));
- Builder.finalize();
- Finalized = true;
- }
+ assert(!finalized && "should only finalize once");
+ for (SectionChunk *c : sections)
+ if (c->live)
+ builder.add(toStringRef(c->getContents()));
+ builder.finalize();
+ finalized = true;
+}
- for (SectionChunk *C : Sections) {
- if (!C->Live)
+void MergeChunk::assignSubsectionRVAs() {
+ for (SectionChunk *c : sections) {
+ if (!c->live)
continue;
- size_t Off = Builder.getOffset(toStringRef(C->getContents()));
- C->setOutputSection(Out);
- C->setRVA(RVA + Off);
- C->OutputSectionOff = OutputSectionOff + Off;
+ size_t off = builder.getOffset(toStringRef(c->getContents()));
+ c->setRVA(rva + off);
}
}
@@ -841,21 +900,21 @@ uint32_t MergeChunk::getOutputCharacteristics() const {
}
size_t MergeChunk::getSize() const {
- return Builder.getSize();
+ return builder.getSize();
}
-void MergeChunk::writeTo(uint8_t *Buf) const {
- Builder.write(Buf + OutputSectionOff);
+void MergeChunk::writeTo(uint8_t *buf) const {
+ builder.write(buf);
}
// MinGW specific.
-size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; }
+size_t AbsolutePointerChunk::getSize() const { return config->wordsize; }
-void AbsolutePointerChunk::writeTo(uint8_t *Buf) const {
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, Value);
+void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
+ if (config->is64()) {
+ write64le(buf, value);
} else {
- write32le(Buf + OutputSectionOff, Value);
+ write32le(buf, value);
}
}
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index f8a0ddd8ef3b..6bb629fe998b 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -1,9 +1,8 @@
//===- Chunks.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -14,6 +13,7 @@
#include "InputFiles.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/MC/StringTableBuilder.h"
@@ -40,10 +40,13 @@ class RuntimePseudoReloc;
class Symbol;
// Mask for permissions (discardable, writable, readable, executable, etc).
-const uint32_t PermMask = 0xFE000000;
+const uint32_t permMask = 0xFE000000;
// Mask for section types (code, data, bss).
-const uint32_t TypeMask = 0x000000E0;
+const uint32_t typeMask = 0x000000E0;
+
+// The log base 2 of the largest section alignment, which is log2(8192), or 13.
+enum : unsigned { Log2MaxSectionAlignment = 13 };
// A Chunk represents a chunk of data that will occupy space in the
// output (if the resolver chose that). It may or may not be backed by
@@ -51,81 +54,126 @@ const uint32_t TypeMask = 0x000000E0;
// doesn't even have actual data (if common or bss).
class Chunk {
public:
- enum Kind { SectionKind, OtherKind };
- Kind kind() const { return ChunkKind; }
- virtual ~Chunk() = default;
+ enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind };
+ Kind kind() const { return chunkKind; }
// Returns the size of this chunk (even if this is a common or BSS.)
- virtual size_t getSize() const = 0;
+ size_t getSize() const;
+
+ // Returns chunk alignment in power of two form. Value values are powers of
+ // two from 1 to 8192.
+ uint32_t getAlignment() const { return 1U << p2Align; }
+
+ // Update the chunk section alignment measured in bytes. Internally alignment
+ // is stored in log2.
+ void setAlignment(uint32_t align) {
+ // Treat zero byte alignment as 1 byte alignment.
+ align = align ? align : 1;
+ assert(llvm::isPowerOf2_32(align) && "alignment is not a power of 2");
+ p2Align = llvm::Log2_32(align);
+ assert(p2Align <= Log2MaxSectionAlignment &&
+ "impossible requested alignment");
+ }
// Write this chunk to a mmap'ed file, assuming Buf is pointing to
// beginning of the file. Because this function may use RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
- virtual void writeTo(uint8_t *Buf) const {}
+ void writeTo(uint8_t *buf) const;
+
+ // The writer sets and uses the addresses. In practice, PE images cannot be
+ // larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs
+ // can be stored with 32 bits.
+ uint32_t getRVA() const { return rva; }
+ void setRVA(uint64_t v) {
+ rva = (uint32_t)v;
+ assert(rva == v && "RVA truncated");
+ }
- // Called by the writer once before assigning addresses and writing
- // the output.
- virtual void readRelocTargets() {}
+ // Returns readable/writable/executable bits.
+ uint32_t getOutputCharacteristics() const;
- // Called if restarting thunk addition.
- virtual void resetRelocTargets() {}
+ // Returns the section name if this is a section chunk.
+ // It is illegal to call this function on non-section chunks.
+ StringRef getSectionName() const;
- // Called by the writer after an RVA is assigned, but before calling
- // getSize().
- virtual void finalizeContents() {}
+ // An output section has pointers to chunks in the section, and each
+ // chunk has a back pointer to an output section.
+ void setOutputSectionIdx(uint16_t o) { osidx = o; }
+ uint16_t getOutputSectionIdx() const { return osidx; }
+ OutputSection *getOutputSection() const;
- // The writer sets and uses the addresses.
- uint64_t getRVA() const { return RVA; }
- void setRVA(uint64_t V) { RVA = V; }
+ // Windows-specific.
+ // Collect all locations that contain absolute addresses for base relocations.
+ void getBaserels(std::vector<Baserel> *res);
+ // Returns a human-readable name of this chunk. Chunks are unnamed chunks of
+ // bytes, so this is used only for logging or debugging.
+ StringRef getDebugName() const;
+
+ // Return true if this file has the hotpatch flag set to true in the
+ // S_COMPILE3 record in codeview debug info. Also returns true for some thunks
+ // synthesized by the linker.
+ bool isHotPatchable() const;
+
+protected:
+ Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {}
+
+ const Kind chunkKind;
+
+public:
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
- // will be filled with zeros.
- virtual bool hasData() const { return true; }
+ // will be filled with zeros. Corresponds to the
+ // IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit.
+ uint8_t hasData : 1;
+
+public:
+ // The alignment of this chunk, stored in log2 form. The writer uses the
+ // value.
+ uint8_t p2Align : 7;
+
+ // The output section index for this chunk. The first valid section number is
+ // one.
+ uint16_t osidx = 0;
+
+ // The RVA of this chunk in the output. The writer sets a value.
+ uint32_t rva = 0;
+};
+
+class NonSectionChunk : public Chunk {
+public:
+ virtual ~NonSectionChunk() = default;
+
+ // Returns the size of this chunk (even if this is a common or BSS.)
+ virtual size_t getSize() const = 0;
- // Returns readable/writable/executable bits.
virtual uint32_t getOutputCharacteristics() const { return 0; }
+ // Write this chunk to a mmap'ed file, assuming Buf is pointing to
+ // beginning of the file. Because this function may use RVA values
+ // of other chunks for relocations, you need to set them properly
+ // before calling this function.
+ virtual void writeTo(uint8_t *buf) const {}
+
// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
virtual StringRef getSectionName() const {
llvm_unreachable("unimplemented getSectionName");
}
- // An output section has pointers to chunks in the section, and each
- // chunk has a back pointer to an output section.
- void setOutputSection(OutputSection *O) { Out = O; }
- OutputSection *getOutputSection() const { return Out; }
-
// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
- virtual void getBaserels(std::vector<Baserel> *Res) {}
+ virtual void getBaserels(std::vector<Baserel> *res) {}
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
- virtual StringRef getDebugName() { return ""; }
+ virtual StringRef getDebugName() const { return ""; }
- // The alignment of this chunk. The writer uses the value.
- uint32_t Alignment = 1;
+ static bool classof(const Chunk *c) { return c->kind() != SectionKind; }
protected:
- Chunk(Kind K = OtherKind) : ChunkKind(K) {}
- const Kind ChunkKind;
-
- // The RVA of this chunk in the output. The writer sets a value.
- uint64_t RVA = 0;
-
- // The output section for this chunk.
- OutputSection *Out = nullptr;
-
-public:
- // The offset from beginning of the output section. The writer sets a value.
- uint64_t OutputSectionOff = 0;
-
- // Whether this section needs to be kept distinct from other sections during
- // ICF. This is set by the driver using address-significance tables.
- bool KeepUnique = false;
+ NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}
};
// A chunk corresponding a section of an input file.
@@ -139,39 +187,41 @@ public:
std::random_access_iterator_tag, Symbol *> {
friend SectionChunk;
- ObjFile *File;
+ ObjFile *file;
- symbol_iterator(ObjFile *File, const coff_relocation *I)
- : symbol_iterator::iterator_adaptor_base(I), File(File) {}
+ symbol_iterator(ObjFile *file, const coff_relocation *i)
+ : symbol_iterator::iterator_adaptor_base(i), file(file) {}
public:
symbol_iterator() = default;
- Symbol *operator*() const { return File->getSymbol(I->SymbolTableIndex); }
+ Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); }
};
- SectionChunk(ObjFile *File, const coff_section *Header);
- static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
- void readRelocTargets() override;
- void resetRelocTargets() override;
- size_t getSize() const override { return Header->SizeOfRawData; }
+ SectionChunk(ObjFile *file, const coff_section *header);
+ static bool classof(const Chunk *c) { return c->kind() == SectionKind; }
+ size_t getSize() const { return header->SizeOfRawData; }
ArrayRef<uint8_t> getContents() const;
- void writeTo(uint8_t *Buf) const override;
- bool hasData() const override;
- uint32_t getOutputCharacteristics() const override;
- StringRef getSectionName() const override { return SectionName; }
- void getBaserels(std::vector<Baserel> *Res) override;
+ void writeTo(uint8_t *buf) const;
+
+ uint32_t getOutputCharacteristics() const {
+ return header->Characteristics & (permMask | typeMask);
+ }
+ StringRef getSectionName() const {
+ return StringRef(sectionNameData, sectionNameSize);
+ }
+ void getBaserels(std::vector<Baserel> *res);
bool isCOMDAT() const;
- void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
- void applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
- void applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
- void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
+ void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
+ void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
+ void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
+ void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
- void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &Res);
+ void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
// Called if the garbage collector decides to not include this chunk
// in a final output. It's supposed to print out a log message to stdout.
@@ -179,69 +229,169 @@ public:
// Adds COMDAT associative sections to this COMDAT section. A chunk
// and its children are treated as a group by the garbage collector.
- void addAssociative(SectionChunk *Child);
+ void addAssociative(SectionChunk *child);
- StringRef getDebugName() override;
+ StringRef getDebugName() const;
// True if this is a codeview debug info chunk. These will not be laid out in
// the image. Instead they will end up in the PDB, if one is requested.
bool isCodeView() const {
- return SectionName == ".debug" || SectionName.startswith(".debug$");
+ return getSectionName() == ".debug" || getSectionName().startswith(".debug$");
}
// True if this is a DWARF debug info or exception handling chunk.
bool isDWARF() const {
- return SectionName.startswith(".debug_") || SectionName == ".eh_frame";
+ return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame";
}
// Allow iteration over the bodies of this chunk's relocated symbols.
llvm::iterator_range<symbol_iterator> symbols() const {
- return llvm::make_range(symbol_iterator(File, Relocs.begin()),
- symbol_iterator(File, Relocs.end()));
+ return llvm::make_range(symbol_iterator(file, relocsData),
+ symbol_iterator(file, relocsData + relocsSize));
+ }
+
+ ArrayRef<coff_relocation> getRelocs() const {
+ return llvm::makeArrayRef(relocsData, relocsSize);
}
+ // Reloc setter used by ARM range extension thunk insertion.
+ void setRelocs(ArrayRef<coff_relocation> newRelocs) {
+ relocsData = newRelocs.data();
+ relocsSize = newRelocs.size();
+ assert(relocsSize == newRelocs.size() && "reloc size truncation");
+ }
+
+ // Single linked list iterator for associated comdat children.
+ class AssociatedIterator
+ : public llvm::iterator_facade_base<
+ AssociatedIterator, std::forward_iterator_tag, SectionChunk> {
+ public:
+ AssociatedIterator() = default;
+ AssociatedIterator(SectionChunk *head) : cur(head) {}
+ AssociatedIterator &operator=(const AssociatedIterator &r) {
+ cur = r.cur;
+ return *this;
+ }
+ bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }
+ const SectionChunk &operator*() const { return *cur; }
+ SectionChunk &operator*() { return *cur; }
+ AssociatedIterator &operator++() {
+ cur = cur->assocChildren;
+ return *this;
+ }
+
+ private:
+ SectionChunk *cur = nullptr;
+ };
+
// Allow iteration over the associated child chunks for this section.
- ArrayRef<SectionChunk *> children() const { return AssocChildren; }
+ llvm::iterator_range<AssociatedIterator> children() const {
+ return llvm::make_range(AssociatedIterator(assocChildren),
+ AssociatedIterator(nullptr));
+ }
// The section ID this chunk belongs to in its Obj.
uint32_t getSectionNumber() const;
- // A pointer pointing to a replacement for this chunk.
- // Initially it points to "this" object. If this chunk is merged
- // with other chunk by ICF, it points to another chunk,
- // and this chunk is considered as dead.
- SectionChunk *Repl;
+ ArrayRef<uint8_t> consumeDebugMagic();
- // The CRC of the contents as described in the COFF spec 4.5.5.
- // Auxiliary Format 5: Section Definitions. Used for ICF.
- uint32_t Checksum = 0;
+ static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> data,
+ StringRef sectionName);
- const coff_section *Header;
+ static SectionChunk *findByName(ArrayRef<SectionChunk *> sections,
+ StringRef name);
// The file that this chunk was created from.
- ObjFile *File;
+ ObjFile *file;
+
+ // Pointer to the COFF section header in the input file.
+ const coff_section *header;
// The COMDAT leader symbol if this is a COMDAT chunk.
- DefinedRegular *Sym = nullptr;
+ DefinedRegular *sym = nullptr;
- ArrayRef<coff_relocation> Relocs;
+ // The CRC of the contents as described in the COFF spec 4.5.5.
+ // Auxiliary Format 5: Section Definitions. Used for ICF.
+ uint32_t checksum = 0;
// Used by the garbage collector.
- bool Live;
+ bool live;
+
+ // Whether this section needs to be kept distinct from other sections during
+ // ICF. This is set by the driver using address-significance tables.
+ bool keepUnique = false;
- // When inserting a thunk, we need to adjust a relocation to point to
- // the thunk instead of the actual original target Symbol.
- std::vector<Symbol *> RelocTargets;
+ // The COMDAT selection if this is a COMDAT chunk.
+ llvm::COFF::COMDATType selection = (llvm::COFF::COMDATType)0;
+
+ // A pointer pointing to a replacement for this chunk.
+ // Initially it points to "this" object. If this chunk is merged
+ // with other chunk by ICF, it points to another chunk,
+ // and this chunk is considered as dead.
+ SectionChunk *repl;
private:
- StringRef SectionName;
- std::vector<SectionChunk *> AssocChildren;
+ SectionChunk *assocChildren = nullptr;
// Used for ICF (Identical COMDAT Folding)
- void replace(SectionChunk *Other);
- uint32_t Class[2] = {0, 0};
+ void replace(SectionChunk *other);
+ uint32_t eqClass[2] = {0, 0};
+
+ // Relocations for this section. Size is stored below.
+ const coff_relocation *relocsData;
+
+ // Section name string. Size is stored below.
+ const char *sectionNameData;
+
+ uint32_t relocsSize = 0;
+ uint32_t sectionNameSize = 0;
};
+// Inline methods to implement faux-virtual dispatch for SectionChunk.
+
+inline size_t Chunk::getSize() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getSize();
+ else
+ return static_cast<const NonSectionChunk *>(this)->getSize();
+}
+
+inline uint32_t Chunk::getOutputCharacteristics() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getOutputCharacteristics();
+ else
+ return static_cast<const NonSectionChunk *>(this)
+ ->getOutputCharacteristics();
+}
+
+inline void Chunk::writeTo(uint8_t *buf) const {
+ if (isa<SectionChunk>(this))
+ static_cast<const SectionChunk *>(this)->writeTo(buf);
+ else
+ static_cast<const NonSectionChunk *>(this)->writeTo(buf);
+}
+
+inline StringRef Chunk::getSectionName() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getSectionName();
+ else
+ return static_cast<const NonSectionChunk *>(this)->getSectionName();
+}
+
+inline void Chunk::getBaserels(std::vector<Baserel> *res) {
+ if (isa<SectionChunk>(this))
+ static_cast<SectionChunk *>(this)->getBaserels(res);
+ else
+ static_cast<NonSectionChunk *>(this)->getBaserels(res);
+}
+
+inline StringRef Chunk::getDebugName() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getDebugName();
+ else
+ return static_cast<const NonSectionChunk *>(this)->getDebugName();
+}
+
// This class is used to implement an lld-specific feature (not implemented in
// MSVC) that minimizes the output size by finding string literals sharing tail
// parts and merging them.
@@ -251,60 +401,60 @@ private:
// The MergeChunk then tail merges the strings using the StringTableBuilder
// class and assigns RVAs and section offsets to each of the member chunks based
// on the offsets assigned by the StringTableBuilder.
-class MergeChunk : public Chunk {
+class MergeChunk : public NonSectionChunk {
public:
- MergeChunk(uint32_t Alignment);
- static void addSection(SectionChunk *C);
- void finalizeContents() override;
+ MergeChunk(uint32_t alignment);
+ static void addSection(SectionChunk *c);
+ void finalizeContents();
+ void assignSubsectionRVAs();
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".rdata"; }
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
- static std::map<uint32_t, MergeChunk *> Instances;
- std::vector<SectionChunk *> Sections;
+ static MergeChunk *instances[Log2MaxSectionAlignment + 1];
+ std::vector<SectionChunk *> sections;
private:
- llvm::StringTableBuilder Builder;
- bool Finalized = false;
+ llvm::StringTableBuilder builder;
+ bool finalized = false;
};
// A chunk for common symbols. Common chunks don't have actual data.
-class CommonChunk : public Chunk {
+class CommonChunk : public NonSectionChunk {
public:
- CommonChunk(const COFFSymbolRef Sym);
- size_t getSize() const override { return Sym.getValue(); }
- bool hasData() const override { return false; }
+ CommonChunk(const COFFSymbolRef sym);
+ size_t getSize() const override { return sym.getValue(); }
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".bss"; }
private:
- const COFFSymbolRef Sym;
+ const COFFSymbolRef sym;
};
// A chunk for linker-created strings.
-class StringChunk : public Chunk {
+class StringChunk : public NonSectionChunk {
public:
- explicit StringChunk(StringRef S) : Str(S) {}
- size_t getSize() const override { return Str.size() + 1; }
- void writeTo(uint8_t *Buf) const override;
+ explicit StringChunk(StringRef s) : str(s) {}
+ size_t getSize() const override { return str.size() + 1; }
+ void writeTo(uint8_t *buf) const override;
private:
- StringRef Str;
+ StringRef str;
};
-static const uint8_t ImportThunkX86[] = {
+static const uint8_t importThunkX86[] = {
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0
};
-static const uint8_t ImportThunkARM[] = {
+static const uint8_t importThunkARM[] = {
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0
0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip]
};
-static const uint8_t ImportThunkARM64[] = {
+static const uint8_t importThunkARM64[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, #0
0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16]
0x00, 0x02, 0x1f, 0xd6, // br x16
@@ -313,78 +463,85 @@ static const uint8_t ImportThunkARM64[] = {
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, its
// contents will be a JMP instruction to some __imp_ symbol.
-class ImportThunkChunkX64 : public Chunk {
+class ImportThunkChunk : public NonSectionChunk {
public:
- explicit ImportThunkChunkX64(Defined *S);
- size_t getSize() const override { return sizeof(ImportThunkX86); }
- void writeTo(uint8_t *Buf) const override;
+ ImportThunkChunk(Defined *s)
+ : NonSectionChunk(ImportThunkKind), impSymbol(s) {}
+ static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
-private:
- Defined *ImpSymbol;
+protected:
+ Defined *impSymbol;
};
-class ImportThunkChunkX86 : public Chunk {
+class ImportThunkChunkX64 : public ImportThunkChunk {
public:
- explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {}
- size_t getSize() const override { return sizeof(ImportThunkX86); }
- void getBaserels(std::vector<Baserel> *Res) override;
- void writeTo(uint8_t *Buf) const override;
+ explicit ImportThunkChunkX64(Defined *s);
+ size_t getSize() const override { return sizeof(importThunkX86); }
+ void writeTo(uint8_t *buf) const override;
+};
-private:
- Defined *ImpSymbol;
+class ImportThunkChunkX86 : public ImportThunkChunk {
+public:
+ explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {}
+ size_t getSize() const override { return sizeof(importThunkX86); }
+ void getBaserels(std::vector<Baserel> *res) override;
+ void writeTo(uint8_t *buf) const override;
};
-class ImportThunkChunkARM : public Chunk {
+class ImportThunkChunkARM : public ImportThunkChunk {
public:
- explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {}
- size_t getSize() const override { return sizeof(ImportThunkARM); }
- void getBaserels(std::vector<Baserel> *Res) override;
- void writeTo(uint8_t *Buf) const override;
+ explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {}
+ size_t getSize() const override { return sizeof(importThunkARM); }
+ void getBaserels(std::vector<Baserel> *res) override;
+ void writeTo(uint8_t *buf) const override;
+};
-private:
- Defined *ImpSymbol;
+class ImportThunkChunkARM64 : public ImportThunkChunk {
+public:
+ explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {}
+ size_t getSize() const override { return sizeof(importThunkARM64); }
+ void writeTo(uint8_t *buf) const override;
};
-class ImportThunkChunkARM64 : public Chunk {
+class RangeExtensionThunkARM : public NonSectionChunk {
public:
- explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {}
- size_t getSize() const override { return sizeof(ImportThunkARM64); }
- void writeTo(uint8_t *Buf) const override;
+ explicit RangeExtensionThunkARM(Defined *t) : target(t) {}
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) const override;
-private:
- Defined *ImpSymbol;
+ Defined *target;
};
-class RangeExtensionThunk : public Chunk {
+class RangeExtensionThunkARM64 : public NonSectionChunk {
public:
- explicit RangeExtensionThunk(Defined *T) : Target(T) {}
+ explicit RangeExtensionThunkARM64(Defined *t) : target(t) {}
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
- Defined *Target;
+ Defined *target;
};
// Windows-specific.
// See comments for DefinedLocalImport class.
-class LocalImportChunk : public Chunk {
+class LocalImportChunk : public NonSectionChunk {
public:
- explicit LocalImportChunk(Defined *S) : Sym(S) {
- Alignment = Config->Wordsize;
+ explicit LocalImportChunk(Defined *s) : sym(s) {
+ setAlignment(config->wordsize);
}
size_t getSize() const override;
- void getBaserels(std::vector<Baserel> *Res) override;
- void writeTo(uint8_t *Buf) const override;
+ void getBaserels(std::vector<Baserel> *res) override;
+ void writeTo(uint8_t *buf) const override;
private:
- Defined *Sym;
+ Defined *sym;
};
// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
// offset into the chunk. Order does not matter as the RVA table will be sorted
// later.
struct ChunkAndOffset {
- Chunk *InputChunk;
- uint32_t Offset;
+ Chunk *inputChunk;
+ uint32_t offset;
struct DenseMapInfo {
static ChunkAndOffset getEmptyKey() {
@@ -393,12 +550,12 @@ struct ChunkAndOffset {
static ChunkAndOffset getTombstoneKey() {
return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0};
}
- static unsigned getHashValue(const ChunkAndOffset &CO) {
+ static unsigned getHashValue(const ChunkAndOffset &co) {
return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue(
- {CO.InputChunk, CO.Offset});
+ {co.inputChunk, co.offset});
}
- static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) {
- return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset;
+ static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) {
+ return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset;
}
};
};
@@ -406,48 +563,48 @@ struct ChunkAndOffset {
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;
// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
-class RVATableChunk : public Chunk {
+class RVATableChunk : public NonSectionChunk {
public:
- explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {}
- size_t getSize() const override { return Syms.size() * 4; }
- void writeTo(uint8_t *Buf) const override;
+ explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {}
+ size_t getSize() const override { return syms.size() * 4; }
+ void writeTo(uint8_t *buf) const override;
private:
- SymbolRVASet Syms;
+ SymbolRVASet syms;
};
// Windows-specific.
// This class represents a block in .reloc section.
// See the PE/COFF spec 5.6 for details.
-class BaserelChunk : public Chunk {
+class BaserelChunk : public NonSectionChunk {
public:
- BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End);
- size_t getSize() const override { return Data.size(); }
- void writeTo(uint8_t *Buf) const override;
+ BaserelChunk(uint32_t page, Baserel *begin, Baserel *end);
+ size_t getSize() const override { return data.size(); }
+ void writeTo(uint8_t *buf) const override;
private:
- std::vector<uint8_t> Data;
+ std::vector<uint8_t> data;
};
class Baserel {
public:
- Baserel(uint32_t V, uint8_t Ty) : RVA(V), Type(Ty) {}
- explicit Baserel(uint32_t V) : Baserel(V, getDefaultType()) {}
+ Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {}
+ explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {}
uint8_t getDefaultType();
- uint32_t RVA;
- uint8_t Type;
+ uint32_t rva;
+ uint8_t type;
};
// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a
// specific place in a section, without any data. This is used for the MinGW
// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
// of an empty chunk isn't MinGW specific.
-class EmptyChunk : public Chunk {
+class EmptyChunk : public NonSectionChunk {
public:
EmptyChunk() {}
size_t getSize() const override { return 0; }
- void writeTo(uint8_t *Buf) const override {}
+ void writeTo(uint8_t *buf) const override {}
};
// MinGW specific, for the "automatic import of variables from DLLs" feature.
@@ -456,17 +613,17 @@ public:
// the reference didn't use the dllimport attribute. The MinGW runtime will
// process this table after loading, before handling control over to user
// code.
-class PseudoRelocTableChunk : public Chunk {
+class PseudoRelocTableChunk : public NonSectionChunk {
public:
- PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs)
- : Relocs(std::move(Relocs)) {
- Alignment = 4;
+ PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &relocs)
+ : relocs(std::move(relocs)) {
+ setAlignment(4);
}
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
private:
- std::vector<RuntimePseudoReloc> Relocs;
+ std::vector<RuntimePseudoReloc> relocs;
};
// MinGW specific; information about one individual location in the image
@@ -474,37 +631,48 @@ private:
// one individual element in the PseudoRelocTableChunk table.
class RuntimePseudoReloc {
public:
- RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset,
- int Flags)
- : Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {}
+ RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset,
+ int flags)
+ : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {}
- Defined *Sym;
- SectionChunk *Target;
- uint32_t TargetOffset;
+ Defined *sym;
+ SectionChunk *target;
+ uint32_t targetOffset;
// The Flags field contains the size of the relocation, in bits. No other
// flags are currently defined.
- int Flags;
+ int flags;
};
// MinGW specific. A Chunk that contains one pointer-sized absolute value.
-class AbsolutePointerChunk : public Chunk {
+class AbsolutePointerChunk : public NonSectionChunk {
public:
- AbsolutePointerChunk(uint64_t Value) : Value(Value) {
- Alignment = getSize();
+ AbsolutePointerChunk(uint64_t value) : value(value) {
+ setAlignment(getSize());
}
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
private:
- uint64_t Value;
+ uint64_t value;
};
-void applyMOV32T(uint8_t *Off, uint32_t V);
-void applyBranch24T(uint8_t *Off, int32_t V);
+// Return true if this file has the hotpatch flag set to true in the S_COMPILE3
+// record in codeview debug info. Also returns true for some thunks synthesized
+// by the linker.
+inline bool Chunk::isHotPatchable() const {
+ if (auto *sc = dyn_cast<SectionChunk>(this))
+ return sc->file->hotPatchable;
+ else if (isa<ImportThunkChunk>(this))
+ return true;
+ return false;
+}
+
+void applyMOV32T(uint8_t *off, uint32_t v);
+void applyBranch24T(uint8_t *off, int32_t v);
-void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift);
-void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit);
-void applyArm64Branch26(uint8_t *Off, int64_t V);
+void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift);
+void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit);
+void applyArm64Branch26(uint8_t *off, int64_t v);
} // namespace coff
} // namespace lld
diff --git a/COFF/Config.h b/COFF/Config.h
index 8915b6a3bdd8..1b0e24042710 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -1,9 +1,8 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -29,6 +28,7 @@ class DefinedAbsolute;
class DefinedRelative;
class StringChunk;
class Symbol;
+class InputFile;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
@@ -38,30 +38,30 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
// Represents an /export option.
struct Export {
- StringRef Name; // N in /export:N or /export:E=N
- StringRef ExtName; // E in /export:E=N
- Symbol *Sym = nullptr;
- uint16_t Ordinal = 0;
- bool Noname = false;
- bool Data = false;
- bool Private = false;
- bool Constant = false;
+ StringRef name; // N in /export:N or /export:E=N
+ StringRef extName; // E in /export:E=N
+ Symbol *sym = nullptr;
+ uint16_t ordinal = 0;
+ bool noname = false;
+ bool data = false;
+ bool isPrivate = false;
+ bool constant = false;
// If an export is a form of /export:foo=dllname.bar, that means
// that foo should be exported as an alias to bar in the DLL.
- // ForwardTo is set to "dllname.bar" part. Usually empty.
- StringRef ForwardTo;
- StringChunk *ForwardChunk = nullptr;
+ // forwardTo is set to "dllname.bar" part. Usually empty.
+ StringRef forwardTo;
+ StringChunk *forwardChunk = nullptr;
// True if this /export option was in .drectves section.
- bool Directives = false;
- StringRef SymbolName;
- StringRef ExportName; // Name in DLL
-
- bool operator==(const Export &E) {
- return (Name == E.Name && ExtName == E.ExtName &&
- Ordinal == E.Ordinal && Noname == E.Noname &&
- Data == E.Data && Private == E.Private);
+ bool directives = false;
+ StringRef symbolName;
+ StringRef exportName; // Name in DLL
+
+ bool operator==(const Export &e) {
+ return (name == e.name && extName == e.extName &&
+ ordinal == e.ordinal && noname == e.noname &&
+ data == e.data && isPrivate == e.isPrivate);
}
};
@@ -81,130 +81,149 @@ enum class GuardCFLevel {
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
- bool is64() { return Machine == AMD64 || Machine == ARM64; }
-
- llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
- size_t Wordsize;
- bool Verbose = false;
- WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
- Symbol *Entry = nullptr;
- bool NoEntry = false;
- std::string OutputFile;
- std::string ImportName;
- bool DoGC = true;
- bool DoICF = true;
- bool TailMerge;
- bool Relocatable = true;
- bool ForceMultiple = false;
- bool ForceUnresolved = false;
- bool Debug = false;
- bool DebugDwarf = false;
- bool DebugGHashes = false;
- bool DebugSymtab = false;
- bool ShowTiming = false;
- unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
- std::vector<std::string> NatvisFiles;
- llvm::SmallString<128> PDBAltPath;
- llvm::SmallString<128> PDBPath;
- llvm::SmallString<128> PDBSourcePath;
- std::vector<llvm::StringRef> Argv;
+ bool is64() { return machine == AMD64 || machine == ARM64; }
+
+ llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
+ size_t wordsize;
+ bool verbose = false;
+ WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
+ Symbol *entry = nullptr;
+ bool noEntry = false;
+ std::string outputFile;
+ std::string importName;
+ bool demangle = true;
+ bool doGC = true;
+ bool doICF = true;
+ bool tailMerge;
+ bool relocatable = true;
+ bool forceMultiple = false;
+ bool forceMultipleRes = false;
+ bool forceUnresolved = false;
+ bool debug = false;
+ bool debugDwarf = false;
+ bool debugGHashes = false;
+ bool debugSymtab = false;
+ bool showTiming = false;
+ bool showSummary = false;
+ unsigned debugTypes = static_cast<unsigned>(DebugType::None);
+ std::vector<std::string> natvisFiles;
+ llvm::SmallString<128> pdbAltPath;
+ llvm::SmallString<128> pdbPath;
+ llvm::SmallString<128> pdbSourcePath;
+ std::vector<llvm::StringRef> argv;
// Symbols in this set are considered as live by the garbage collector.
- std::vector<Symbol *> GCRoot;
+ std::vector<Symbol *> gcroot;
- std::set<StringRef> NoDefaultLibs;
- bool NoDefaultLibAll = false;
+ std::set<std::string> noDefaultLibs;
+ bool noDefaultLibAll = false;
// True if we are creating a DLL.
- bool DLL = false;
- StringRef Implib;
- std::vector<Export> Exports;
- std::set<std::string> DelayLoads;
- std::map<std::string, int> DLLOrder;
- Symbol *DelayLoadHelper = nullptr;
+ bool dll = false;
+ StringRef implib;
+ std::vector<Export> exports;
+ std::set<std::string> delayLoads;
+ std::map<std::string, int> dllOrder;
+ Symbol *delayLoadHelper = nullptr;
- bool SaveTemps = false;
+ bool saveTemps = false;
// /guard:cf
- GuardCFLevel GuardCF = GuardCFLevel::Off;
+ GuardCFLevel guardCF = GuardCFLevel::Off;
// Used for SafeSEH.
- Symbol *SEHTable = nullptr;
- Symbol *SEHCount = nullptr;
+ bool safeSEH = false;
+ Symbol *sehTable = nullptr;
+ Symbol *sehCount = nullptr;
// Used for /opt:lldlto=N
- unsigned LTOO = 2;
+ unsigned ltoo = 2;
// Used for /opt:lldltojobs=N
- unsigned ThinLTOJobs = 0;
+ unsigned thinLTOJobs = 0;
// Used for /opt:lldltopartitions=N
- unsigned LTOPartitions = 1;
+ unsigned ltoPartitions = 1;
// Used for /opt:lldltocache=path
- StringRef LTOCache;
+ StringRef ltoCache;
// Used for /opt:lldltocachepolicy=policy
- llvm::CachePruningPolicy LTOCachePolicy;
+ llvm::CachePruningPolicy ltoCachePolicy;
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
- std::map<StringRef, StringRef> Merge;
+ std::map<StringRef, StringRef> merge;
// Used for /section=.name,{DEKPRSW} to set section attributes.
- std::map<StringRef, uint32_t> Section;
+ std::map<StringRef, uint32_t> section;
// Options for manifest files.
- ManifestKind Manifest = No;
- int ManifestID = 1;
- StringRef ManifestDependency;
- bool ManifestUAC = true;
- std::vector<std::string> ManifestInput;
- StringRef ManifestLevel = "'asInvoker'";
- StringRef ManifestUIAccess = "'false'";
- StringRef ManifestFile;
+ ManifestKind manifest = No;
+ int manifestID = 1;
+ StringRef manifestDependency;
+ bool manifestUAC = true;
+ std::vector<std::string> manifestInput;
+ StringRef manifestLevel = "'asInvoker'";
+ StringRef manifestUIAccess = "'false'";
+ StringRef manifestFile;
// Used for /aligncomm.
- std::map<std::string, int> AlignComm;
+ std::map<std::string, int> alignComm;
// Used for /failifmismatch.
- std::map<StringRef, StringRef> MustMatch;
+ std::map<StringRef, std::pair<StringRef, InputFile *>> mustMatch;
// Used for /alternatename.
- std::map<StringRef, StringRef> AlternateNames;
+ std::map<StringRef, StringRef> alternateNames;
// Used for /order.
- llvm::StringMap<int> Order;
+ llvm::StringMap<int> order;
// Used for /lldmap.
- std::string MapFile;
-
- uint64_t ImageBase = -1;
- uint64_t StackReserve = 1024 * 1024;
- uint64_t StackCommit = 4096;
- uint64_t HeapReserve = 1024 * 1024;
- uint64_t HeapCommit = 4096;
- uint32_t MajorImageVersion = 0;
- uint32_t MinorImageVersion = 0;
- uint32_t MajorOSVersion = 6;
- uint32_t MinorOSVersion = 0;
- uint32_t Timestamp = 0;
- bool DynamicBase = true;
- bool AllowBind = true;
- bool NxCompat = true;
- bool AllowIsolation = true;
- bool TerminalServerAware = true;
- bool LargeAddressAware = false;
- bool HighEntropyVA = false;
- bool AppContainer = false;
- bool MinGW = false;
- bool WarnMissingOrderSymbol = true;
- bool WarnLocallyDefinedImported = true;
- bool WarnDebugInfoUnusable = true;
- bool Incremental = true;
- bool IntegrityCheck = false;
- bool KillAt = false;
- bool Repro = false;
+ std::string mapFile;
+
+ // Used for /thinlto-index-only:
+ llvm::StringRef thinLTOIndexOnlyArg;
+
+ // Used for /thinlto-object-prefix-replace:
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+
+ // Used for /thinlto-object-suffix-replace:
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+
+ uint64_t imageBase = -1;
+ uint64_t fileAlign = 512;
+ uint64_t stackReserve = 1024 * 1024;
+ uint64_t stackCommit = 4096;
+ uint64_t heapReserve = 1024 * 1024;
+ uint64_t heapCommit = 4096;
+ uint32_t majorImageVersion = 0;
+ uint32_t minorImageVersion = 0;
+ uint32_t majorOSVersion = 6;
+ uint32_t minorOSVersion = 0;
+ uint32_t timestamp = 0;
+ uint32_t functionPadMin = 0;
+ bool dynamicBase = true;
+ bool allowBind = true;
+ bool nxCompat = true;
+ bool allowIsolation = true;
+ bool terminalServerAware = true;
+ bool largeAddressAware = false;
+ bool highEntropyVA = false;
+ bool appContainer = false;
+ bool mingw = false;
+ bool warnMissingOrderSymbol = true;
+ bool warnLocallyDefinedImported = true;
+ bool warnDebugInfoUnusable = true;
+ bool incremental = true;
+ bool integrityCheck = false;
+ bool killAt = false;
+ bool repro = false;
+ bool swaprunCD = false;
+ bool swaprunNet = false;
+ bool thinLTOEmitImportsFiles;
+ bool thinLTOIndexOnly;
};
-extern Configuration *Config;
+extern Configuration *config;
} // namespace coff
} // namespace lld
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index 599cc5892a16..40d1f463aa3f 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -1,9 +1,8 @@
//===- DLL.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -36,149 +35,167 @@ namespace {
// Import table
// A chunk for the import descriptor table.
-class HintNameChunk : public Chunk {
+class HintNameChunk : public NonSectionChunk {
public:
- HintNameChunk(StringRef N, uint16_t H) : Name(N), Hint(H) {}
+ HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {}
size_t getSize() const override {
// Starts with 2 byte Hint field, followed by a null-terminated string,
// ends with 0 or 1 byte padding.
- return alignTo(Name.size() + 3, 2);
+ return alignTo(name.size() + 3, 2);
}
- void writeTo(uint8_t *Buf) const override {
- write16le(Buf + OutputSectionOff, Hint);
- memcpy(Buf + OutputSectionOff + 2, Name.data(), Name.size());
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+ write16le(buf, hint);
+ memcpy(buf + 2, name.data(), name.size());
}
private:
- StringRef Name;
- uint16_t Hint;
+ StringRef name;
+ uint16_t hint;
};
// A chunk for the import descriptor table.
-class LookupChunk : public Chunk {
+class LookupChunk : public NonSectionChunk {
public:
- explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; }
- size_t getSize() const override { return Config->Wordsize; }
+ explicit LookupChunk(Chunk *c) : hintName(c) {
+ setAlignment(config->wordsize);
+ }
+ size_t getSize() const override { return config->wordsize; }
- void writeTo(uint8_t *Buf) const override {
- write32le(Buf + OutputSectionOff, HintName->getRVA());
+ void writeTo(uint8_t *buf) const override {
+ if (config->is64())
+ write64le(buf, hintName->getRVA());
+ else
+ write32le(buf, hintName->getRVA());
}
- Chunk *HintName;
+ Chunk *hintName;
};
// A chunk for the import descriptor table.
// This chunk represent import-by-ordinal symbols.
// See Microsoft PE/COFF spec 7.1. Import Header for details.
-class OrdinalOnlyChunk : public Chunk {
+class OrdinalOnlyChunk : public NonSectionChunk {
public:
- explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {
- Alignment = Config->Wordsize;
+ explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return Config->Wordsize; }
+ size_t getSize() const override { return config->wordsize; }
- void writeTo(uint8_t *Buf) const override {
+ void writeTo(uint8_t *buf) const override {
// An import-by-ordinal slot has MSB 1 to indicate that
// this is import-by-ordinal (and not import-by-name).
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, (1ULL << 63) | Ordinal);
+ if (config->is64()) {
+ write64le(buf, (1ULL << 63) | ordinal);
} else {
- write32le(Buf + OutputSectionOff, (1ULL << 31) | Ordinal);
+ write32le(buf, (1ULL << 31) | ordinal);
}
}
- uint16_t Ordinal;
+ uint16_t ordinal;
};
// A chunk for the import descriptor table.
-class ImportDirectoryChunk : public Chunk {
+class ImportDirectoryChunk : public NonSectionChunk {
public:
- explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
+ explicit ImportDirectoryChunk(Chunk *n) : dllName(n) {}
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
- void writeTo(uint8_t *Buf) const override {
- auto *E = (coff_import_directory_table_entry *)(Buf + OutputSectionOff);
- E->ImportLookupTableRVA = LookupTab->getRVA();
- E->NameRVA = DLLName->getRVA();
- E->ImportAddressTableRVA = AddressTab->getRVA();
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+
+ auto *e = (coff_import_directory_table_entry *)(buf);
+ e->ImportLookupTableRVA = lookupTab->getRVA();
+ e->NameRVA = dllName->getRVA();
+ e->ImportAddressTableRVA = addressTab->getRVA();
}
- Chunk *DLLName;
- Chunk *LookupTab;
- Chunk *AddressTab;
+ Chunk *dllName;
+ Chunk *lookupTab;
+ Chunk *addressTab;
};
// A chunk representing null terminator in the import table.
// Contents of this chunk is always null bytes.
-class NullChunk : public Chunk {
+class NullChunk : public NonSectionChunk {
public:
- explicit NullChunk(size_t N) : Size(N) {}
- bool hasData() const override { return false; }
- size_t getSize() const override { return Size; }
+ explicit NullChunk(size_t n) : size(n) { hasData = false; }
+ size_t getSize() const override { return size; }
+
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, size);
+ }
private:
- size_t Size;
+ size_t size;
};
static std::vector<std::vector<DefinedImportData *>>
-binImports(const std::vector<DefinedImportData *> &Imports) {
+binImports(const std::vector<DefinedImportData *> &imports) {
// Group DLL-imported symbols by DLL name because that's how
// symbols are layed out in the import descriptor table.
- auto Less = [](const std::string &A, const std::string &B) {
- return Config->DLLOrder[A] < Config->DLLOrder[B];
+ auto less = [](const std::string &a, const std::string &b) {
+ return config->dllOrder[a] < config->dllOrder[b];
};
std::map<std::string, std::vector<DefinedImportData *>,
- bool(*)(const std::string &, const std::string &)> M(Less);
- for (DefinedImportData *Sym : Imports)
- M[Sym->getDLLName().lower()].push_back(Sym);
+ bool(*)(const std::string &, const std::string &)> m(less);
+ for (DefinedImportData *sym : imports)
+ m[sym->getDLLName().lower()].push_back(sym);
- std::vector<std::vector<DefinedImportData *>> V;
- for (auto &KV : M) {
+ std::vector<std::vector<DefinedImportData *>> v;
+ for (auto &kv : m) {
// Sort symbols by name for each group.
- std::vector<DefinedImportData *> &Syms = KV.second;
- std::sort(Syms.begin(), Syms.end(),
- [](DefinedImportData *A, DefinedImportData *B) {
- return A->getName() < B->getName();
+ std::vector<DefinedImportData *> &syms = kv.second;
+ std::sort(syms.begin(), syms.end(),
+ [](DefinedImportData *a, DefinedImportData *b) {
+ return a->getName() < b->getName();
});
- V.push_back(std::move(Syms));
+ v.push_back(std::move(syms));
}
- return V;
+ return v;
}
// Export table
// See Microsoft PE/COFF spec 4.3 for details.
// A chunk for the delay import descriptor table etnry.
-class DelayDirectoryChunk : public Chunk {
+class DelayDirectoryChunk : public NonSectionChunk {
public:
- explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {}
+ explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {}
size_t getSize() const override {
return sizeof(delay_import_directory_table_entry);
}
- void writeTo(uint8_t *Buf) const override {
- auto *E = (delay_import_directory_table_entry *)(Buf + OutputSectionOff);
- E->Attributes = 1;
- E->Name = DLLName->getRVA();
- E->ModuleHandle = ModuleHandle->getRVA();
- E->DelayImportAddressTable = AddressTab->getRVA();
- E->DelayImportNameTable = NameTab->getRVA();
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+
+ auto *e = (delay_import_directory_table_entry *)(buf);
+ e->Attributes = 1;
+ e->Name = dllName->getRVA();
+ e->ModuleHandle = moduleHandle->getRVA();
+ e->DelayImportAddressTable = addressTab->getRVA();
+ e->DelayImportNameTable = nameTab->getRVA();
}
- Chunk *DLLName;
- Chunk *ModuleHandle;
- Chunk *AddressTab;
- Chunk *NameTab;
+ Chunk *dllName;
+ Chunk *moduleHandle;
+ Chunk *addressTab;
+ Chunk *nameTab;
};
// Initial contents for delay-loaded functions.
// This code calls __delayLoadHelper2 function to resolve a symbol
// and then overwrites its jump table slot with the result
// for subsequent function calls.
-static const uint8_t ThunkX64[] = {
+static const uint8_t thunkX64[] = {
+ 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
+ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeX64[] = {
0x51, // push rcx
0x52, // push rdx
0x41, 0x50, // push r8
@@ -188,7 +205,7 @@ static const uint8_t ThunkX64[] = {
0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1
0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2
0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3
- 0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_<FUNCNAME>]
+ 0x48, 0x8B, 0xD0, // mov rdx, rax
0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...]
0xE8, 0, 0, 0, 0, // call __delayLoadHelper2
0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp]
@@ -203,10 +220,15 @@ static const uint8_t ThunkX64[] = {
0xFF, 0xE0, // jmp rax
};
-static const uint8_t ThunkX86[] = {
+static const uint8_t thunkX86[] = {
+ 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME>
+ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeX86[] = {
0x51, // push ecx
0x52, // push edx
- 0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME>
+ 0x50, // push eax
0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll
0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8
0x5A, // pop edx
@@ -214,9 +236,13 @@ static const uint8_t ThunkX86[] = {
0xFF, 0xE0, // jmp eax
};
-static const uint8_t ThunkARM[] = {
+static const uint8_t thunkARM[] = {
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME>
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME>
+ 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeARM[] = {
0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr}
0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16
0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7}
@@ -230,9 +256,13 @@ static const uint8_t ThunkARM[] = {
0x60, 0x47, // bx ip
};
-static const uint8_t ThunkARM64[] = {
+static const uint8_t thunkARM64[] = {
0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME>
0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME>
+ 0x00, 0x00, 0x00, 0x14, // b __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeARM64[] = {
0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]!
0xfd, 0x03, 0x00, 0x91, // mov x29, sp
0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16]
@@ -261,370 +291,445 @@ static const uint8_t ThunkARM64[] = {
};
// A chunk for the delay import thunk.
-class ThunkChunkX64 : public Chunk {
+class ThunkChunkX64 : public NonSectionChunk {
+public:
+ ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+
+ size_t getSize() const override { return sizeof(thunkX64); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkX64, sizeof(thunkX64));
+ write32le(buf + 3, imp->getRVA() - rva - 7);
+ write32le(buf + 8, tailMerge->getRVA() - rva - 12);
+ }
+
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
+};
+
+class TailMergeChunkX64 : public NonSectionChunk {
+public:
+ TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {}
+
+ size_t getSize() const override { return sizeof(tailMergeX64); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeX64, sizeof(tailMergeX64));
+ write32le(buf + 39, desc->getRVA() - rva - 43);
+ write32le(buf + 44, helper->getRVA() - rva - 48);
+ }
+
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
+};
+
+class ThunkChunkX86 : public NonSectionChunk {
+public:
+ ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+
+ size_t getSize() const override { return sizeof(thunkX86); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkX86, sizeof(thunkX86));
+ write32le(buf + 1, imp->getRVA() + config->imageBase);
+ write32le(buf + 6, tailMerge->getRVA() - rva - 10);
+ }
+
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 1);
+ }
+
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
+};
+
+class TailMergeChunkX86 : public NonSectionChunk {
public:
- ThunkChunkX64(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {}
- size_t getSize() const override { return sizeof(ThunkX64); }
+ size_t getSize() const override { return sizeof(tailMergeX86); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkX64, sizeof(ThunkX64));
- write32le(Buf + OutputSectionOff + 36, Imp->getRVA() - RVA - 40);
- write32le(Buf + OutputSectionOff + 43, Desc->getRVA() - RVA - 47);
- write32le(Buf + OutputSectionOff + 48, Helper->getRVA() - RVA - 52);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeX86, sizeof(tailMergeX86));
+ write32le(buf + 4, desc->getRVA() + config->imageBase);
+ write32le(buf + 9, helper->getRVA() - rva - 13);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 4);
+ }
+
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
};
-class ThunkChunkX86 : public Chunk {
+class ThunkChunkARM : public NonSectionChunk {
public:
- ThunkChunkX86(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
- size_t getSize() const override { return sizeof(ThunkX86); }
+ size_t getSize() const override { return sizeof(thunkARM); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkX86, sizeof(ThunkX86));
- write32le(Buf + OutputSectionOff + 3, Imp->getRVA() + Config->ImageBase);
- write32le(Buf + OutputSectionOff + 8, Desc->getRVA() + Config->ImageBase);
- write32le(Buf + OutputSectionOff + 13, Helper->getRVA() - RVA - 17);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkARM, sizeof(thunkARM));
+ applyMOV32T(buf + 0, imp->getRVA() + config->imageBase);
+ applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
}
- void getBaserels(std::vector<Baserel> *Res) override {
- Res->emplace_back(RVA + 3);
- Res->emplace_back(RVA + 8);
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
};
-class ThunkChunkARM : public Chunk {
+class TailMergeChunkARM : public NonSectionChunk {
public:
- ThunkChunkARM(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {}
- size_t getSize() const override { return sizeof(ThunkARM); }
+ size_t getSize() const override { return sizeof(tailMergeARM); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkARM, sizeof(ThunkARM));
- applyMOV32T(Buf + OutputSectionOff + 0, Imp->getRVA() + Config->ImageBase);
- applyMOV32T(Buf + OutputSectionOff + 22, Desc->getRVA() + Config->ImageBase);
- applyBranch24T(Buf + OutputSectionOff + 30, Helper->getRVA() - RVA - 34);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
+ applyMOV32T(buf + 14, desc->getRVA() + config->imageBase);
+ applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
}
- void getBaserels(std::vector<Baserel> *Res) override {
- Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T);
- Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T);
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
};
-class ThunkChunkARM64 : public Chunk {
+class ThunkChunkARM64 : public NonSectionChunk {
public:
- ThunkChunkARM64(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
- size_t getSize() const override { return sizeof(ThunkARM64); }
+ size_t getSize() const override { return sizeof(thunkARM64); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64));
- applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12);
- applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0);
- applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12);
- applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0);
- applyArm64Branch26(Buf + OutputSectionOff + 60,
- Helper->getRVA() - RVA - 60);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkARM64, sizeof(thunkARM64));
+ applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12);
+ applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0);
+ applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
+};
+
+class TailMergeChunkARM64 : public NonSectionChunk {
+public:
+ TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {}
+
+ size_t getSize() const override { return sizeof(tailMergeARM64); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64));
+ applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12);
+ applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0);
+ applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52);
+ }
+
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
};
// A chunk for the import descriptor table.
-class DelayAddressChunk : public Chunk {
+class DelayAddressChunk : public NonSectionChunk {
public:
- explicit DelayAddressChunk(Chunk *C) : Thunk(C) {
- Alignment = Config->Wordsize;
+ explicit DelayAddressChunk(Chunk *c) : thunk(c) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return Config->Wordsize; }
+ size_t getSize() const override { return config->wordsize; }
- void writeTo(uint8_t *Buf) const override {
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
+ void writeTo(uint8_t *buf) const override {
+ if (config->is64()) {
+ write64le(buf, thunk->getRVA() + config->imageBase);
} else {
- uint32_t Bit = 0;
+ uint32_t bit = 0;
// Pointer to thumb code must have the LSB set, so adjust it.
- if (Config->Machine == ARMNT)
- Bit = 1;
- write32le(Buf + OutputSectionOff, (Thunk->getRVA() + Config->ImageBase) | Bit);
+ if (config->machine == ARMNT)
+ bit = 1;
+ write32le(buf, (thunk->getRVA() + config->imageBase) | bit);
}
}
- void getBaserels(std::vector<Baserel> *Res) override {
- Res->emplace_back(RVA);
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva);
}
- Chunk *Thunk;
+ Chunk *thunk;
};
// Export table
// Read Microsoft PE/COFF spec 5.3 for details.
// A chunk for the export descriptor table.
-class ExportDirectoryChunk : public Chunk {
+class ExportDirectoryChunk : public NonSectionChunk {
public:
- ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O)
- : MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N),
- OrdinalTab(O) {}
+ ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o)
+ : maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n),
+ ordinalTab(o) {}
size_t getSize() const override {
return sizeof(export_directory_table_entry);
}
- void writeTo(uint8_t *Buf) const override {
- auto *E = (export_directory_table_entry *)(Buf + OutputSectionOff);
- E->NameRVA = DLLName->getRVA();
- E->OrdinalBase = 0;
- E->AddressTableEntries = MaxOrdinal + 1;
- E->NumberOfNamePointers = NameTabSize;
- E->ExportAddressTableRVA = AddressTab->getRVA();
- E->NamePointerRVA = NameTab->getRVA();
- E->OrdinalTableRVA = OrdinalTab->getRVA();
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+
+ auto *e = (export_directory_table_entry *)(buf);
+ e->NameRVA = dllName->getRVA();
+ e->OrdinalBase = 0;
+ e->AddressTableEntries = maxOrdinal + 1;
+ e->NumberOfNamePointers = nameTabSize;
+ e->ExportAddressTableRVA = addressTab->getRVA();
+ e->NamePointerRVA = nameTab->getRVA();
+ e->OrdinalTableRVA = ordinalTab->getRVA();
}
- uint16_t MaxOrdinal;
- uint16_t NameTabSize;
- Chunk *DLLName;
- Chunk *AddressTab;
- Chunk *NameTab;
- Chunk *OrdinalTab;
+ uint16_t maxOrdinal;
+ uint16_t nameTabSize;
+ Chunk *dllName;
+ Chunk *addressTab;
+ Chunk *nameTab;
+ Chunk *ordinalTab;
};
-class AddressTableChunk : public Chunk {
+class AddressTableChunk : public NonSectionChunk {
public:
- explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {}
- size_t getSize() const override { return Size * 4; }
+ explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {}
+ size_t getSize() const override { return size * 4; }
- void writeTo(uint8_t *Buf) const override {
- memset(Buf + OutputSectionOff, 0, getSize());
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
- for (const Export &E : Config->Exports) {
- uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
- uint32_t Bit = 0;
+ for (const Export &e : config->exports) {
+ uint8_t *p = buf + e.ordinal * 4;
+ uint32_t bit = 0;
// Pointer to thumb code must have the LSB set, so adjust it.
- if (Config->Machine == ARMNT && !E.Data)
- Bit = 1;
- if (E.ForwardChunk) {
- write32le(P, E.ForwardChunk->getRVA() | Bit);
+ if (config->machine == ARMNT && !e.data)
+ bit = 1;
+ if (e.forwardChunk) {
+ write32le(p, e.forwardChunk->getRVA() | bit);
} else {
- write32le(P, cast<Defined>(E.Sym)->getRVA() | Bit);
+ write32le(p, cast<Defined>(e.sym)->getRVA() | bit);
}
}
}
private:
- size_t Size;
+ size_t size;
};
-class NamePointersChunk : public Chunk {
+class NamePointersChunk : public NonSectionChunk {
public:
- explicit NamePointersChunk(std::vector<Chunk *> &V) : Chunks(V) {}
- size_t getSize() const override { return Chunks.size() * 4; }
-
- void writeTo(uint8_t *Buf) const override {
- uint8_t *P = Buf + OutputSectionOff;
- for (Chunk *C : Chunks) {
- write32le(P, C->getRVA());
- P += 4;
+ explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {}
+ size_t getSize() const override { return chunks.size() * 4; }
+
+ void writeTo(uint8_t *buf) const override {
+ for (Chunk *c : chunks) {
+ write32le(buf, c->getRVA());
+ buf += 4;
}
}
private:
- std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> chunks;
};
-class ExportOrdinalChunk : public Chunk {
+class ExportOrdinalChunk : public NonSectionChunk {
public:
- explicit ExportOrdinalChunk(size_t I) : Size(I) {}
- size_t getSize() const override { return Size * 2; }
+ explicit ExportOrdinalChunk(size_t i) : size(i) {}
+ size_t getSize() const override { return size * 2; }
- void writeTo(uint8_t *Buf) const override {
- uint8_t *P = Buf + OutputSectionOff;
- for (Export &E : Config->Exports) {
- if (E.Noname)
+ void writeTo(uint8_t *buf) const override {
+ for (Export &e : config->exports) {
+ if (e.noname)
continue;
- write16le(P, E.Ordinal);
- P += 2;
+ write16le(buf, e.ordinal);
+ buf += 2;
}
}
private:
- size_t Size;
+ size_t size;
};
} // anonymous namespace
void IdataContents::create() {
- std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
+ std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
// Create .idata contents for each DLL.
- for (std::vector<DefinedImportData *> &Syms : V) {
+ for (std::vector<DefinedImportData *> &syms : v) {
// Create lookup and address tables. If they have external names,
- // we need to create HintName chunks to store the names.
+ // we need to create hintName chunks to store the names.
// If they don't (if they are import-by-ordinals), we store only
// ordinal values to the table.
- size_t Base = Lookups.size();
- for (DefinedImportData *S : Syms) {
- uint16_t Ord = S->getOrdinal();
- if (S->getExternalName().empty()) {
- Lookups.push_back(make<OrdinalOnlyChunk>(Ord));
- Addresses.push_back(make<OrdinalOnlyChunk>(Ord));
+ size_t base = lookups.size();
+ for (DefinedImportData *s : syms) {
+ uint16_t ord = s->getOrdinal();
+ if (s->getExternalName().empty()) {
+ lookups.push_back(make<OrdinalOnlyChunk>(ord));
+ addresses.push_back(make<OrdinalOnlyChunk>(ord));
continue;
}
- auto *C = make<HintNameChunk>(S->getExternalName(), Ord);
- Lookups.push_back(make<LookupChunk>(C));
- Addresses.push_back(make<LookupChunk>(C));
- Hints.push_back(C);
+ auto *c = make<HintNameChunk>(s->getExternalName(), ord);
+ lookups.push_back(make<LookupChunk>(c));
+ addresses.push_back(make<LookupChunk>(c));
+ hints.push_back(c);
}
// Terminate with null values.
- Lookups.push_back(make<NullChunk>(Config->Wordsize));
- Addresses.push_back(make<NullChunk>(Config->Wordsize));
+ lookups.push_back(make<NullChunk>(config->wordsize));
+ addresses.push_back(make<NullChunk>(config->wordsize));
- for (int I = 0, E = Syms.size(); I < E; ++I)
- Syms[I]->setLocation(Addresses[Base + I]);
+ for (int i = 0, e = syms.size(); i < e; ++i)
+ syms[i]->setLocation(addresses[base + i]);
// Create the import table header.
- DLLNames.push_back(make<StringChunk>(Syms[0]->getDLLName()));
- auto *Dir = make<ImportDirectoryChunk>(DLLNames.back());
- Dir->LookupTab = Lookups[Base];
- Dir->AddressTab = Addresses[Base];
- Dirs.push_back(Dir);
+ dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
+ auto *dir = make<ImportDirectoryChunk>(dllNames.back());
+ dir->lookupTab = lookups[base];
+ dir->addressTab = addresses[base];
+ dirs.push_back(dir);
}
// Add null terminator.
- Dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry)));
+ dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry)));
}
std::vector<Chunk *> DelayLoadContents::getChunks() {
- std::vector<Chunk *> V;
- V.insert(V.end(), Dirs.begin(), Dirs.end());
- V.insert(V.end(), Names.begin(), Names.end());
- V.insert(V.end(), HintNames.begin(), HintNames.end());
- V.insert(V.end(), DLLNames.begin(), DLLNames.end());
- return V;
+ std::vector<Chunk *> v;
+ v.insert(v.end(), dirs.begin(), dirs.end());
+ v.insert(v.end(), names.begin(), names.end());
+ v.insert(v.end(), hintNames.begin(), hintNames.end());
+ v.insert(v.end(), dllNames.begin(), dllNames.end());
+ return v;
}
std::vector<Chunk *> DelayLoadContents::getDataChunks() {
- std::vector<Chunk *> V;
- V.insert(V.end(), ModuleHandles.begin(), ModuleHandles.end());
- V.insert(V.end(), Addresses.begin(), Addresses.end());
- return V;
+ std::vector<Chunk *> v;
+ v.insert(v.end(), moduleHandles.begin(), moduleHandles.end());
+ v.insert(v.end(), addresses.begin(), addresses.end());
+ return v;
}
uint64_t DelayLoadContents::getDirSize() {
- return Dirs.size() * sizeof(delay_import_directory_table_entry);
+ return dirs.size() * sizeof(delay_import_directory_table_entry);
}
-void DelayLoadContents::create(Defined *H) {
- Helper = H;
- std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
+void DelayLoadContents::create(Defined *h) {
+ helper = h;
+ std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
// Create .didat contents for each DLL.
- for (std::vector<DefinedImportData *> &Syms : V) {
+ for (std::vector<DefinedImportData *> &syms : v) {
// Create the delay import table header.
- DLLNames.push_back(make<StringChunk>(Syms[0]->getDLLName()));
- auto *Dir = make<DelayDirectoryChunk>(DLLNames.back());
-
- size_t Base = Addresses.size();
- for (DefinedImportData *S : Syms) {
- Chunk *T = newThunkChunk(S, Dir);
- auto *A = make<DelayAddressChunk>(T);
- Addresses.push_back(A);
- Thunks.push_back(T);
- StringRef ExtName = S->getExternalName();
- if (ExtName.empty()) {
- Names.push_back(make<OrdinalOnlyChunk>(S->getOrdinal()));
+ dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
+ auto *dir = make<DelayDirectoryChunk>(dllNames.back());
+
+ size_t base = addresses.size();
+ Chunk *tm = newTailMergeChunk(dir);
+ for (DefinedImportData *s : syms) {
+ Chunk *t = newThunkChunk(s, tm);
+ auto *a = make<DelayAddressChunk>(t);
+ addresses.push_back(a);
+ thunks.push_back(t);
+ StringRef extName = s->getExternalName();
+ if (extName.empty()) {
+ names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal()));
} else {
- auto *C = make<HintNameChunk>(ExtName, 0);
- Names.push_back(make<LookupChunk>(C));
- HintNames.push_back(C);
+ auto *c = make<HintNameChunk>(extName, 0);
+ names.push_back(make<LookupChunk>(c));
+ hintNames.push_back(c);
}
}
+ thunks.push_back(tm);
// Terminate with null values.
- Addresses.push_back(make<NullChunk>(8));
- Names.push_back(make<NullChunk>(8));
+ addresses.push_back(make<NullChunk>(8));
+ names.push_back(make<NullChunk>(8));
- for (int I = 0, E = Syms.size(); I < E; ++I)
- Syms[I]->setLocation(Addresses[Base + I]);
- auto *MH = make<NullChunk>(8);
- MH->Alignment = 8;
- ModuleHandles.push_back(MH);
+ for (int i = 0, e = syms.size(); i < e; ++i)
+ syms[i]->setLocation(addresses[base + i]);
+ auto *mh = make<NullChunk>(8);
+ mh->setAlignment(8);
+ moduleHandles.push_back(mh);
// Fill the delay import table header fields.
- Dir->ModuleHandle = MH;
- Dir->AddressTab = Addresses[Base];
- Dir->NameTab = Names[Base];
- Dirs.push_back(Dir);
+ dir->moduleHandle = mh;
+ dir->addressTab = addresses[base];
+ dir->nameTab = names[base];
+ dirs.push_back(dir);
}
// Add null terminator.
- Dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
+ dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
+}
+
+Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
+ switch (config->machine) {
+ case AMD64:
+ return make<TailMergeChunkX64>(dir, helper);
+ case I386:
+ return make<TailMergeChunkX86>(dir, helper);
+ case ARMNT:
+ return make<TailMergeChunkARM>(dir, helper);
+ case ARM64:
+ return make<TailMergeChunkARM64>(dir, helper);
+ default:
+ llvm_unreachable("unsupported machine type");
+ }
}
-Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
- switch (Config->Machine) {
+Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
+ Chunk *tailMerge) {
+ switch (config->machine) {
case AMD64:
- return make<ThunkChunkX64>(S, Dir, Helper);
+ return make<ThunkChunkX64>(s, tailMerge);
case I386:
- return make<ThunkChunkX86>(S, Dir, Helper);
+ return make<ThunkChunkX86>(s, tailMerge);
case ARMNT:
- return make<ThunkChunkARM>(S, Dir, Helper);
+ return make<ThunkChunkARM>(s, tailMerge);
case ARM64:
- return make<ThunkChunkARM64>(S, Dir, Helper);
+ return make<ThunkChunkARM64>(s, tailMerge);
default:
llvm_unreachable("unsupported machine type");
}
}
EdataContents::EdataContents() {
- uint16_t MaxOrdinal = 0;
- for (Export &E : Config->Exports)
- MaxOrdinal = std::max(MaxOrdinal, E.Ordinal);
-
- auto *DLLName = make<StringChunk>(sys::path::filename(Config->OutputFile));
- auto *AddressTab = make<AddressTableChunk>(MaxOrdinal);
- std::vector<Chunk *> Names;
- for (Export &E : Config->Exports)
- if (!E.Noname)
- Names.push_back(make<StringChunk>(E.ExportName));
-
- std::vector<Chunk *> Forwards;
- for (Export &E : Config->Exports) {
- if (E.ForwardTo.empty())
+ uint16_t maxOrdinal = 0;
+ for (Export &e : config->exports)
+ maxOrdinal = std::max(maxOrdinal, e.ordinal);
+
+ auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile));
+ auto *addressTab = make<AddressTableChunk>(maxOrdinal);
+ std::vector<Chunk *> names;
+ for (Export &e : config->exports)
+ if (!e.noname)
+ names.push_back(make<StringChunk>(e.exportName));
+
+ std::vector<Chunk *> forwards;
+ for (Export &e : config->exports) {
+ if (e.forwardTo.empty())
continue;
- E.ForwardChunk = make<StringChunk>(E.ForwardTo);
- Forwards.push_back(E.ForwardChunk);
- }
-
- auto *NameTab = make<NamePointersChunk>(Names);
- auto *OrdinalTab = make<ExportOrdinalChunk>(Names.size());
- auto *Dir = make<ExportDirectoryChunk>(MaxOrdinal, Names.size(), DLLName,
- AddressTab, NameTab, OrdinalTab);
- Chunks.push_back(Dir);
- Chunks.push_back(DLLName);
- Chunks.push_back(AddressTab);
- Chunks.push_back(NameTab);
- Chunks.push_back(OrdinalTab);
- Chunks.insert(Chunks.end(), Names.begin(), Names.end());
- Chunks.insert(Chunks.end(), Forwards.begin(), Forwards.end());
+ e.forwardChunk = make<StringChunk>(e.forwardTo);
+ forwards.push_back(e.forwardChunk);
+ }
+
+ auto *nameTab = make<NamePointersChunk>(names);
+ auto *ordinalTab = make<ExportOrdinalChunk>(names.size());
+ auto *dir = make<ExportDirectoryChunk>(maxOrdinal, names.size(), dllName,
+ addressTab, nameTab, ordinalTab);
+ chunks.push_back(dir);
+ chunks.push_back(dllName);
+ chunks.push_back(addressTab);
+ chunks.push_back(nameTab);
+ chunks.push_back(ordinalTab);
+ chunks.insert(chunks.end(), names.begin(), names.end());
+ chunks.insert(chunks.end(), forwards.begin(), forwards.end());
}
} // namespace coff
diff --git a/COFF/DLL.h b/COFF/DLL.h
index a298271e2c0d..ce0ee01c4a3d 100644
--- a/COFF/DLL.h
+++ b/COFF/DLL.h
@@ -1,9 +1,8 @@
//===- DLL.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -22,45 +21,46 @@ namespace coff {
// call create() to populate the chunk vectors.
class IdataContents {
public:
- void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
- bool empty() { return Imports.empty(); }
+ void add(DefinedImportData *sym) { imports.push_back(sym); }
+ bool empty() { return imports.empty(); }
void create();
- std::vector<DefinedImportData *> Imports;
- std::vector<Chunk *> Dirs;
- std::vector<Chunk *> Lookups;
- std::vector<Chunk *> Addresses;
- std::vector<Chunk *> Hints;
- std::vector<Chunk *> DLLNames;
+ std::vector<DefinedImportData *> imports;
+ std::vector<Chunk *> dirs;
+ std::vector<Chunk *> lookups;
+ std::vector<Chunk *> addresses;
+ std::vector<Chunk *> hints;
+ std::vector<Chunk *> dllNames;
};
// Windows-specific.
// DelayLoadContents creates all chunks for the delay-load DLL import table.
class DelayLoadContents {
public:
- void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
- bool empty() { return Imports.empty(); }
- void create(Defined *Helper);
+ void add(DefinedImportData *sym) { imports.push_back(sym); }
+ bool empty() { return imports.empty(); }
+ void create(Defined *helper);
std::vector<Chunk *> getChunks();
std::vector<Chunk *> getDataChunks();
- ArrayRef<Chunk *> getCodeChunks() { return Thunks; }
+ ArrayRef<Chunk *> getCodeChunks() { return thunks; }
- uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
+ uint64_t getDirRVA() { return dirs[0]->getRVA(); }
uint64_t getDirSize();
private:
- Chunk *newThunkChunk(DefinedImportData *S, Chunk *Dir);
+ Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
+ Chunk *newTailMergeChunk(Chunk *dir);
- Defined *Helper;
- std::vector<DefinedImportData *> Imports;
- std::vector<Chunk *> Dirs;
- std::vector<Chunk *> ModuleHandles;
- std::vector<Chunk *> Addresses;
- std::vector<Chunk *> Names;
- std::vector<Chunk *> HintNames;
- std::vector<Chunk *> Thunks;
- std::vector<Chunk *> DLLNames;
+ Defined *helper;
+ std::vector<DefinedImportData *> imports;
+ std::vector<Chunk *> dirs;
+ std::vector<Chunk *> moduleHandles;
+ std::vector<Chunk *> addresses;
+ std::vector<Chunk *> names;
+ std::vector<Chunk *> hintNames;
+ std::vector<Chunk *> thunks;
+ std::vector<Chunk *> dllNames;
};
// Windows-specific.
@@ -68,11 +68,11 @@ private:
class EdataContents {
public:
EdataContents();
- std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> chunks;
- uint64_t getRVA() { return Chunks[0]->getRVA(); }
+ uint64_t getRVA() { return chunks[0]->getRVA(); }
uint64_t getSize() {
- return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA();
+ return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA();
}
};
diff --git a/COFF/DebugTypes.cpp b/COFF/DebugTypes.cpp
new file mode 100644
index 000000000000..78c1c78b408d
--- /dev/null
+++ b/COFF/DebugTypes.cpp
@@ -0,0 +1,268 @@
+//===- DebugTypes.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 "DebugTypes.h"
+#include "Driver.h"
+#include "InputFiles.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.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/Support/Path.h"
+
+using namespace lld;
+using namespace lld::coff;
+using namespace llvm;
+using namespace llvm::codeview;
+
+namespace {
+// The TypeServerSource class represents a PDB type server, a file referenced by
+// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
+// files, therefore there must be only once instance per OBJ lot. The file path
+// is discovered from the dependent OBJ's debug type stream. The
+// TypeServerSource object is then queued and loaded by the COFF Driver. The
+// debug type stream for such PDB files will be merged first in the final PDB,
+// 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;
+};
+
+// 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) {}
+
+ // Information about the PDB type server dependency, that needs to be loaded
+ // in before merging this OBJ.
+ TypeServer2Record typeServerDependency;
+};
+
+// This class represents the debug type stream of a Microsoft precompiled
+// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output
+// PDB, before any other OBJs that depend on this. Note that only MSVC generate
+// such files, clang does not.
+class PrecompSource : public TpiSource {
+public:
+ PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {}
+};
+
+// 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) {}
+
+ // Information about the Precomp OBJ dependency, that needs to be loaded in
+ // before merging this OBJ.
+ PrecompRecord precompDependency;
+};
+} // namespace
+
+static std::vector<std::unique_ptr<TpiSource>> GC;
+
+TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {
+ GC.push_back(std::unique_ptr<TpiSource>(this));
+}
+
+TpiSource *lld::coff::makeTpiSource(const ObjFile *f) {
+ return new TpiSource(TpiSource::Regular, f);
+}
+
+TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f,
+ const TypeServer2Record *ts) {
+ TypeServerSource::enqueue(f, *ts);
+ return new UseTypeServerSource(f, ts);
+}
+
+TpiSource *lld::coff::makePrecompSource(const ObjFile *f) {
+ return new PrecompSource(f);
+}
+
+TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f,
+ const PrecompRecord *precomp) {
+ return new UsePrecompSource(f, precomp);
+}
+
+namespace lld {
+namespace coff {
+template <>
+const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
+ assert(source->kind == TpiSource::UsingPCH);
+ return ((const UsePrecompSource *)source)->precompDependency;
+}
+
+template <>
+const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
+ assert(source->kind == TpiSource::UsingPDB);
+ return ((const UseTypeServerSource *)source)->typeServerDependency;
+}
+} // namespace coff
+} // namespace lld
+
+std::map<std::string, std::pair<std::string, TypeServerSource *>>
+ TypeServerSource::instances;
+
+// 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);
+
+ // 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();
+}
+
+// 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
+}
+
+// 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;
+}
+
+// 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);
+
+ Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
+ if (!p)
+ return createFileError(ts.Name, errorCodeToError(std::error_code(
+ ENOENT, std::generic_category())));
+
+ 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());
+
+ std::pair<std::string, TypeServerSource *> &pdb = it->second;
+
+ if (!pdb.second)
+ return createFileError(
+ *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str()));
+
+ pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile();
+ pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
+
+ // 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())
+ return createFileError(
+ ts.Name,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
+
+ return pdb.second;
+}
+
+// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
+// moved here.
+Expected<llvm::pdb::NativeSession *>
+lld::coff::findTypeServerSource(const ObjFile *f) {
+ Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
+ if (!ts)
+ return ts.takeError();
+ return ts.get()->session.get();
+}
+
+// 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);
+}
+
+// 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 lld::coff::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<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 new TypeServerSource(m, session.release());
+}
diff --git a/COFF/DebugTypes.h b/COFF/DebugTypes.h
new file mode 100644
index 000000000000..e37c727232d0
--- /dev/null
+++ b/COFF/DebugTypes.h
@@ -0,0 +1,60 @@
+//===- DebugTypes.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_DEBUGTYPES_H
+#define LLD_COFF_DEBUGTYPES_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace llvm {
+namespace codeview {
+class PrecompRecord;
+class TypeServer2Record;
+} // namespace codeview
+namespace pdb {
+class NativeSession;
+}
+} // namespace llvm
+
+namespace lld {
+namespace coff {
+
+class ObjFile;
+
+class TpiSource {
+public:
+ enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB };
+
+ TpiSource(TpiKind k, const ObjFile *f);
+ virtual ~TpiSource() {}
+
+ const TpiKind kind;
+ const ObjFile *file;
+};
+
+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);
+
+void loadTypeServerSource(llvm::MemoryBufferRef m);
+
+// Temporary interface to get the dependency
+template <typename T> const T &retrieveDependencyInfo(const TpiSource *source);
+
+// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here
+llvm::Expected<llvm::pdb::NativeSession *>
+findTypeServerSource(const ObjFile *f);
+
+} // namespace coff
+} // namespace lld
+
+#endif \ No newline at end of file
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 2e4b1e6d3147..d7af50b9318f 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -1,14 +1,14 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "DebugTypes.h"
#include "ICF.h"
#include "InputFiles.h"
#include "MarkLive.h"
@@ -19,7 +19,9 @@
#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#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"
@@ -28,6 +30,7 @@
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/COFFModuleDefinition.h"
+#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
@@ -51,43 +54,68 @@ using llvm::sys::Process;
namespace lld {
namespace coff {
-static Timer InputFileTimer("Input File Reading", Timer::root());
+static Timer inputFileTimer("Input File Reading", Timer::root());
-Configuration *Config;
-LinkerDriver *Driver;
+Configuration *config;
+LinkerDriver *driver;
-bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
- errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
- errorHandler().ErrorOS = &Diag;
- errorHandler().ColorDiagnostics = Diag.has_colors();
- errorHandler().ErrorLimitExceededMsg =
+bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
+ errorHandler().logName = args::getFilenameWithoutExe(args[0]);
+ errorHandler().errorOS = &diag;
+ errorHandler().colorDiagnostics = diag.has_colors();
+ errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now"
" (use /errorlimit:0 to see all errors)";
- errorHandler().ExitEarly = CanExitEarly;
- Config = make<Configuration>();
+ errorHandler().exitEarly = canExitEarly;
+ config = make<Configuration>();
- Symtab = make<SymbolTable>();
+ symtab = make<SymbolTable>();
- Driver = make<LinkerDriver>();
- Driver->link(Args);
+ driver = make<LinkerDriver>();
+ driver->link(args);
// Call exit() if we can to avoid calling destructors.
- if (CanExitEarly)
+ if (canExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
- ObjFile::Instances.clear();
- ImportFile::Instances.clear();
- BitcodeFile::Instances.clear();
+ ObjFile::instances.clear();
+ ImportFile::instances.clear();
+ BitcodeFile::instances.clear();
+ memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
return !errorCount();
}
+// Parse options of the form "old;new".
+static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
+ unsigned id) {
+ auto *arg = args.getLastArg(id);
+ if (!arg)
+ return {"", ""};
+
+ StringRef s = arg->getValue();
+ std::pair<StringRef, StringRef> ret = s.split(';');
+ if (ret.second.empty())
+ error(arg->getSpelling() + " expects 'old;new' format, but got " + s);
+ return ret;
+}
+
// Drop directory components and replace extension with ".exe" or ".dll".
-static std::string getOutputPath(StringRef Path) {
- auto P = Path.find_last_of("\\/");
- StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1);
- const char* E = Config->DLL ? ".dll" : ".exe";
- return (S.substr(0, S.rfind('.')) + E).str();
+static std::string getOutputPath(StringRef path) {
+ auto p = path.find_last_of("\\/");
+ StringRef s = (p == StringRef::npos) ? path : path.substr(p + 1);
+ const char* e = config->dll ? ".dll" : ".exe";
+ return (s.substr(0, s.rfind('.')) + e).str();
+}
+
+// Returns true if S matches /crtend.?\.o$/.
+static bool isCrtend(StringRef s) {
+ if (!s.endswith(".o"))
+ return false;
+ s = s.drop_back(2);
+ if (s.endswith("crtend"))
+ return true;
+ return !s.empty() && s.drop_back().endswith("crtend");
}
// ErrorOr is not default constructible, so it cannot be used as the type
@@ -95,347 +123,401 @@ static std::string getOutputPath(StringRef Path) {
// FIXME: We could open the file in createFutureForFile and avoid needing to
// return an error here, but for the moment that would cost us a file descriptor
// (a limited resource on Windows) for the duration that the future is pending.
-typedef std::pair<std::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair;
+using MBErrPair = std::pair<std::unique_ptr<MemoryBuffer>, std::error_code>;
// Create a std::future that opens and maps a file using the best strategy for
// the host platform.
-static std::future<MBErrPair> createFutureForFile(std::string Path) {
+static std::future<MBErrPair> createFutureForFile(std::string path) {
#if _WIN32
// On Windows, file I/O is relatively slow so it is best to do this
// asynchronously.
- auto Strategy = std::launch::async;
+ auto strategy = std::launch::async;
#else
- auto Strategy = std::launch::deferred;
+ auto strategy = std::launch::deferred;
#endif
- return std::async(Strategy, [=]() {
- auto MBOrErr = MemoryBuffer::getFile(Path,
+ return std::async(strategy, [=]() {
+ auto mbOrErr = MemoryBuffer::getFile(path,
/*FileSize*/ -1,
/*RequiresNullTerminator*/ false);
- if (!MBOrErr)
- return MBErrPair{nullptr, MBOrErr.getError()};
- return MBErrPair{std::move(*MBOrErr), std::error_code()};
+ if (!mbOrErr)
+ return MBErrPair{nullptr, mbOrErr.getError()};
+ return MBErrPair{std::move(*mbOrErr), std::error_code()};
});
}
// Symbol names are mangled by prepending "_" on x86.
-static StringRef mangle(StringRef Sym) {
- assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
- if (Config->Machine == I386)
- return Saver.save("_" + Sym);
- return Sym;
+static StringRef mangle(StringRef sym) {
+ assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
+ if (config->machine == I386)
+ return saver.save("_" + sym);
+ return sym;
}
-static bool findUnderscoreMangle(StringRef Sym) {
- StringRef Entry = Symtab->findMangle(mangle(Sym));
- return !Entry.empty() && !isa<Undefined>(Symtab->find(Entry));
+static bool findUnderscoreMangle(StringRef sym) {
+ Symbol *s = symtab->findMangle(mangle(sym));
+ return s && !isa<Undefined>(s);
}
-MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
- MemoryBufferRef MBRef = *MB;
- make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take ownership
+MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
+ MemoryBufferRef mbref = *mb;
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take ownership
- if (Driver->Tar)
- Driver->Tar->append(relativeToRoot(MBRef.getBufferIdentifier()),
- MBRef.getBuffer());
- return MBRef;
+ if (driver->tar)
+ driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()),
+ mbref.getBuffer());
+ return mbref;
}
-void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB,
- bool WholeArchive) {
- StringRef Filename = MB->getBufferIdentifier();
+void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
+ bool wholeArchive) {
+ StringRef filename = mb->getBufferIdentifier();
- MemoryBufferRef MBRef = takeBuffer(std::move(MB));
- FilePaths.push_back(Filename);
+ MemoryBufferRef mbref = takeBuffer(std::move(mb));
+ filePaths.push_back(filename);
// File type is detected by contents, not by file extension.
- switch (identify_magic(MBRef.getBuffer())) {
+ switch (identify_magic(mbref.getBuffer())) {
case file_magic::windows_resource:
- Resources.push_back(MBRef);
+ resources.push_back(mbref);
break;
case file_magic::archive:
- if (WholeArchive) {
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MBRef), Filename + ": failed to parse archive");
+ if (wholeArchive) {
+ std::unique_ptr<Archive> file =
+ CHECK(Archive::create(mbref), filename + ": failed to parse archive");
- for (MemoryBufferRef M : getArchiveMembers(File.get()))
- addArchiveBuffer(M, "<whole-archive>", Filename);
+ for (MemoryBufferRef m : getArchiveMembers(file.get()))
+ addArchiveBuffer(m, "<whole-archive>", filename, 0);
return;
}
- Symtab->addFile(make<ArchiveFile>(MBRef));
+ symtab->addFile(make<ArchiveFile>(mbref));
break;
case file_magic::bitcode:
- Symtab->addFile(make<BitcodeFile>(MBRef));
+ symtab->addFile(make<BitcodeFile>(mbref, "", 0));
break;
case file_magic::coff_object:
case file_magic::coff_import_library:
- Symtab->addFile(make<ObjFile>(MBRef));
+ symtab->addFile(make<ObjFile>(mbref));
+ break;
+ case file_magic::pdb:
+ loadTypeServerSource(mbref);
break;
case file_magic::coff_cl_gl_object:
- error(Filename + ": is not a native COFF file. Recompile without /GL");
+ error(filename + ": is not a native COFF file. Recompile without /GL");
break;
case file_magic::pecoff_executable:
- if (Filename.endswith_lower(".dll")) {
- error(Filename + ": bad file type. Did you specify a DLL instead of an "
+ if (filename.endswith_lower(".dll")) {
+ error(filename + ": bad file type. Did you specify a DLL instead of an "
"import library?");
break;
}
LLVM_FALLTHROUGH;
default:
- error(MBRef.getBufferIdentifier() + ": unknown file type");
+ error(mbref.getBufferIdentifier() + ": unknown file type");
break;
}
}
-void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) {
- auto Future =
- std::make_shared<std::future<MBErrPair>>(createFutureForFile(Path));
- std::string PathStr = Path;
+void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
+ auto future =
+ std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
+ std::string pathStr = path;
enqueueTask([=]() {
- auto MBOrErr = Future->get();
- if (MBOrErr.second)
- error("could not open " + PathStr + ": " + MBOrErr.second.message());
- else
- Driver->addBuffer(std::move(MBOrErr.first), WholeArchive);
+ auto mbOrErr = future->get();
+ if (mbOrErr.second) {
+ std::string msg =
+ "could not open '" + pathStr + "': " + mbOrErr.second.message();
+ // Check if the filename is a typo for an option flag. OptTable thinks
+ // that all args that are not known options and that start with / are
+ // filenames, but e.g. `/nodefaultlibs` is more likely a typo for
+ // the option `/nodefaultlib` than a reference to a file in the root
+ // directory.
+ std::string nearest;
+ if (COFFOptTable().findNearest(pathStr, nearest) > 1)
+ error(msg);
+ else
+ error(msg + "; did you mean '" + nearest + "'");
+ } else
+ driver->addBuffer(std::move(mbOrErr.first), wholeArchive);
});
}
-void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
- StringRef ParentName) {
- file_magic Magic = identify_magic(MB.getBuffer());
- if (Magic == file_magic::coff_import_library) {
- Symtab->addFile(make<ImportFile>(MB));
+void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
+ StringRef parentName,
+ uint64_t offsetInArchive) {
+ file_magic magic = identify_magic(mb.getBuffer());
+ if (magic == file_magic::coff_import_library) {
+ InputFile *imp = make<ImportFile>(mb);
+ imp->parentName = parentName;
+ symtab->addFile(imp);
return;
}
- InputFile *Obj;
- if (Magic == file_magic::coff_object) {
- Obj = make<ObjFile>(MB);
- } else if (Magic == file_magic::bitcode) {
- Obj = make<BitcodeFile>(MB);
+ InputFile *obj;
+ if (magic == file_magic::coff_object) {
+ obj = make<ObjFile>(mb);
+ } else if (magic == file_magic::bitcode) {
+ obj = make<BitcodeFile>(mb, parentName, offsetInArchive);
} else {
- error("unknown file type: " + MB.getBufferIdentifier());
+ error("unknown file type: " + mb.getBufferIdentifier());
return;
}
- Obj->ParentName = ParentName;
- Symtab->addFile(Obj);
- log("Loaded " + toString(Obj) + " for " + SymName);
+ obj->parentName = parentName;
+ symtab->addFile(obj);
+ log("Loaded " + toString(obj) + " for " + symName);
}
-void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
- StringRef SymName,
- StringRef ParentName) {
- if (!C.getParent()->isThin()) {
- MemoryBufferRef MB = CHECK(
- C.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " + SymName);
- enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); });
+void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
+ StringRef symName,
+ StringRef parentName) {
+
+ auto reportBufferError = [=](Error &&e,
+ StringRef childName) {
+ fatal("could not get the buffer for the member defining symbol " +
+ symName + ": " + parentName + "(" + childName + "): " +
+ toString(std::move(e)));
+ };
+
+ if (!c.getParent()->isThin()) {
+ uint64_t offsetInArchive = c.getChildOffset();
+ Expected<MemoryBufferRef> mbOrErr = c.getMemoryBufferRef();
+ if (!mbOrErr)
+ reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
+ MemoryBufferRef mb = mbOrErr.get();
+ enqueueTask([=]() {
+ driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive);
+ });
return;
}
- auto Future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(
- CHECK(C.getFullName(),
- "could not get the filename for the member defining symbol " +
- SymName)));
+ std::string childName = CHECK(
+ c.getFullName(),
+ "could not get the filename for the member defining symbol " +
+ symName);
+ auto future = std::make_shared<std::future<MBErrPair>>(
+ createFutureForFile(childName));
enqueueTask([=]() {
- auto MBOrErr = Future->get();
- if (MBOrErr.second)
- fatal("could not get the buffer for the member defining " + SymName +
- ": " + MBOrErr.second.message());
- Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName,
- ParentName);
+ auto mbOrErr = future->get();
+ if (mbOrErr.second)
+ reportBufferError(errorCodeToError(mbOrErr.second), childName);
+ driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName,
+ parentName, /* OffsetInArchive */ 0);
});
}
-static bool isDecorated(StringRef Sym) {
- return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") ||
- (!Config->MinGW && Sym.contains('@'));
+static bool isDecorated(StringRef sym) {
+ return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") ||
+ (!config->mingw && sym.contains('@'));
}
// Parses .drectve section contents and returns a list of files
// specified by /defaultlib.
-void LinkerDriver::parseDirectives(StringRef S) {
- ArgParser Parser;
+void LinkerDriver::parseDirectives(InputFile *file) {
+ StringRef s = file->getDirectives();
+ if (s.empty())
+ return;
+
+ log("Directives: " + toString(file) + ": " + s);
+
+ 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);
+ opt::InputArgList args;
+ std::vector<StringRef> exports;
+ std::tie(args, exports) = parser.parseDirectives(s);
- for (StringRef E : Exports) {
+ for (StringRef e : 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,
// we dedup them first.
- if (!DirectivesExports.insert(E).second)
+ if (!directivesExports.insert(e).second)
continue;
- Export Exp = parseExport(E);
- if (Config->Machine == I386 && Config->MinGW) {
- if (!isDecorated(Exp.Name))
- Exp.Name = Saver.save("_" + Exp.Name);
- if (!Exp.ExtName.empty() && !isDecorated(Exp.ExtName))
- Exp.ExtName = Saver.save("_" + Exp.ExtName);
+ Export exp = parseExport(e);
+ if (config->machine == I386 && config->mingw) {
+ if (!isDecorated(exp.name))
+ exp.name = saver.save("_" + exp.name);
+ if (!exp.extName.empty() && !isDecorated(exp.extName))
+ exp.extName = saver.save("_" + exp.extName);
}
- Exp.Directives = true;
- Config->Exports.push_back(Exp);
+ exp.directives = true;
+ config->exports.push_back(exp);
}
- for (auto *Arg : Args) {
- switch (Arg->getOption().getUnaliasedOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_aligncomm:
- parseAligncomm(Arg->getValue());
+ parseAligncomm(arg->getValue());
break;
case OPT_alternatename:
- parseAlternateName(Arg->getValue());
+ parseAlternateName(arg->getValue());
break;
case OPT_defaultlib:
- if (Optional<StringRef> Path = findLib(Arg->getValue()))
- enqueuePath(*Path, false);
+ if (Optional<StringRef> path = findLib(arg->getValue()))
+ enqueuePath(*path, false);
break;
case OPT_entry:
- Config->Entry = addUndefined(mangle(Arg->getValue()));
+ config->entry = addUndefined(mangle(arg->getValue()));
break;
case OPT_failifmismatch:
- checkFailIfMismatch(Arg->getValue());
+ checkFailIfMismatch(arg->getValue(), file);
break;
case OPT_incl:
- addUndefined(Arg->getValue());
+ addUndefined(arg->getValue());
break;
case OPT_merge:
- parseMerge(Arg->getValue());
+ parseMerge(arg->getValue());
break;
case OPT_nodefaultlib:
- Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
+ config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
break;
case OPT_section:
- parseSection(Arg->getValue());
+ parseSection(arg->getValue());
break;
case OPT_subsystem:
- parseSubsystem(Arg->getValue(), &Config->Subsystem,
- &Config->MajorOSVersion, &Config->MinorOSVersion);
+ parseSubsystem(arg->getValue(), &config->subsystem,
+ &config->majorOSVersion, &config->minorOSVersion);
break;
+ // Only add flags here that link.exe accepts in
+ // `#pragma comment(linker, "/flag")`-generated sections.
case OPT_editandcontinue:
- case OPT_fastfail:
case OPT_guardsym:
- case OPT_natvis:
case OPT_throwingnew:
break;
default:
- error(Arg->getSpelling() + " is not allowed in .drectve");
+ error(arg->getSpelling() + " is not allowed in .drectve");
}
}
}
// Find file from search paths. You can omit ".obj", this function takes
// care of that. Note that the returned path is not guaranteed to exist.
-StringRef LinkerDriver::doFindFile(StringRef Filename) {
- bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
- if (HasPathSep)
- return Filename;
- bool HasExt = Filename.contains('.');
- for (StringRef Dir : SearchPaths) {
- SmallString<128> Path = Dir;
- sys::path::append(Path, Filename);
- if (sys::fs::exists(Path.str()))
- return Saver.save(Path.str());
- if (!HasExt) {
- Path.append(".obj");
- if (sys::fs::exists(Path.str()))
- return Saver.save(Path.str());
+StringRef LinkerDriver::doFindFile(StringRef filename) {
+ bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos);
+ if (hasPathSep)
+ return filename;
+ bool hasExt = filename.contains('.');
+ for (StringRef dir : searchPaths) {
+ SmallString<128> path = dir;
+ sys::path::append(path, filename);
+ if (sys::fs::exists(path.str()))
+ return saver.save(path.str());
+ if (!hasExt) {
+ path.append(".obj");
+ if (sys::fs::exists(path.str()))
+ return saver.save(path.str());
}
}
- return Filename;
+ return filename;
}
-static Optional<sys::fs::UniqueID> getUniqueID(StringRef Path) {
- sys::fs::UniqueID Ret;
- if (sys::fs::getUniqueID(Path, Ret))
+static Optional<sys::fs::UniqueID> getUniqueID(StringRef path) {
+ sys::fs::UniqueID ret;
+ if (sys::fs::getUniqueID(path, ret))
return None;
- return Ret;
+ return ret;
}
// Resolves a file path. This never returns the same path
// (in that case, it returns None).
-Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
- StringRef Path = doFindFile(Filename);
+Optional<StringRef> LinkerDriver::findFile(StringRef filename) {
+ StringRef path = doFindFile(filename);
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) {
- bool Seen = !VisitedFiles.insert(*ID).second;
- if (Seen)
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(path)) {
+ bool seen = !visitedFiles.insert(*id).second;
+ if (seen)
return None;
}
- if (Path.endswith_lower(".lib"))
- VisitedLibs.insert(sys::path::filename(Path));
- return Path;
+ if (path.endswith_lower(".lib"))
+ visitedLibs.insert(sys::path::filename(path));
+ return path;
}
// MinGW specific. If an embedded directive specified to link to
// foo.lib, but it isn't found, try libfoo.a instead.
-StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) {
- if (Filename.contains('/') || Filename.contains('\\'))
- return Filename;
-
- SmallString<128> S = Filename;
- sys::path::replace_extension(S, ".a");
- StringRef LibName = Saver.save("lib" + S.str());
- return doFindFile(LibName);
+StringRef LinkerDriver::doFindLibMinGW(StringRef filename) {
+ if (filename.contains('/') || filename.contains('\\'))
+ return filename;
+
+ SmallString<128> s = filename;
+ sys::path::replace_extension(s, ".a");
+ StringRef libName = saver.save("lib" + s.str());
+ return doFindFile(libName);
}
// Find library file from search path.
-StringRef LinkerDriver::doFindLib(StringRef Filename) {
+StringRef LinkerDriver::doFindLib(StringRef filename) {
// Add ".lib" to Filename if that has no file extension.
- bool HasExt = Filename.contains('.');
- if (!HasExt)
- Filename = Saver.save(Filename + ".lib");
- StringRef Ret = doFindFile(Filename);
+ bool hasExt = filename.contains('.');
+ if (!hasExt)
+ filename = saver.save(filename + ".lib");
+ StringRef ret = doFindFile(filename);
// For MinGW, if the find above didn't turn up anything, try
// looking for a MinGW formatted library name.
- if (Config->MinGW && Ret == Filename)
- return doFindLibMinGW(Filename);
- return Ret;
+ if (config->mingw && ret == filename)
+ return doFindLibMinGW(filename);
+ return ret;
}
// Resolves a library path. /nodefaultlib options are taken into
// consideration. This never returns the same path (in that case,
// it returns None).
-Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
- if (Config->NoDefaultLibAll)
+Optional<StringRef> LinkerDriver::findLib(StringRef filename) {
+ if (config->noDefaultLibAll)
return None;
- if (!VisitedLibs.insert(Filename.lower()).second)
+ if (!visitedLibs.insert(filename.lower()).second)
return None;
- StringRef Path = doFindLib(Filename);
- if (Config->NoDefaultLibs.count(Path))
+ StringRef path = doFindLib(filename);
+ if (config->noDefaultLibs.count(path.lower()))
return None;
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
- if (!VisitedFiles.insert(*ID).second)
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+ if (!visitedFiles.insert(*id).second)
return None;
- return Path;
+ return path;
}
// Parses LIB environment which contains a list of search paths.
void LinkerDriver::addLibSearchPaths() {
- Optional<std::string> EnvOpt = Process::GetEnv("LIB");
- if (!EnvOpt.hasValue())
+ Optional<std::string> envOpt = Process::GetEnv("LIB");
+ if (!envOpt.hasValue())
return;
- StringRef Env = Saver.save(*EnvOpt);
- while (!Env.empty()) {
- StringRef Path;
- std::tie(Path, Env) = Env.split(';');
- SearchPaths.push_back(Path);
+ StringRef env = saver.save(*envOpt);
+ while (!env.empty()) {
+ StringRef path;
+ std::tie(path, env) = env.split(';');
+ searchPaths.push_back(path);
}
}
-Symbol *LinkerDriver::addUndefined(StringRef Name) {
- Symbol *B = Symtab->addUndefined(Name);
- if (!B->IsGCRoot) {
- B->IsGCRoot = true;
- Config->GCRoot.push_back(B);
+Symbol *LinkerDriver::addUndefined(StringRef name) {
+ Symbol *b = symtab->addUndefined(name);
+ if (!b->isGCRoot) {
+ b->isGCRoot = true;
+ config->gcroot.push_back(b);
}
- return B;
+ return b;
+}
+
+StringRef LinkerDriver::mangleMaybe(Symbol *s) {
+ // If the plain symbol name has already been resolved, do nothing.
+ Undefined *unmangled = dyn_cast<Undefined>(s);
+ if (!unmangled)
+ return "";
+
+ // Otherwise, see if a similar, mangled symbol exists in the symbol table.
+ Symbol *mangled = symtab->findMangle(unmangled->getName());
+ if (!mangled)
+ return "";
+
+ // If we find a similar mangled symbol, make this an alias to it and return
+ // its name.
+ log(unmangled->getName() + " aliased to " + mangled->getName());
+ unmangled->weakAlias = symtab->addUndefined(mangled->getName());
+ return mangled->getName();
}
// Windows specific -- find default entry point name.
@@ -444,15 +526,15 @@ Symbol *LinkerDriver::addUndefined(StringRef Name) {
// each of which corresponds to a user-defined "main" function. This function
// infers an entry point from a user-defined "main" function.
StringRef LinkerDriver::findDefaultEntry() {
- assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
+ assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
"must handle /subsystem before calling this");
- if (Config->MinGW)
- return mangle(Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
+ if (config->mingw)
+ return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
? "WinMainCRTStartup"
: "mainCRTStartup");
- if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+ if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
if (findUnderscoreMangle("wWinMain")) {
if (!findUnderscoreMangle("WinMain"))
return mangle("wWinMainCRTStartup");
@@ -469,44 +551,44 @@ StringRef LinkerDriver::findDefaultEntry() {
}
WindowsSubsystem LinkerDriver::inferSubsystem() {
- if (Config->DLL)
+ if (config->dll)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
- if (Config->MinGW)
+ if (config->mingw)
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
// Note that link.exe infers the subsystem from the presence of these
// functions even if /entry: or /nodefaultlib are passed which causes them
// to not be called.
- bool HaveMain = findUnderscoreMangle("main");
- bool HaveWMain = findUnderscoreMangle("wmain");
- bool HaveWinMain = findUnderscoreMangle("WinMain");
- bool HaveWWinMain = findUnderscoreMangle("wWinMain");
- if (HaveMain || HaveWMain) {
- if (HaveWinMain || HaveWWinMain) {
- warn(std::string("found ") + (HaveMain ? "main" : "wmain") + " and " +
- (HaveWinMain ? "WinMain" : "wWinMain") +
+ bool haveMain = findUnderscoreMangle("main");
+ bool haveWMain = findUnderscoreMangle("wmain");
+ bool haveWinMain = findUnderscoreMangle("WinMain");
+ bool haveWWinMain = findUnderscoreMangle("wWinMain");
+ if (haveMain || haveWMain) {
+ if (haveWinMain || haveWWinMain) {
+ warn(std::string("found ") + (haveMain ? "main" : "wmain") + " and " +
+ (haveWinMain ? "WinMain" : "wWinMain") +
"; defaulting to /subsystem:console");
}
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
}
- if (HaveWinMain || HaveWWinMain)
+ if (haveWinMain || haveWWinMain)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
return IMAGE_SUBSYSTEM_UNKNOWN;
}
static uint64_t getDefaultImageBase() {
- if (Config->is64())
- return Config->DLL ? 0x180000000 : 0x140000000;
- return Config->DLL ? 0x10000000 : 0x400000;
+ if (config->is64())
+ return config->dll ? 0x180000000 : 0x140000000;
+ return config->dll ? 0x10000000 : 0x400000;
}
-static std::string createResponseFile(const opt::InputArgList &Args,
- ArrayRef<StringRef> FilePaths,
- ArrayRef<StringRef> SearchPaths) {
- SmallString<0> Data;
- raw_svector_ostream OS(Data);
+static std::string createResponseFile(const opt::InputArgList &args,
+ ArrayRef<StringRef> filePaths,
+ ArrayRef<StringRef> searchPaths) {
+ SmallString<0> data;
+ raw_svector_ostream os(data);
- for (auto *Arg : Args) {
- switch (Arg->getOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_linkrepro:
case OPT_INPUT:
case OPT_defaultlib:
@@ -518,32 +600,37 @@ static std::string createResponseFile(const opt::InputArgList &Args,
case OPT_manifestinput:
case OPT_manifestuac:
break;
+ case OPT_implib:
+ case OPT_pdb:
+ case OPT_out:
+ os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n";
+ break;
default:
- OS << toString(*Arg) << "\n";
+ os << toString(*arg) << "\n";
}
}
- for (StringRef Path : SearchPaths) {
- std::string RelPath = relativeToRoot(Path);
- OS << "/libpath:" << quote(RelPath) << "\n";
+ for (StringRef path : searchPaths) {
+ std::string relPath = relativeToRoot(path);
+ os << "/libpath:" << quote(relPath) << "\n";
}
- for (StringRef Path : FilePaths)
- OS << quote(relativeToRoot(Path)) << "\n";
+ for (StringRef path : filePaths)
+ os << quote(relativeToRoot(path)) << "\n";
- return Data.str();
+ return data.str();
}
enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
-static DebugKind parseDebugKind(const opt::InputArgList &Args) {
- auto *A = Args.getLastArg(OPT_debug, OPT_debug_opt);
- if (!A)
+static DebugKind parseDebugKind(const opt::InputArgList &args) {
+ auto *a = args.getLastArg(OPT_debug, OPT_debug_opt);
+ if (!a)
return DebugKind::None;
- if (A->getNumValues() == 0)
+ if (a->getNumValues() == 0)
return DebugKind::Full;
- DebugKind Debug = StringSwitch<DebugKind>(A->getValue())
+ DebugKind debug = StringSwitch<DebugKind>(a->getValue())
.CaseLower("none", DebugKind::None)
.CaseLower("full", DebugKind::Full)
.CaseLower("fastlink", DebugKind::FastLink)
@@ -553,67 +640,68 @@ static DebugKind parseDebugKind(const opt::InputArgList &Args) {
.CaseLower("symtab", DebugKind::Symtab)
.Default(DebugKind::Unknown);
- if (Debug == DebugKind::FastLink) {
+ if (debug == DebugKind::FastLink) {
warn("/debug:fastlink unsupported; using /debug:full");
return DebugKind::Full;
}
- if (Debug == DebugKind::Unknown) {
- error("/debug: unknown option: " + Twine(A->getValue()));
+ if (debug == DebugKind::Unknown) {
+ error("/debug: unknown option: " + Twine(a->getValue()));
return DebugKind::None;
}
- return Debug;
+ return debug;
}
-static unsigned parseDebugTypes(const opt::InputArgList &Args) {
- unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+static unsigned parseDebugTypes(const opt::InputArgList &args) {
+ unsigned debugTypes = static_cast<unsigned>(DebugType::None);
- if (auto *A = Args.getLastArg(OPT_debugtype)) {
- SmallVector<StringRef, 3> Types;
- A->getSpelling().split(Types, ',', /*KeepEmpty=*/false);
+ if (auto *a = args.getLastArg(OPT_debugtype)) {
+ SmallVector<StringRef, 3> types;
+ StringRef(a->getValue())
+ .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
- for (StringRef Type : Types) {
- unsigned V = StringSwitch<unsigned>(Type.lower())
+ for (StringRef type : types) {
+ unsigned v = StringSwitch<unsigned>(type.lower())
.Case("cv", static_cast<unsigned>(DebugType::CV))
.Case("pdata", static_cast<unsigned>(DebugType::PData))
.Case("fixup", static_cast<unsigned>(DebugType::Fixup))
.Default(0);
- if (V == 0) {
- warn("/debugtype: unknown option: " + Twine(A->getValue()));
+ if (v == 0) {
+ warn("/debugtype: unknown option '" + type + "'");
continue;
}
- DebugTypes |= V;
+ debugTypes |= v;
}
- return DebugTypes;
+ return debugTypes;
}
// Default debug types
- DebugTypes = static_cast<unsigned>(DebugType::CV);
- if (Args.hasArg(OPT_driver))
- DebugTypes |= static_cast<unsigned>(DebugType::PData);
- if (Args.hasArg(OPT_profile))
- DebugTypes |= static_cast<unsigned>(DebugType::Fixup);
+ debugTypes = static_cast<unsigned>(DebugType::CV);
+ if (args.hasArg(OPT_driver))
+ debugTypes |= static_cast<unsigned>(DebugType::PData);
+ if (args.hasArg(OPT_profile))
+ debugTypes |= static_cast<unsigned>(DebugType::Fixup);
- return DebugTypes;
+ return debugTypes;
}
-static std::string getMapFile(const opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file);
- if (!Arg)
+static std::string getMapFile(const opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+ if (!arg)
return "";
- if (Arg->getOption().getID() == OPT_lldmap_file)
- return Arg->getValue();
+ if (arg->getOption().getID() == OPT_lldmap_file)
+ return arg->getValue();
- assert(Arg->getOption().getID() == OPT_lldmap);
- StringRef OutFile = Config->OutputFile;
- return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str();
+ assert(arg->getOption().getID() == OPT_lldmap);
+ StringRef outFile = config->outputFile;
+ return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
static std::string getImplibPath() {
- if (!Config->Implib.empty())
- return Config->Implib;
- SmallString<128> Out = StringRef(Config->OutputFile);
- sys::path::replace_extension(Out, ".lib");
- return Out.str();
+ if (!config->implib.empty())
+ return config->implib;
+ SmallString<128> out = StringRef(config->outputFile);
+ sys::path::replace_extension(out, ".lib");
+ return out.str();
}
//
@@ -624,289 +712,346 @@ static std::string getImplibPath() {
// LINK | {value} | {value}.{.dll/.exe} | {output name}
// LIB | {value} | {value}.dll | {output name}.dll
//
-static std::string getImportName(bool AsLib) {
- SmallString<128> Out;
+static std::string getImportName(bool asLib) {
+ SmallString<128> out;
- if (Config->ImportName.empty()) {
- Out.assign(sys::path::filename(Config->OutputFile));
- if (AsLib)
- sys::path::replace_extension(Out, ".dll");
+ if (config->importName.empty()) {
+ out.assign(sys::path::filename(config->outputFile));
+ if (asLib)
+ sys::path::replace_extension(out, ".dll");
} else {
- Out.assign(Config->ImportName);
- if (!sys::path::has_extension(Out))
- sys::path::replace_extension(Out,
- (Config->DLL || AsLib) ? ".dll" : ".exe");
+ out.assign(config->importName);
+ if (!sys::path::has_extension(out))
+ sys::path::replace_extension(out,
+ (config->dll || asLib) ? ".dll" : ".exe");
}
- return Out.str();
+ return 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.Ordinal = E1.Ordinal;
- E2.Noname = E1.Noname;
- E2.Data = E1.Data;
- E2.Private = E1.Private;
- E2.Constant = E1.Constant;
- Exports.push_back(E2);
- }
-
- auto HandleError = [](Error &&E) {
- handleAllErrors(std::move(E),
- [](ErrorInfoBase &EIB) { error(EIB.message()); });
+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.Ordinal = e1.ordinal;
+ e2.Noname = e1.noname;
+ e2.Data = e1.data;
+ e2.Private = e1.isPrivate;
+ e2.Constant = e1.constant;
+ exports.push_back(e2);
+ }
+
+ auto handleError = [](Error &&e) {
+ handleAllErrors(std::move(e),
+ [](ErrorInfoBase &eib) { error(eib.message()); });
};
- std::string LibName = getImportName(AsLib);
- std::string Path = getImplibPath();
+ std::string libName = getImportName(asLib);
+ std::string path = getImplibPath();
- if (!Config->Incremental) {
- HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine,
- Config->MinGW));
+ if (!config->incremental) {
+ handleError(writeImportLibrary(libName, path, exports, config->machine,
+ config->mingw));
return;
}
// If the import library already exists, replace it only if the contents
// have changed.
- ErrorOr<std::unique_ptr<MemoryBuffer>> OldBuf = MemoryBuffer::getFile(
- Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false);
- if (!OldBuf) {
- HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine,
- Config->MinGW));
+ ErrorOr<std::unique_ptr<MemoryBuffer>> oldBuf = MemoryBuffer::getFile(
+ path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false);
+ if (!oldBuf) {
+ handleError(writeImportLibrary(libName, path, exports, config->machine,
+ config->mingw));
return;
}
- SmallString<128> TmpName;
- if (std::error_code EC =
- sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName))
- fatal("cannot create temporary file for import library " + Path + ": " +
- EC.message());
+ SmallString<128> tmpName;
+ if (std::error_code ec =
+ sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName))
+ fatal("cannot create temporary file for import library " + path + ": " +
+ ec.message());
- if (Error E = writeImportLibrary(LibName, TmpName, Exports, Config->Machine,
- Config->MinGW)) {
- HandleError(std::move(E));
+ if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine,
+ config->mingw)) {
+ handleError(std::move(e));
return;
}
- std::unique_ptr<MemoryBuffer> NewBuf = check(MemoryBuffer::getFile(
- TmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false));
- if ((*OldBuf)->getBuffer() != NewBuf->getBuffer()) {
- OldBuf->reset();
- HandleError(errorCodeToError(sys::fs::rename(TmpName, Path)));
+ std::unique_ptr<MemoryBuffer> newBuf = check(MemoryBuffer::getFile(
+ tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false));
+ if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) {
+ oldBuf->reset();
+ handleError(errorCodeToError(sys::fs::rename(tmpName, path)));
} else {
- sys::fs::remove(TmpName);
+ sys::fs::remove(tmpName);
}
}
-static void parseModuleDefs(StringRef Path) {
- std::unique_ptr<MemoryBuffer> MB = CHECK(
- MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
- COFFModuleDefinition M = check(parseCOFFModuleDefinition(
- MB->getMemBufferRef(), Config->Machine, Config->MinGW));
-
- if (Config->OutputFile.empty())
- Config->OutputFile = Saver.save(M.OutputFile);
- Config->ImportName = Saver.save(M.ImportName);
- if (M.ImageBase)
- Config->ImageBase = M.ImageBase;
- if (M.StackReserve)
- Config->StackReserve = M.StackReserve;
- if (M.StackCommit)
- Config->StackCommit = M.StackCommit;
- if (M.HeapReserve)
- Config->HeapReserve = M.HeapReserve;
- if (M.HeapCommit)
- Config->HeapCommit = M.HeapCommit;
- if (M.MajorImageVersion)
- Config->MajorImageVersion = M.MajorImageVersion;
- if (M.MinorImageVersion)
- Config->MinorImageVersion = M.MinorImageVersion;
- if (M.MajorOSVersion)
- Config->MajorOSVersion = M.MajorOSVersion;
- if (M.MinorOSVersion)
- Config->MinorOSVersion = M.MinorOSVersion;
-
- for (COFFShortExport E1 : M.Exports) {
- Export E2;
+static void parseModuleDefs(StringRef path) {
+ std::unique_ptr<MemoryBuffer> mb = CHECK(
+ MemoryBuffer::getFile(path, -1, false, true), "could not open " + path);
+ COFFModuleDefinition m = check(parseCOFFModuleDefinition(
+ mb->getMemBufferRef(), config->machine, config->mingw));
+
+ if (config->outputFile.empty())
+ config->outputFile = saver.save(m.OutputFile);
+ config->importName = saver.save(m.ImportName);
+ if (m.ImageBase)
+ config->imageBase = m.ImageBase;
+ if (m.StackReserve)
+ config->stackReserve = m.StackReserve;
+ if (m.StackCommit)
+ config->stackCommit = m.StackCommit;
+ if (m.HeapReserve)
+ config->heapReserve = m.HeapReserve;
+ if (m.HeapCommit)
+ config->heapCommit = m.HeapCommit;
+ if (m.MajorImageVersion)
+ config->majorImageVersion = m.MajorImageVersion;
+ if (m.MinorImageVersion)
+ config->minorImageVersion = m.MinorImageVersion;
+ if (m.MajorOSVersion)
+ config->majorOSVersion = m.MajorOSVersion;
+ if (m.MinorOSVersion)
+ config->minorOSVersion = m.MinorOSVersion;
+
+ for (COFFShortExport e1 : m.Exports) {
+ Export e2;
// In simple cases, only Name is set. Renamed exports are parsed
// 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('.')) {
- E2.Name = Saver.save(E1.ExtName);
- E2.ForwardTo = Saver.save(E1.Name);
- Config->Exports.push_back(E2);
+ if (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);
continue;
}
- E2.Name = Saver.save(E1.Name);
- E2.ExtName = Saver.save(E1.ExtName);
- E2.Ordinal = E1.Ordinal;
- E2.Noname = E1.Noname;
- E2.Data = E1.Data;
- E2.Private = E1.Private;
- E2.Constant = E1.Constant;
- Config->Exports.push_back(E2);
+ e2.name = saver.save(e1.Name);
+ e2.extName = saver.save(e1.ExtName);
+ e2.ordinal = e1.Ordinal;
+ e2.noname = e1.Noname;
+ e2.data = e1.Data;
+ e2.isPrivate = e1.Private;
+ e2.constant = e1.Constant;
+ config->exports.push_back(e2);
}
}
-void LinkerDriver::enqueueTask(std::function<void()> Task) {
- TaskQueue.push_back(std::move(Task));
+void LinkerDriver::enqueueTask(std::function<void()> task) {
+ taskQueue.push_back(std::move(task));
}
bool LinkerDriver::run() {
- ScopedTimer T(InputFileTimer);
+ ScopedTimer t(inputFileTimer);
- bool DidWork = !TaskQueue.empty();
- while (!TaskQueue.empty()) {
- TaskQueue.front()();
- TaskQueue.pop_front();
+ bool didWork = !taskQueue.empty();
+ while (!taskQueue.empty()) {
+ taskQueue.front()();
+ taskQueue.pop_front();
}
- return DidWork;
+ return didWork;
}
// Parse an /order file. If an option is given, the linker places
// COMDAT sections in the same order as their names appear in the
// given file.
-static void parseOrderFile(StringRef Arg) {
+static void parseOrderFile(StringRef arg) {
// For some reason, the MSVC linker requires a filename to be
// preceded by "@".
- if (!Arg.startswith("@")) {
+ if (!arg.startswith("@")) {
error("malformed /order option: '@' missing");
return;
}
// Get a list of all comdat sections for error checking.
- DenseSet<StringRef> Set;
- for (Chunk *C : Symtab->getChunks())
- if (auto *Sec = dyn_cast<SectionChunk>(C))
- if (Sec->Sym)
- Set.insert(Sec->Sym->getName());
+ DenseSet<StringRef> set;
+ for (Chunk *c : symtab->getChunks())
+ if (auto *sec = dyn_cast<SectionChunk>(c))
+ if (sec->sym)
+ set.insert(sec->sym->getName());
// Open a file.
- StringRef Path = Arg.substr(1);
- std::unique_ptr<MemoryBuffer> MB = CHECK(
- MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
+ StringRef path = arg.substr(1);
+ std::unique_ptr<MemoryBuffer> mb = CHECK(
+ MemoryBuffer::getFile(path, -1, false, true), "could not open " + path);
// Parse a file. An order file contains one symbol per line.
// 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())) {
- if (Config->Machine == I386 && !isDecorated(S))
- S = "_" + S;
+ for (std::string s : args::getLines(mb->getMemBufferRef())) {
+ if (config->machine == I386 && !isDecorated(s))
+ s = "_" + s;
- if (Set.count(S) == 0) {
- if (Config->WarnMissingOrderSymbol)
- warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]");
+ if (set.count(s) == 0) {
+ if (config->warnMissingOrderSymbol)
+ warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]");
}
else
- Config->Order[S] = INT_MIN + Config->Order.size();
+ config->order[s] = INT_MIN + config->order.size();
}
}
-static void markAddrsig(Symbol *S) {
- if (auto *D = dyn_cast_or_null<Defined>(S))
- if (Chunk *C = D->getChunk())
- C->KeepUnique = true;
+static void markAddrsig(Symbol *s) {
+ if (auto *d = dyn_cast_or_null<Defined>(s))
+ if (SectionChunk *c = dyn_cast_or_null<SectionChunk>(d->getChunk()))
+ c->keepUnique = true;
}
static void findKeepUniqueSections() {
// Exported symbols could be address-significant in other executables or DSOs,
// so we conservatively mark them as address-significant.
- for (Export &R : Config->Exports)
- markAddrsig(R.Sym);
+ for (Export &r : config->exports)
+ markAddrsig(r.sym);
// Visit the address-significance table in each object file and mark each
// referenced symbol as address-significant.
- for (ObjFile *Obj : ObjFile::Instances) {
- ArrayRef<Symbol *> Syms = Obj->getSymbols();
- if (Obj->AddrsigSec) {
- ArrayRef<uint8_t> Contents;
- Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents);
- const uint8_t *Cur = Contents.begin();
- while (Cur != Contents.end()) {
- unsigned Size;
- const char *Err;
- uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
- if (Err)
- fatal(toString(Obj) + ": could not decode addrsig section: " + Err);
- if (SymIndex >= Syms.size())
- fatal(toString(Obj) + ": invalid symbol index in addrsig section");
- markAddrsig(Syms[SymIndex]);
- Cur += Size;
+ for (ObjFile *obj : ObjFile::instances) {
+ ArrayRef<Symbol *> syms = obj->getSymbols();
+ if (obj->addrsigSec) {
+ ArrayRef<uint8_t> contents;
+ cantFail(
+ obj->getCOFFObj()->getSectionContents(obj->addrsigSec, contents));
+ const uint8_t *cur = contents.begin();
+ while (cur != contents.end()) {
+ unsigned size;
+ const char *err;
+ uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err);
+ if (err)
+ fatal(toString(obj) + ": could not decode addrsig section: " + err);
+ if (symIndex >= syms.size())
+ fatal(toString(obj) + ": invalid symbol index in addrsig section");
+ markAddrsig(syms[symIndex]);
+ cur += size;
}
} else {
// If an object file does not have an address-significance table,
// conservatively mark all of its symbols as address-significant.
- for (Symbol *S : Syms)
- markAddrsig(S);
+ for (Symbol *s : syms)
+ markAddrsig(s);
}
}
}
-// link.exe replaces each %foo% in AltPath with the contents of environment
+// link.exe replaces each %foo% in altPath with the contents of environment
// variable foo, and adds the two magic env vars _PDB (expands to the basename
// of pdb's output path) and _EXT (expands to the extension of the output
// binary).
// lld only supports %_PDB% and %_EXT% and warns on references to all other env
// vars.
-static void parsePDBAltPath(StringRef AltPath) {
- SmallString<128> Buf;
- StringRef PDBBasename =
- sys::path::filename(Config->PDBPath, sys::path::Style::windows);
- StringRef BinaryExtension =
- sys::path::extension(Config->OutputFile, sys::path::Style::windows);
- if (!BinaryExtension.empty())
- BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'.
+static void parsePDBAltPath(StringRef altPath) {
+ SmallString<128> buf;
+ StringRef pdbBasename =
+ sys::path::filename(config->pdbPath, sys::path::Style::windows);
+ StringRef binaryExtension =
+ sys::path::extension(config->outputFile, sys::path::Style::windows);
+ if (!binaryExtension.empty())
+ binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'.
// Invariant:
- // +--------- Cursor ('a...' might be the empty string).
- // | +----- FirstMark
- // | | +- SecondMark
+ // +--------- cursor ('a...' might be the empty string).
+ // | +----- firstMark
+ // | | +- secondMark
// v v v
// a...%...%...
- size_t Cursor = 0;
- while (Cursor < AltPath.size()) {
- size_t FirstMark, SecondMark;
- if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos ||
- (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) {
+ size_t cursor = 0;
+ while (cursor < altPath.size()) {
+ size_t firstMark, secondMark;
+ if ((firstMark = altPath.find('%', cursor)) == StringRef::npos ||
+ (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) {
// Didn't find another full fragment, treat rest of string as literal.
- Buf.append(AltPath.substr(Cursor));
+ buf.append(altPath.substr(cursor));
break;
}
// Found a full fragment. Append text in front of first %, and interpret
// text between first and second % as variable name.
- Buf.append(AltPath.substr(Cursor, FirstMark - Cursor));
- StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1);
- if (Var.equals_lower("%_pdb%"))
- Buf.append(PDBBasename);
- else if (Var.equals_lower("%_ext%"))
- Buf.append(BinaryExtension);
+ buf.append(altPath.substr(cursor, firstMark - cursor));
+ StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1);
+ if (var.equals_lower("%_pdb%"))
+ buf.append(pdbBasename);
+ else if (var.equals_lower("%_ext%"))
+ buf.append(binaryExtension);
else {
warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " +
- Var + " as literal");
- Buf.append(Var);
+ var + " as literal");
+ buf.append(var);
}
- Cursor = SecondMark + 1;
+ cursor = secondMark + 1;
}
- Config->PDBAltPath = Buf;
+ config->pdbAltPath = buf;
}
-void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
- // If the first command line argument is "/lib", link.exe acts like lib.exe.
- // We call our own implementation of lib.exe that understands bitcode files.
- if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) {
- if (llvm::libDriverMain(ArgsArr.slice(1)) != 0)
- fatal("lib failed");
+/// Check that at most one resource obj file was used.
+/// Call after ObjFile::Instances is complete.
+static void diagnoseMultipleResourceObjFiles() {
+ // The .rsrc$01 section in a resource obj file contains a tree description
+ // of resources. Merging multiple resource obj files would require merging
+ // the trees instead of using usual linker section merging semantics.
+ // Since link.exe disallows linking more than one resource obj file with
+ // LNK4078, mirror that. The normal use of resource files is to give the
+ // linker many .res files, which are then converted to a single resource obj
+ // file internally, so this is not a big restriction in practice.
+ ObjFile *resourceObjFile = nullptr;
+ for (ObjFile *f : ObjFile::instances) {
+ if (!f->isResourceObjFile)
+ continue;
+
+ if (!resourceObjFile) {
+ resourceObjFile = f;
+ continue;
+ }
+
+ error(toString(f) +
+ ": more than one resource obj file not allowed, already got " +
+ toString(resourceObjFile));
+ }
+}
+
+// In MinGW, if no symbols are chosen to be exported, then all symbols are
+// automatically exported by default. This behavior can be forced by the
+// -export-all-symbols option, so that it happens even when exports are
+// explicitly specified. The automatic behavior can be disabled using the
+// -exclude-all-symbols option, so that lld-link behaves like link.exe rather
+// than MinGW in the case that nothing is explicitly exported.
+void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
+ if (!config->dll)
return;
+
+ if (!args.hasArg(OPT_export_all_symbols)) {
+ if (!config->exports.empty())
+ return;
+ if (args.hasArg(OPT_exclude_all_symbols))
+ return;
}
+ AutoExporter exporter;
+
+ for (auto *arg : args.filtered(OPT_wholearchive_file))
+ if (Optional<StringRef> path = doFindFile(arg->getValue()))
+ exporter.addWholeArchive(*path);
+
+ symtab->forEachSymbol([&](Symbol *s) {
+ auto *def = dyn_cast<Defined>(s);
+ if (!exporter.shouldExport(def))
+ return;
+
+ Export e;
+ e.name = def->getName();
+ e.sym = def;
+ if (Chunk *c = def->getChunk())
+ if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
+ e.data = true;
+ config->exports.push_back(e);
+ });
+}
+
+void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Needed for LTO.
InitializeAllTargetInfos();
InitializeAllTargets();
@@ -914,279 +1059,308 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
InitializeAllAsmParsers();
InitializeAllAsmPrinters();
+ // If the first command line argument is "/lib", link.exe acts like lib.exe.
+ // We call our own implementation of lib.exe that understands bitcode files.
+ if (argsArr.size() > 1 && StringRef(argsArr[1]).equals_lower("/lib")) {
+ if (llvm::libDriverMain(argsArr.slice(1)) != 0)
+ fatal("lib failed");
+ return;
+ }
+
// Parse command line options.
- ArgParser Parser;
- opt::InputArgList Args = Parser.parseLINK(ArgsArr);
+ ArgParser parser;
+ opt::InputArgList args = parser.parseLINK(argsArr);
// Parse and evaluate -mllvm options.
- std::vector<const char *> V;
- V.push_back("lld-link (LLVM option parsing)");
- for (auto *Arg : Args.filtered(OPT_mllvm))
- V.push_back(Arg->getValue());
- cl::ParseCommandLineOptions(V.size(), V.data());
+ std::vector<const char *> v;
+ v.push_back("lld-link (LLVM option parsing)");
+ for (auto *arg : args.filtered(OPT_mllvm))
+ v.push_back(arg->getValue());
+ cl::ParseCommandLineOptions(v.size(), v.data());
// Handle /errorlimit early, because error() depends on it.
- if (auto *Arg = Args.getLastArg(OPT_errorlimit)) {
- int N = 20;
- StringRef S = Arg->getValue();
- if (S.getAsInteger(10, N))
- error(Arg->getSpelling() + " number expected, but got " + S);
- errorHandler().ErrorLimit = N;
+ if (auto *arg = args.getLastArg(OPT_errorlimit)) {
+ int n = 20;
+ StringRef s = arg->getValue();
+ if (s.getAsInteger(10, n))
+ error(arg->getSpelling() + " number expected, but got " + s);
+ errorHandler().errorLimit = n;
}
// Handle /help
- if (Args.hasArg(OPT_help)) {
- printHelp(ArgsArr[0]);
+ if (args.hasArg(OPT_help)) {
+ printHelp(argsArr[0]);
return;
}
- if (Args.hasArg(OPT_show_timing))
- Config->ShowTiming = true;
+ lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true);
+
+ if (args.hasArg(OPT_show_timing))
+ config->showTiming = true;
- ScopedTimer T(Timer::root());
+ 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.
- if (Args.hasArg(OPT_dash_dash_version)) {
+ if (args.hasArg(OPT_dash_dash_version)) {
outs() << getLLDVersion() << "\n";
return;
}
// Handle /lldmingw early, since it can potentially affect how other
// options are handled.
- Config->MinGW = Args.hasArg(OPT_lldmingw);
+ config->mingw = args.hasArg(OPT_lldmingw);
- if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
- SmallString<64> Path = StringRef(Arg->getValue());
- sys::path::append(Path, "repro.tar");
+ if (auto *arg = args.getLastArg(OPT_linkrepro)) {
+ SmallString<64> path = StringRef(arg->getValue());
+ sys::path::append(path, "repro.tar");
- Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
- TarWriter::create(Path, "repro");
+ Expected<std::unique_ptr<TarWriter>> errOrWriter =
+ TarWriter::create(path, "repro");
- if (ErrOrWriter) {
- Tar = std::move(*ErrOrWriter);
+ if (errOrWriter) {
+ tar = std::move(*errOrWriter);
} else {
- error("/linkrepro: failed to open " + Path + ": " +
- toString(ErrOrWriter.takeError()));
+ error("/linkrepro: failed to open " + path + ": " +
+ toString(errOrWriter.takeError()));
}
}
- if (!Args.hasArg(OPT_INPUT)) {
- if (Args.hasArg(OPT_deffile))
- Config->NoEntry = true;
+ if (!args.hasArg(OPT_INPUT)) {
+ if (args.hasArg(OPT_deffile))
+ config->noEntry = true;
else
fatal("no input files");
}
// Construct search path list.
- SearchPaths.push_back("");
- for (auto *Arg : Args.filtered(OPT_libpath))
- SearchPaths.push_back(Arg->getValue());
+ searchPaths.push_back("");
+ for (auto *arg : args.filtered(OPT_libpath))
+ searchPaths.push_back(arg->getValue());
addLibSearchPaths();
// Handle /ignore
- for (auto *Arg : Args.filtered(OPT_ignore)) {
- SmallVector<StringRef, 8> Vec;
- StringRef(Arg->getValue()).split(Vec, ',');
- for (StringRef S : Vec) {
- if (S == "4037")
- Config->WarnMissingOrderSymbol = false;
- else if (S == "4099")
- Config->WarnDebugInfoUnusable = false;
- else if (S == "4217")
- Config->WarnLocallyDefinedImported = false;
+ for (auto *arg : args.filtered(OPT_ignore)) {
+ SmallVector<StringRef, 8> vec;
+ StringRef(arg->getValue()).split(vec, ',');
+ for (StringRef s : vec) {
+ if (s == "4037")
+ config->warnMissingOrderSymbol = false;
+ else if (s == "4099")
+ config->warnDebugInfoUnusable = false;
+ else if (s == "4217")
+ config->warnLocallyDefinedImported = false;
// Other warning numbers are ignored.
}
}
// Handle /out
- if (auto *Arg = Args.getLastArg(OPT_out))
- Config->OutputFile = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_out))
+ config->outputFile = arg->getValue();
// Handle /verbose
- if (Args.hasArg(OPT_verbose))
- Config->Verbose = true;
- errorHandler().Verbose = Config->Verbose;
+ if (args.hasArg(OPT_verbose))
+ config->verbose = true;
+ errorHandler().verbose = config->verbose;
// Handle /force or /force:unresolved
- if (Args.hasArg(OPT_force, OPT_force_unresolved))
- Config->ForceUnresolved = true;
+ if (args.hasArg(OPT_force, OPT_force_unresolved))
+ config->forceUnresolved = true;
// Handle /force or /force:multiple
- if (Args.hasArg(OPT_force, OPT_force_multiple))
- Config->ForceMultiple = true;
+ if (args.hasArg(OPT_force, OPT_force_multiple))
+ config->forceMultiple = true;
+
+ // Handle /force or /force:multipleres
+ if (args.hasArg(OPT_force, OPT_force_multipleres))
+ config->forceMultipleRes = true;
// Handle /debug
- DebugKind Debug = parseDebugKind(Args);
- if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf ||
- Debug == DebugKind::GHash) {
- Config->Debug = true;
- Config->Incremental = true;
+ DebugKind debug = parseDebugKind(args);
+ if (debug == DebugKind::Full || debug == DebugKind::Dwarf ||
+ debug == DebugKind::GHash) {
+ config->debug = true;
+ config->incremental = true;
}
+ // Handle /demangle
+ config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no);
+
// Handle /debugtype
- Config->DebugTypes = parseDebugTypes(Args);
+ config->debugTypes = parseDebugTypes(args);
// Handle /pdb
- bool ShouldCreatePDB =
- (Debug == DebugKind::Full || Debug == DebugKind::GHash);
- if (ShouldCreatePDB) {
- if (auto *Arg = Args.getLastArg(OPT_pdb))
- Config->PDBPath = Arg->getValue();
- if (auto *Arg = Args.getLastArg(OPT_pdbaltpath))
- Config->PDBAltPath = Arg->getValue();
- if (Args.hasArg(OPT_natvis))
- Config->NatvisFiles = Args.getAllArgValues(OPT_natvis);
+ bool shouldCreatePDB =
+ (debug == DebugKind::Full || debug == DebugKind::GHash);
+ if (shouldCreatePDB) {
+ if (auto *arg = args.getLastArg(OPT_pdb))
+ config->pdbPath = arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_pdbaltpath))
+ config->pdbAltPath = arg->getValue();
+ if (args.hasArg(OPT_natvis))
+ config->natvisFiles = args.getAllArgValues(OPT_natvis);
- if (auto *Arg = Args.getLastArg(OPT_pdb_source_path))
- Config->PDBSourcePath = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_pdb_source_path))
+ config->pdbSourcePath = arg->getValue();
}
// Handle /noentry
- if (Args.hasArg(OPT_noentry)) {
- if (Args.hasArg(OPT_dll))
- Config->NoEntry = true;
+ if (args.hasArg(OPT_noentry)) {
+ if (args.hasArg(OPT_dll))
+ config->noEntry = true;
else
error("/noentry must be specified with /dll");
}
// Handle /dll
- if (Args.hasArg(OPT_dll)) {
- Config->DLL = true;
- Config->ManifestID = 2;
+ if (args.hasArg(OPT_dll)) {
+ config->dll = true;
+ config->manifestID = 2;
}
// Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase
// because we need to explicitly check whether that option or its inverse was
// present in the argument list in order to handle /fixed.
- auto *DynamicBaseArg = Args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
- if (DynamicBaseArg &&
- DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
- Config->DynamicBase = false;
+ auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
+ if (dynamicBaseArg &&
+ dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
+ config->dynamicBase = false;
// MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the
// default setting for any other project type.", but link.exe defaults to
// /FIXED:NO for exe outputs as well. Match behavior, not docs.
- bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false);
- if (Fixed) {
- if (DynamicBaseArg &&
- DynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
+ bool fixed = args.hasFlag(OPT_fixed, OPT_fixed_no, false);
+ if (fixed) {
+ if (dynamicBaseArg &&
+ dynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
error("/fixed must not be specified with /dynamicbase");
} else {
- Config->Relocatable = false;
- Config->DynamicBase = false;
+ config->relocatable = false;
+ config->dynamicBase = false;
}
}
// Handle /appcontainer
- Config->AppContainer =
- Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);
+ config->appContainer =
+ args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);
// Handle /machine
- if (auto *Arg = Args.getLastArg(OPT_machine))
- Config->Machine = getMachineType(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_machine)) {
+ config->machine = getMachineType(arg->getValue());
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN)
+ fatal(Twine("unknown /machine argument: ") + arg->getValue());
+ }
// Handle /nodefaultlib:<filename>
- for (auto *Arg : Args.filtered(OPT_nodefaultlib))
- Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
+ for (auto *arg : args.filtered(OPT_nodefaultlib))
+ config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
// Handle /nodefaultlib
- if (Args.hasArg(OPT_nodefaultlib_all))
- Config->NoDefaultLibAll = true;
+ if (args.hasArg(OPT_nodefaultlib_all))
+ config->noDefaultLibAll = true;
// Handle /base
- if (auto *Arg = Args.getLastArg(OPT_base))
- parseNumbers(Arg->getValue(), &Config->ImageBase);
+ if (auto *arg = args.getLastArg(OPT_base))
+ parseNumbers(arg->getValue(), &config->imageBase);
+
+ // Handle /filealign
+ if (auto *arg = args.getLastArg(OPT_filealign)) {
+ parseNumbers(arg->getValue(), &config->fileAlign);
+ if (!isPowerOf2_64(config->fileAlign))
+ error("/filealign: not a power of two: " + Twine(config->fileAlign));
+ }
// Handle /stack
- if (auto *Arg = Args.getLastArg(OPT_stack))
- parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit);
+ if (auto *arg = args.getLastArg(OPT_stack))
+ parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit);
// Handle /guard:cf
- if (auto *Arg = Args.getLastArg(OPT_guard))
- parseGuard(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_guard))
+ parseGuard(arg->getValue());
// Handle /heap
- if (auto *Arg = Args.getLastArg(OPT_heap))
- parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit);
+ if (auto *arg = args.getLastArg(OPT_heap))
+ parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit);
// Handle /version
- if (auto *Arg = Args.getLastArg(OPT_version))
- parseVersion(Arg->getValue(), &Config->MajorImageVersion,
- &Config->MinorImageVersion);
+ if (auto *arg = args.getLastArg(OPT_version))
+ parseVersion(arg->getValue(), &config->majorImageVersion,
+ &config->minorImageVersion);
// Handle /subsystem
- if (auto *Arg = Args.getLastArg(OPT_subsystem))
- parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion,
- &Config->MinorOSVersion);
+ if (auto *arg = args.getLastArg(OPT_subsystem))
+ parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion,
+ &config->minorOSVersion);
// Handle /timestamp
- if (llvm::opt::Arg *Arg = Args.getLastArg(OPT_timestamp, OPT_repro)) {
- if (Arg->getOption().getID() == OPT_repro) {
- Config->Timestamp = 0;
- Config->Repro = true;
+ if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) {
+ if (arg->getOption().getID() == OPT_repro) {
+ config->timestamp = 0;
+ config->repro = true;
} else {
- Config->Repro = false;
- StringRef Value(Arg->getValue());
- if (Value.getAsInteger(0, Config->Timestamp))
- fatal(Twine("invalid timestamp: ") + Value +
+ config->repro = false;
+ StringRef value(arg->getValue());
+ if (value.getAsInteger(0, config->timestamp))
+ fatal(Twine("invalid timestamp: ") + value +
". Expected 32-bit integer");
}
} else {
- Config->Repro = false;
- Config->Timestamp = time(nullptr);
+ config->repro = false;
+ config->timestamp = time(nullptr);
}
// Handle /alternatename
- for (auto *Arg : Args.filtered(OPT_alternatename))
- parseAlternateName(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_alternatename))
+ parseAlternateName(arg->getValue());
// Handle /include
- for (auto *Arg : Args.filtered(OPT_incl))
- addUndefined(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_incl))
+ addUndefined(arg->getValue());
// Handle /implib
- if (auto *Arg = Args.getLastArg(OPT_implib))
- Config->Implib = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_implib))
+ config->implib = arg->getValue();
// Handle /opt.
- bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile);
- unsigned ICFLevel =
- Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
- unsigned TailMerge = 1;
- for (auto *Arg : Args.filtered(OPT_opt)) {
- std::string Str = StringRef(Arg->getValue()).lower();
- SmallVector<StringRef, 1> Vec;
- StringRef(Str).split(Vec, ',');
- for (StringRef S : Vec) {
- if (S == "ref") {
- DoGC = true;
- } else if (S == "noref") {
- DoGC = false;
- } else if (S == "icf" || S.startswith("icf=")) {
- ICFLevel = 2;
- } else if (S == "noicf") {
- ICFLevel = 0;
- } else if (S == "lldtailmerge") {
- TailMerge = 2;
- } else if (S == "nolldtailmerge") {
- TailMerge = 0;
- } else if (S.startswith("lldlto=")) {
- StringRef OptLevel = S.substr(7);
- if (OptLevel.getAsInteger(10, Config->LTOO) || Config->LTOO > 3)
- 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)
- error("/opt:lldltojobs: invalid job count: " + Jobs);
- } else if (S.startswith("lldltopartitions=")) {
- StringRef N = S.substr(17);
- if (N.getAsInteger(10, Config->LTOPartitions) ||
- Config->LTOPartitions == 0)
- error("/opt:lldltopartitions: invalid partition count: " + N);
- } else if (S != "lbr" && S != "nolbr")
- error("/opt: unknown option: " + S);
+ bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile);
+ unsigned icfLevel =
+ args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
+ unsigned tailMerge = 1;
+ for (auto *arg : args.filtered(OPT_opt)) {
+ std::string str = StringRef(arg->getValue()).lower();
+ SmallVector<StringRef, 1> vec;
+ StringRef(str).split(vec, ',');
+ for (StringRef s : vec) {
+ if (s == "ref") {
+ doGC = true;
+ } else if (s == "noref") {
+ doGC = false;
+ } else if (s == "icf" || s.startswith("icf=")) {
+ icfLevel = 2;
+ } else if (s == "noicf") {
+ icfLevel = 0;
+ } else if (s == "lldtailmerge") {
+ tailMerge = 2;
+ } else if (s == "nolldtailmerge") {
+ tailMerge = 0;
+ } else if (s.startswith("lldlto=")) {
+ StringRef optLevel = s.substr(7);
+ if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3)
+ 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)
+ error("/opt:lldltojobs: invalid job count: " + jobs);
+ } else if (s.startswith("lldltopartitions=")) {
+ StringRef n = s.substr(17);
+ if (n.getAsInteger(10, config->ltoPartitions) ||
+ config->ltoPartitions == 0)
+ error("/opt:lldltopartitions: invalid partition count: " + n);
+ } else if (s != "lbr" && s != "nolbr")
+ error("/opt: unknown option: " + s);
}
}
@@ -1195,37 +1369,37 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// FIXME: LLD only implements "limited" ICF, i.e. it only merges identical
// code. If the user passes /OPT:ICF explicitly, LLD should merge identical
// comdat readonly data.
- if (ICFLevel == 1 && !DoGC)
- ICFLevel = 0;
- Config->DoGC = DoGC;
- Config->DoICF = ICFLevel > 0;
- Config->TailMerge = (TailMerge == 1 && Config->DoICF) || TailMerge == 2;
+ if (icfLevel == 1 && !doGC)
+ icfLevel = 0;
+ config->doGC = doGC;
+ config->doICF = icfLevel > 0;
+ config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2;
// Handle /lldsavetemps
- if (Args.hasArg(OPT_lldsavetemps))
- Config->SaveTemps = true;
+ if (args.hasArg(OPT_lldsavetemps))
+ config->saveTemps = true;
// Handle /kill-at
- if (Args.hasArg(OPT_kill_at))
- Config->KillAt = true;
+ if (args.hasArg(OPT_kill_at))
+ config->killAt = true;
// Handle /lldltocache
- if (auto *Arg = Args.getLastArg(OPT_lldltocache))
- Config->LTOCache = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_lldltocache))
+ config->ltoCache = arg->getValue();
// Handle /lldsavecachepolicy
- if (auto *Arg = Args.getLastArg(OPT_lldltocachepolicy))
- Config->LTOCachePolicy = CHECK(
- parseCachePruningPolicy(Arg->getValue()),
- Twine("/lldltocachepolicy: invalid cache policy: ") + Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_lldltocachepolicy))
+ config->ltoCachePolicy = CHECK(
+ parseCachePruningPolicy(arg->getValue()),
+ Twine("/lldltocachepolicy: invalid cache policy: ") + arg->getValue());
// Handle /failifmismatch
- for (auto *Arg : Args.filtered(OPT_failifmismatch))
- checkFailIfMismatch(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_failifmismatch))
+ checkFailIfMismatch(arg->getValue(), nullptr);
// Handle /merge
- for (auto *Arg : Args.filtered(OPT_merge))
- parseMerge(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_merge))
+ parseMerge(arg->getValue());
// Add default section merging rules after user rules. User rules take
// precedence, but we will emit a warning if there is a conflict.
@@ -1235,130 +1409,137 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
parseMerge(".xdata=.rdata");
parseMerge(".bss=.data");
- if (Config->MinGW) {
+ if (config->mingw) {
parseMerge(".ctors=.rdata");
parseMerge(".dtors=.rdata");
parseMerge(".CRT=.rdata");
}
// Handle /section
- for (auto *Arg : Args.filtered(OPT_section))
- parseSection(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_section))
+ parseSection(arg->getValue());
// Handle /aligncomm
- for (auto *Arg : Args.filtered(OPT_aligncomm))
- parseAligncomm(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_aligncomm))
+ parseAligncomm(arg->getValue());
// Handle /manifestdependency. This enables /manifest unless /manifest:no is
// also passed.
- if (auto *Arg = Args.getLastArg(OPT_manifestdependency)) {
- Config->ManifestDependency = Arg->getValue();
- Config->Manifest = Configuration::SideBySide;
+ if (auto *arg = args.getLastArg(OPT_manifestdependency)) {
+ config->manifestDependency = arg->getValue();
+ config->manifest = Configuration::SideBySide;
}
// Handle /manifest and /manifest:
- if (auto *Arg = Args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
- if (Arg->getOption().getID() == OPT_manifest)
- Config->Manifest = Configuration::SideBySide;
+ if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
+ if (arg->getOption().getID() == OPT_manifest)
+ config->manifest = Configuration::SideBySide;
else
- parseManifest(Arg->getValue());
+ parseManifest(arg->getValue());
}
// Handle /manifestuac
- if (auto *Arg = Args.getLastArg(OPT_manifestuac))
- parseManifestUAC(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_manifestuac))
+ parseManifestUAC(arg->getValue());
// Handle /manifestfile
- if (auto *Arg = Args.getLastArg(OPT_manifestfile))
- Config->ManifestFile = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_manifestfile))
+ config->manifestFile = arg->getValue();
// Handle /manifestinput
- for (auto *Arg : Args.filtered(OPT_manifestinput))
- Config->ManifestInput.push_back(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_manifestinput))
+ config->manifestInput.push_back(arg->getValue());
- if (!Config->ManifestInput.empty() &&
- Config->Manifest != Configuration::Embed) {
+ if (!config->manifestInput.empty() &&
+ config->manifest != Configuration::Embed) {
fatal("/manifestinput: requires /manifest:embed");
}
+ config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
+ config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
+ args.hasArg(OPT_thinlto_index_only_arg);
+ config->thinLTOIndexOnlyArg =
+ args.getLastArgValue(OPT_thinlto_index_only_arg);
+ config->thinLTOPrefixReplace =
+ getOldNewOptions(args, OPT_thinlto_prefix_replace);
+ config->thinLTOObjectSuffixReplace =
+ getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
// Handle miscellaneous boolean flags.
- Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
- Config->AllowIsolation =
- Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
- Config->Incremental =
- Args.hasFlag(OPT_incremental, OPT_incremental_no,
- !Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order) &&
- !Args.hasArg(OPT_profile));
- Config->IntegrityCheck =
- Args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
- Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
- Config->TerminalServerAware =
- !Config->DLL && Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
- Config->DebugDwarf = Debug == DebugKind::Dwarf;
- Config->DebugGHashes = Debug == DebugKind::GHash;
- Config->DebugSymtab = Debug == DebugKind::Symtab;
-
- Config->MapFile = getMapFile(Args);
-
- if (Config->Incremental && Args.hasArg(OPT_profile)) {
+ config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
+ config->allowIsolation =
+ args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
+ config->incremental =
+ args.hasFlag(OPT_incremental, OPT_incremental_no,
+ !config->doGC && !config->doICF && !args.hasArg(OPT_order) &&
+ !args.hasArg(OPT_profile));
+ config->integrityCheck =
+ args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
+ config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
+ for (auto *arg : args.filtered(OPT_swaprun))
+ parseSwaprun(arg->getValue());
+ config->terminalServerAware =
+ !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
+ config->debugDwarf = debug == DebugKind::Dwarf;
+ config->debugGHashes = debug == DebugKind::GHash;
+ config->debugSymtab = debug == DebugKind::Symtab;
+
+ config->mapFile = getMapFile(args);
+
+ if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
- Config->Incremental = false;
+ config->incremental = false;
}
- if (Config->Incremental && Args.hasArg(OPT_order)) {
+ if (config->incremental && args.hasArg(OPT_order)) {
warn("ignoring '/incremental' due to '/order' specification");
- Config->Incremental = false;
+ config->incremental = false;
}
- if (Config->Incremental && Config->DoGC) {
+ if (config->incremental && config->doGC) {
warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to "
"disable");
- Config->Incremental = false;
+ config->incremental = false;
}
- if (Config->Incremental && Config->DoICF) {
+ if (config->incremental && config->doICF) {
warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to "
"disable");
- Config->Incremental = false;
+ config->incremental = false;
}
if (errorCount())
return;
- std::set<sys::fs::UniqueID> WholeArchives;
- AutoExporter Exporter;
- for (auto *Arg : Args.filtered(OPT_wholearchive_file)) {
- if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(*Path))
- WholeArchives.insert(*ID);
- Exporter.addWholeArchive(*Path);
- }
- }
+ std::set<sys::fs::UniqueID> wholeArchives;
+ for (auto *arg : args.filtered(OPT_wholearchive_file))
+ if (Optional<StringRef> path = doFindFile(arg->getValue()))
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(*path))
+ wholeArchives.insert(*id);
// A predicate returning true if a given path is an argument for
// /wholearchive:, or /wholearchive is enabled globally.
// This function is a bit tricky because "foo.obj /wholearchive:././foo.obj"
// needs to be handled as "/wholearchive:foo.obj foo.obj".
- auto IsWholeArchive = [&](StringRef Path) -> bool {
- if (Args.hasArg(OPT_wholearchive_flag))
+ auto isWholeArchive = [&](StringRef path) -> bool {
+ if (args.hasArg(OPT_wholearchive_flag))
return true;
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
- return WholeArchives.count(*ID);
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+ return wholeArchives.count(*id);
return false;
};
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
- for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file))
- if (Optional<StringRef> Path = findFile(Arg->getValue()))
- enqueuePath(*Path, IsWholeArchive(*Path));
+ for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file))
+ if (Optional<StringRef> path = findFile(arg->getValue()))
+ enqueuePath(*path, isWholeArchive(*path));
- for (auto *Arg : Args.filtered(OPT_defaultlib))
- if (Optional<StringRef> Path = findLib(Arg->getValue()))
- enqueuePath(*Path, false);
+ for (auto *arg : args.filtered(OPT_defaultlib))
+ if (Optional<StringRef> path = findLib(arg->getValue()))
+ enqueuePath(*path, false);
// Windows specific -- Create a resource file containing a manifest file.
- if (Config->Manifest == Configuration::Embed)
+ if (config->manifest == Configuration::Embed)
addBuffer(createManifestRes(), false);
// Read all input files given via the command line.
@@ -1369,154 +1550,169 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// We should have inferred a machine type by now from the input files, but if
// not we assume x64.
- if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
warn("/machine is not specified. x64 is assumed");
- Config->Machine = AMD64;
+ config->machine = AMD64;
}
- Config->Wordsize = Config->is64() ? 8 : 4;
+ 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;
+
+ // Handle /functionpadmin
+ for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
+ parseFunctionPadMin(arg, config->machine);
// Input files can be Windows resource files (.res files). We use
// WindowsResource to convert resource files to a regular COFF file,
// then link the resulting file normally.
- if (!Resources.empty())
- Symtab->addFile(make<ObjFile>(convertResToCOFF(Resources)));
+ if (!resources.empty())
+ symtab->addFile(make<ObjFile>(convertResToCOFF(resources)));
- if (Tar)
- Tar->append("response.txt",
- createResponseFile(Args, FilePaths,
- ArrayRef<StringRef>(SearchPaths).slice(1)));
+ if (tar)
+ tar->append("response.txt",
+ createResponseFile(args, filePaths,
+ ArrayRef<StringRef>(searchPaths).slice(1)));
// Handle /largeaddressaware
- Config->LargeAddressAware = Args.hasFlag(
- OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64());
+ config->largeAddressAware = args.hasFlag(
+ OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64());
// Handle /highentropyva
- Config->HighEntropyVA =
- Config->is64() &&
- Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);
+ config->highEntropyVA =
+ config->is64() &&
+ args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);
- if (!Config->DynamicBase &&
- (Config->Machine == ARMNT || Config->Machine == ARM64))
+ if (!config->dynamicBase &&
+ (config->machine == ARMNT || config->machine == ARM64))
error("/dynamicbase:no is not compatible with " +
- machineToStr(Config->Machine));
+ machineToStr(config->machine));
// Handle /export
- for (auto *Arg : Args.filtered(OPT_export)) {
- Export E = parseExport(Arg->getValue());
- if (Config->Machine == I386) {
- if (!isDecorated(E.Name))
- E.Name = Saver.save("_" + E.Name);
- if (!E.ExtName.empty() && !isDecorated(E.ExtName))
- E.ExtName = Saver.save("_" + E.ExtName);
+ for (auto *arg : args.filtered(OPT_export)) {
+ Export e = parseExport(arg->getValue());
+ if (config->machine == I386) {
+ if (!isDecorated(e.name))
+ e.name = saver.save("_" + e.name);
+ if (!e.extName.empty() && !isDecorated(e.extName))
+ e.extName = saver.save("_" + e.extName);
}
- Config->Exports.push_back(E);
+ config->exports.push_back(e);
}
// Handle /def
- if (auto *Arg = Args.getLastArg(OPT_deffile)) {
+ if (auto *arg = args.getLastArg(OPT_deffile)) {
// parseModuleDefs mutates Config object.
- parseModuleDefs(Arg->getValue());
+ parseModuleDefs(arg->getValue());
}
// Handle generation of import library from a def file.
- if (!Args.hasArg(OPT_INPUT)) {
+ if (!args.hasArg(OPT_INPUT)) {
fixupExports();
- createImportLibrary(/*AsLib=*/true);
+ createImportLibrary(/*asLib=*/true);
return;
}
// Windows specific -- if no /subsystem is given, we need to infer
// that from entry point name. Must happen before /entry handling,
// and after the early return when just writing an import library.
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
- Config->Subsystem = inferSubsystem();
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
+ if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
+ config->subsystem = inferSubsystem();
+ if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
fatal("subsystem must be defined");
}
// Handle /entry and /dll
- if (auto *Arg = Args.getLastArg(OPT_entry)) {
- Config->Entry = addUndefined(mangle(Arg->getValue()));
- } else if (!Config->Entry && !Config->NoEntry) {
- if (Args.hasArg(OPT_dll)) {
- StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
+ if (auto *arg = args.getLastArg(OPT_entry)) {
+ config->entry = addUndefined(mangle(arg->getValue()));
+ } else if (!config->entry && !config->noEntry) {
+ if (args.hasArg(OPT_dll)) {
+ StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
: "_DllMainCRTStartup";
- Config->Entry = addUndefined(S);
+ config->entry = addUndefined(s);
} else {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
- StringRef S = findDefaultEntry();
- if (S.empty())
+ StringRef s = findDefaultEntry();
+ if (s.empty())
fatal("entry point must be defined");
- Config->Entry = addUndefined(S);
- log("Entry name inferred: " + S);
+ config->entry = addUndefined(s);
+ log("Entry name inferred: " + s);
}
}
// Handle /delayload
- for (auto *Arg : Args.filtered(OPT_delayload)) {
- Config->DelayLoads.insert(StringRef(Arg->getValue()).lower());
- if (Config->Machine == I386) {
- Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8");
+ for (auto *arg : args.filtered(OPT_delayload)) {
+ config->delayLoads.insert(StringRef(arg->getValue()).lower());
+ if (config->machine == I386) {
+ config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
} else {
- Config->DelayLoadHelper = addUndefined("__delayLoadHelper2");
+ config->delayLoadHelper = addUndefined("__delayLoadHelper2");
}
}
// Set default image name if neither /out or /def set it.
- if (Config->OutputFile.empty()) {
- Config->OutputFile =
- getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue());
+ if (config->outputFile.empty()) {
+ config->outputFile =
+ getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue());
}
- if (ShouldCreatePDB) {
+ // Fail early if an output file is not writable.
+ if (auto e = tryCreateFile(config->outputFile)) {
+ error("cannot open output file " + config->outputFile + ": " + e.message());
+ return;
+ }
+
+ if (shouldCreatePDB) {
// Put the PDB next to the image if no /pdb flag was passed.
- if (Config->PDBPath.empty()) {
- Config->PDBPath = Config->OutputFile;
- sys::path::replace_extension(Config->PDBPath, ".pdb");
+ if (config->pdbPath.empty()) {
+ config->pdbPath = config->outputFile;
+ sys::path::replace_extension(config->pdbPath, ".pdb");
}
// The embedded PDB path should be the absolute path to the PDB if no
// /pdbaltpath flag was passed.
- if (Config->PDBAltPath.empty()) {
- Config->PDBAltPath = Config->PDBPath;
+ if (config->pdbAltPath.empty()) {
+ config->pdbAltPath = config->pdbPath;
// It's important to make the path absolute and remove dots. This path
// will eventually be written into the PE header, and certain Microsoft
// tools won't work correctly if these assumptions are not held.
- sys::fs::make_absolute(Config->PDBAltPath);
- sys::path::remove_dots(Config->PDBAltPath);
+ sys::fs::make_absolute(config->pdbAltPath);
+ sys::path::remove_dots(config->pdbAltPath);
} else {
// Don't do this earlier, so that Config->OutputFile is ready.
- parsePDBAltPath(Config->PDBAltPath);
+ parsePDBAltPath(config->pdbAltPath);
}
}
// Set default image base if /base is not given.
- if (Config->ImageBase == uint64_t(-1))
- Config->ImageBase = getDefaultImageBase();
-
- Symtab->addSynthetic(mangle("__ImageBase"), nullptr);
- if (Config->Machine == I386) {
- Symtab->addAbsolute("___safe_se_handler_table", 0);
- Symtab->addAbsolute("___safe_se_handler_count", 0);
- }
-
- Symtab->addAbsolute(mangle("__guard_fids_count"), 0);
- Symtab->addAbsolute(mangle("__guard_fids_table"), 0);
- Symtab->addAbsolute(mangle("__guard_flags"), 0);
- Symtab->addAbsolute(mangle("__guard_iat_count"), 0);
- Symtab->addAbsolute(mangle("__guard_iat_table"), 0);
- Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
- Symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
+ if (config->imageBase == uint64_t(-1))
+ config->imageBase = getDefaultImageBase();
+
+ symtab->addSynthetic(mangle("__ImageBase"), nullptr);
+ if (config->machine == I386) {
+ symtab->addAbsolute("___safe_se_handler_table", 0);
+ symtab->addAbsolute("___safe_se_handler_count", 0);
+ }
+
+ symtab->addAbsolute(mangle("__guard_fids_count"), 0);
+ symtab->addAbsolute(mangle("__guard_fids_table"), 0);
+ symtab->addAbsolute(mangle("__guard_flags"), 0);
+ symtab->addAbsolute(mangle("__guard_iat_count"), 0);
+ symtab->addAbsolute(mangle("__guard_iat_table"), 0);
+ symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
+ symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
// Needed for MSVC 2017 15.5 CRT.
- Symtab->addAbsolute(mangle("__enclave_config"), 0);
+ symtab->addAbsolute(mangle("__enclave_config"), 0);
- if (Config->MinGW) {
- Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
- Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
- Symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
- Symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
+ if (config->mingw) {
+ symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
+ symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+ symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
+ symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
}
// This code may add new undefined symbols to the link, which may enqueue more
@@ -1525,33 +1721,33 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
do {
// Windows specific -- if entry point is not found,
// search for its mangled names.
- if (Config->Entry)
- Symtab->mangleMaybe(Config->Entry);
+ if (config->entry)
+ mangleMaybe(config->entry);
// Windows specific -- Make sure we resolve all dllexported symbols.
- for (Export &E : Config->Exports) {
- if (!E.ForwardTo.empty())
+ for (Export &e : config->exports) {
+ if (!e.forwardTo.empty())
continue;
- E.Sym = addUndefined(E.Name);
- if (!E.Directives)
- Symtab->mangleMaybe(E.Sym);
+ e.sym = addUndefined(e.name);
+ if (!e.directives)
+ e.symbolName = mangleMaybe(e.sym);
}
// Add weak aliases. Weak aliases is a mechanism to give remaining
// undefined symbols final chance to be resolved successfully.
- for (auto Pair : Config->AlternateNames) {
- StringRef From = Pair.first;
- StringRef To = Pair.second;
- Symbol *Sym = Symtab->find(From);
- if (!Sym)
+ for (auto pair : config->alternateNames) {
+ StringRef from = pair.first;
+ StringRef to = pair.second;
+ Symbol *sym = symtab->find(from);
+ if (!sym)
continue;
- if (auto *U = dyn_cast<Undefined>(Sym))
- if (!U->WeakAlias)
- U->WeakAlias = Symtab->addUndefined(To);
+ if (auto *u = dyn_cast<Undefined>(sym))
+ if (!u->weakAlias)
+ u->weakAlias = symtab->addUndefined(to);
}
// Windows specific -- if __load_config_used can be resolved, resolve it.
- if (Symtab->findUnderscore("_load_config_used"))
+ if (symtab->findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
} while (run());
@@ -1559,11 +1755,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
// Do LTO by compiling bitcode input files to a set of native COFF files then
- // link those files.
- Symtab->addCombinedLTOObjects();
+ // link those files (unless -thinlto-index-only was given, in which case we
+ // resolve symbols and write indices, but don't generate native code or link).
+ symtab->addCombinedLTOObjects();
+
+ // 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;
+
+ // If we generated native object files from bitcode files, this resolves
+ // references to the symbols we use from them.
run();
- if (Config->MinGW) {
+ if (args.hasArg(OPT_include_optional)) {
+ // Handle /includeoptional
+ for (auto *arg : args.filtered(OPT_include_optional))
+ if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue())))
+ addUndefined(arg->getValue());
+ while (run());
+ }
+
+ if (config->mingw) {
// Load any further object files that might be needed for doing automatic
// imports.
//
@@ -1577,95 +1791,91 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// normal object file as well (although that won't be used for the
// actual autoimport later on). If this pass adds new undefined references,
// we won't iterate further to resolve them.
- Symtab->loadMinGWAutomaticImports();
+ symtab->loadMinGWAutomaticImports();
run();
}
// Make sure we have resolved all symbols.
- Symtab->reportRemainingUndefines();
+ symtab->reportRemainingUndefines();
if (errorCount())
return;
- // Handle /safeseh.
- if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
- for (ObjFile *File : ObjFile::Instances)
- if (!File->hasSafeSEH())
- error("/safeseh: " + File->getName() + " is not compatible with SEH");
- if (errorCount())
- return;
- }
-
- // In MinGW, all symbols are automatically exported if no symbols
- // are chosen to be exported.
- if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) ||
- Args.hasArg(OPT_export_all_symbols))) {
- Exporter.initSymbolExcludes();
-
- Symtab->forEachSymbol([=](Symbol *S) {
- auto *Def = dyn_cast<Defined>(S);
- if (!Exporter.shouldExport(Def))
- return;
- Export E;
- E.Name = Def->getName();
- E.Sym = Def;
- if (Def->getChunk() &&
- !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
- E.Data = true;
- Config->Exports.push_back(E);
- });
+ if (config->mingw) {
+ // In MinGW, all symbols are automatically exported if no symbols
+ // are chosen to be exported.
+ maybeExportMinGWSymbols(args);
+
+ // Make sure the crtend.o object is the last object file. This object
+ // file can contain terminating section chunks that need to be placed
+ // last. GNU ld processes files and static libraries explicitly in the
+ // order provided on the command line, while lld will pull in needed
+ // files from static libraries only after the last object file on the
+ // command line.
+ for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end();
+ i != e; i++) {
+ ObjFile *file = *i;
+ if (isCrtend(file->getName())) {
+ ObjFile::instances.erase(i);
+ ObjFile::instances.push_back(file);
+ break;
+ }
+ }
}
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.
- if (!Config->Exports.empty() || Config->DLL) {
+ if (!config->exports.empty() || config->dll) {
fixupExports();
- createImportLibrary(/*AsLib=*/false);
+ createImportLibrary(/*asLib=*/false);
assignExportOrdinals();
}
// Handle /output-def (MinGW specific).
- if (auto *Arg = Args.getLastArg(OPT_output_def))
- writeDefFile(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_output_def))
+ writeDefFile(arg->getValue());
// Set extra alignment for .comm symbols
- for (auto Pair : Config->AlignComm) {
- StringRef Name = Pair.first;
- uint32_t Alignment = Pair.second;
+ for (auto pair : config->alignComm) {
+ StringRef name = pair.first;
+ uint32_t alignment = pair.second;
- Symbol *Sym = Symtab->find(Name);
- if (!Sym) {
- warn("/aligncomm symbol " + Name + " not found");
+ Symbol *sym = symtab->find(name);
+ if (!sym) {
+ warn("/aligncomm symbol " + name + " not found");
continue;
}
// If the symbol isn't common, it must have been replaced with a regular
// symbol, which will carry its own alignment.
- auto *DC = dyn_cast<DefinedCommon>(Sym);
- if (!DC)
+ auto *dc = dyn_cast<DefinedCommon>(sym);
+ if (!dc)
continue;
- CommonChunk *C = DC->getChunk();
- C->Alignment = std::max(C->Alignment, Alignment);
+ CommonChunk *c = dc->getChunk();
+ c->setAlignment(std::max(c->getAlignment(), alignment));
}
// Windows specific -- Create a side-by-side manifest file.
- if (Config->Manifest == Configuration::SideBySide)
+ if (config->manifest == Configuration::SideBySide)
createSideBySideManifest();
// Handle /order. We want to do this at this moment because we
// need a complete list of comdat sections to warn on nonexistent
// functions.
- if (auto *Arg = Args.getLastArg(OPT_order))
- parseOrderFile(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_order))
+ parseOrderFile(arg->getValue());
// Identify unreferenced COMDAT sections.
- if (Config->DoGC)
- markLive(Symtab->getChunks());
+ if (config->doGC)
+ markLive(symtab->getChunks());
+
+ // Needs to happen after the last call to addFile().
+ diagnoseMultipleResourceObjFiles();
// Identify identical COMDAT sections to merge them.
- if (Config->DoICF) {
+ if (config->doICF) {
findKeepUniqueSections();
- doICF(Symtab->getChunks());
+ doICF(symtab->getChunks());
}
// Write the result.
@@ -1673,7 +1883,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Stop early so we can print the results.
Timer::root().stop();
- if (Config->ShowTiming)
+ if (config->showTiming)
Timer::root().print();
}
diff --git a/COFF/Driver.h b/COFF/Driver.h
index e779721ab75d..6100c3ca0c9e 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -1,9 +1,8 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -31,7 +30,7 @@ namespace lld {
namespace coff {
class LinkerDriver;
-extern LinkerDriver *Driver;
+extern LinkerDriver *driver;
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
@@ -45,65 +44,71 @@ public:
class ArgParser {
public:
// Concatenate LINK environment variable and given arguments and parse them.
- llvm::opt::InputArgList parseLINK(std::vector<const char *> Args);
+ llvm::opt::InputArgList parseLINK(std::vector<const char *> args);
// Tokenizes a given string and then parses as command line options.
- llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
+ llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
// 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);
+ parseDirectives(StringRef s);
private:
// Parses command line options.
- llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
+ llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
- std::vector<const char *> tokenize(StringRef S);
+ std::vector<const char *> tokenize(StringRef s);
- COFFOptTable Table;
+ COFFOptTable table;
};
class LinkerDriver {
public:
- void link(llvm::ArrayRef<const char *> Args);
+ void link(llvm::ArrayRef<const char *> args);
// Used by the resolver to parse .drectve section contents.
- void parseDirectives(StringRef S);
+ void parseDirectives(InputFile *file);
// Used by ArchiveFile to enqueue members.
- void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
- StringRef ParentName);
+ void enqueueArchiveMember(const Archive::Child &c, StringRef symName,
+ StringRef parentName);
+
+ MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
- MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void enqueuePath(StringRef path, bool wholeArchive);
private:
- std::unique_ptr<llvm::TarWriter> Tar; // for /linkrepro
+ std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
// Opens a file. Path has to be resolved already.
- MemoryBufferRef openFile(StringRef Path);
+ MemoryBufferRef openFile(StringRef path);
// Searches a file from search paths.
- Optional<StringRef> findFile(StringRef Filename);
- Optional<StringRef> findLib(StringRef Filename);
- StringRef doFindFile(StringRef Filename);
- StringRef doFindLib(StringRef Filename);
- StringRef doFindLibMinGW(StringRef Filename);
+ Optional<StringRef> findFile(StringRef filename);
+ Optional<StringRef> findLib(StringRef filename);
+ StringRef doFindFile(StringRef filename);
+ StringRef doFindLib(StringRef filename);
+ StringRef doFindLibMinGW(StringRef filename);
// Parses LIB environment which contains a list of search paths.
void addLibSearchPaths();
// Library search path. The first element is always "" (current directory).
- std::vector<StringRef> SearchPaths;
+ std::vector<StringRef> searchPaths;
+
+ void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
// We don't want to add the same file more than once.
// Files are uniquified by their filesystem and file number.
- std::set<llvm::sys::fs::UniqueID> VisitedFiles;
+ std::set<llvm::sys::fs::UniqueID> visitedFiles;
+
+ std::set<std::string> visitedLibs;
- std::set<std::string> VisitedLibs;
+ Symbol *addUndefined(StringRef sym);
- Symbol *addUndefined(StringRef Sym);
+ StringRef mangleMaybe(Symbol *s);
// Windows specific -- "main" is not the only main function in Windows.
// You can choose one from these four -- {w,}{WinMain,main}.
@@ -115,60 +120,60 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
- void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive);
- void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
- StringRef ParentName);
+ void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
+ void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
+ StringRef parentName, uint64_t offsetInArchive);
- void enqueuePath(StringRef Path, bool WholeArchive);
-
- void enqueueTask(std::function<void()> Task);
+ void enqueueTask(std::function<void()> task);
bool run();
- std::list<std::function<void()>> TaskQueue;
- std::vector<StringRef> FilePaths;
- std::vector<MemoryBufferRef> Resources;
+ std::list<std::function<void()>> taskQueue;
+ std::vector<StringRef> filePaths;
+ std::vector<MemoryBufferRef> resources;
- llvm::StringSet<> DirectivesExports;
+ llvm::StringSet<> directivesExports;
};
// Functions below this line are defined in DriverUtils.cpp.
-void printHelp(const char *Argv0);
-
-// For /machine option.
-MachineTypes getMachineType(StringRef Arg);
-StringRef machineToStr(MachineTypes MT);
+void printHelp(const char *argv0);
// Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr);
+void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
-void parseGuard(StringRef Arg);
+void parseGuard(StringRef arg);
// Parses a string in the form of "<integer>[.<integer>]".
// Minor's default value is 0.
-void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor);
+void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
- uint32_t *Minor);
+void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+ uint32_t *minor);
void parseAlternateName(StringRef);
void parseMerge(StringRef);
void parseSection(StringRef);
void parseAligncomm(StringRef);
+// Parses a string in the form of "[:<integer>]"
+void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine);
+
// Parses a string in the form of "EMBED[,=<integer>]|NO".
-void parseManifest(StringRef Arg);
+void parseManifest(StringRef arg);
// Parses a string in the form of "level=<string>|uiAccess=<string>"
-void parseManifestUAC(StringRef Arg);
+void parseManifestUAC(StringRef arg);
+
+// Parses a string in the form of "cd|net[,(cd|net)]*"
+void parseSwaprun(StringRef arg);
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes();
void createSideBySideManifest();
// Used for dllexported symbols.
-Export parseExport(StringRef Arg);
+Export parseExport(StringRef arg);
void fixupExports();
void assignExportOrdinals();
@@ -176,12 +181,12 @@ void assignExportOrdinals();
// if value matches previous values for the key.
// This feature used in the directive section to reject
// incompatible objects.
-void checkFailIfMismatch(StringRef Arg);
+void checkFailIfMismatch(StringRef arg, InputFile *source);
// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs);
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs);
-void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
+void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
// Create enum with OPT_xxx values for each option in Options.td
enum {
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 3a11895497a4..4360ac23b262 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -1,9 +1,8 @@
//===- DriverUtils.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -48,104 +47,78 @@ const uint16_t RT_MANIFEST = 24;
class Executor {
public:
- explicit Executor(StringRef S) : Prog(Saver.save(S)) {}
- void add(StringRef S) { Args.push_back(Saver.save(S)); }
- void add(std::string &S) { Args.push_back(Saver.save(S)); }
- void add(Twine S) { Args.push_back(Saver.save(S)); }
- void add(const char *S) { Args.push_back(Saver.save(S)); }
+ explicit Executor(StringRef s) : prog(saver.save(s)) {}
+ void add(StringRef s) { args.push_back(saver.save(s)); }
+ void add(std::string &s) { args.push_back(saver.save(s)); }
+ void add(Twine s) { args.push_back(saver.save(s)); }
+ void add(const char *s) { args.push_back(saver.save(s)); }
void run() {
- ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
- if (auto EC = ExeOrErr.getError())
- fatal("unable to find " + Prog + " in PATH: " + EC.message());
- StringRef Exe = Saver.save(*ExeOrErr);
- Args.insert(Args.begin(), Exe);
+ ErrorOr<std::string> exeOrErr = sys::findProgramByName(prog);
+ if (auto ec = exeOrErr.getError())
+ fatal("unable to find " + prog + " in PATH: " + ec.message());
+ StringRef exe = saver.save(*exeOrErr);
+ args.insert(args.begin(), exe);
- if (sys::ExecuteAndWait(Args[0], Args) != 0)
+ if (sys::ExecuteAndWait(args[0], args) != 0)
fatal("ExecuteAndWait failed: " +
- llvm::join(Args.begin(), Args.end(), " "));
+ llvm::join(args.begin(), args.end(), " "));
}
private:
- StringRef Prog;
- std::vector<StringRef> Args;
+ StringRef prog;
+ std::vector<StringRef> args;
};
} // anonymous namespace
-// Returns /machine's value.
-MachineTypes getMachineType(StringRef S) {
- MachineTypes MT = StringSwitch<MachineTypes>(S.lower())
- .Cases("x64", "amd64", AMD64)
- .Cases("x86", "i386", I386)
- .Case("arm", ARMNT)
- .Case("arm64", ARM64)
- .Default(IMAGE_FILE_MACHINE_UNKNOWN);
- if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
- return MT;
- fatal("unknown /machine argument: " + S);
-}
-
-StringRef machineToStr(MachineTypes MT) {
- switch (MT) {
- case ARMNT:
- return "arm";
- case ARM64:
- return "arm64";
- case AMD64:
- return "x64";
- case I386:
- return "x86";
- default:
- llvm_unreachable("unknown machine type");
- }
-}
-
// Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) {
- StringRef S1, S2;
- std::tie(S1, S2) = Arg.split(',');
- if (S1.getAsInteger(0, *Addr))
- fatal("invalid number: " + S1);
- if (Size && !S2.empty() && S2.getAsInteger(0, *Size))
- fatal("invalid number: " + S2);
+void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
+ StringRef s1, s2;
+ std::tie(s1, s2) = arg.split(',');
+ if (s1.getAsInteger(0, *addr))
+ fatal("invalid number: " + s1);
+ if (size && !s2.empty() && s2.getAsInteger(0, *size))
+ fatal("invalid number: " + s2);
}
// Parses a string in the form of "<integer>[.<integer>]".
// If second number is not present, Minor is set to 0.
-void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) {
- StringRef S1, S2;
- std::tie(S1, S2) = Arg.split('.');
- if (S1.getAsInteger(0, *Major))
- fatal("invalid number: " + S1);
- *Minor = 0;
- if (!S2.empty() && S2.getAsInteger(0, *Minor))
- fatal("invalid number: " + S2);
-}
-
-void parseGuard(StringRef FullArg) {
- SmallVector<StringRef, 1> SplitArgs;
- FullArg.split(SplitArgs, ",");
- for (StringRef Arg : SplitArgs) {
- if (Arg.equals_lower("no"))
- Config->GuardCF = GuardCFLevel::Off;
- else if (Arg.equals_lower("nolongjmp"))
- Config->GuardCF = GuardCFLevel::NoLongJmp;
- else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp"))
- Config->GuardCF = GuardCFLevel::Full;
+void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
+ StringRef s1, s2;
+ std::tie(s1, s2) = arg.split('.');
+ if (s1.getAsInteger(0, *major))
+ fatal("invalid number: " + s1);
+ *minor = 0;
+ if (!s2.empty() && s2.getAsInteger(0, *minor))
+ fatal("invalid number: " + s2);
+}
+
+void parseGuard(StringRef fullArg) {
+ SmallVector<StringRef, 1> splitArgs;
+ fullArg.split(splitArgs, ",");
+ for (StringRef arg : splitArgs) {
+ if (arg.equals_lower("no"))
+ config->guardCF = GuardCFLevel::Off;
+ else if (arg.equals_lower("nolongjmp"))
+ config->guardCF = GuardCFLevel::NoLongJmp;
+ else if (arg.equals_lower("cf") || arg.equals_lower("longjmp"))
+ config->guardCF = GuardCFLevel::Full;
else
- fatal("invalid argument to /guard: " + Arg);
+ fatal("invalid argument to /guard: " + arg);
}
}
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
- uint32_t *Minor) {
- StringRef SysStr, Ver;
- std::tie(SysStr, Ver) = Arg.split(',');
- *Sys = StringSwitch<WindowsSubsystem>(SysStr.lower())
+void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+ uint32_t *minor) {
+ StringRef sysStr, ver;
+ std::tie(sysStr, ver) = arg.split(',');
+ std::string sysStrLower = sysStr.lower();
+ *sys = StringSwitch<WindowsSubsystem>(sysStrLower)
.Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
.Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI)
+ .Case("default", IMAGE_SUBSYSTEM_UNKNOWN)
.Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION)
.Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
.Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM)
@@ -154,175 +127,217 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
.Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI)
.Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI)
.Default(IMAGE_SUBSYSTEM_UNKNOWN);
- if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN)
- fatal("unknown subsystem: " + SysStr);
- if (!Ver.empty())
- parseVersion(Ver, Major, Minor);
+ if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default")
+ fatal("unknown subsystem: " + sysStr);
+ if (!ver.empty())
+ parseVersion(ver, major, minor);
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
-void parseAlternateName(StringRef S) {
- StringRef From, To;
- std::tie(From, To) = S.split('=');
- if (From.empty() || To.empty())
- fatal("/alternatename: invalid argument: " + S);
- auto It = Config->AlternateNames.find(From);
- if (It != Config->AlternateNames.end() && It->second != To)
- fatal("/alternatename: conflicts: " + S);
- Config->AlternateNames.insert(It, std::make_pair(From, To));
+void parseAlternateName(StringRef s) {
+ StringRef from, to;
+ std::tie(from, to) = s.split('=');
+ if (from.empty() || to.empty())
+ fatal("/alternatename: invalid argument: " + s);
+ auto it = config->alternateNames.find(from);
+ if (it != config->alternateNames.end() && it->second != to)
+ fatal("/alternatename: conflicts: " + s);
+ config->alternateNames.insert(it, std::make_pair(from, to));
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
-void parseMerge(StringRef S) {
- StringRef From, To;
- std::tie(From, To) = S.split('=');
- if (From.empty() || To.empty())
- fatal("/merge: invalid argument: " + S);
- if (From == ".rsrc" || To == ".rsrc")
+void parseMerge(StringRef s) {
+ StringRef from, to;
+ std::tie(from, to) = s.split('=');
+ if (from.empty() || to.empty())
+ fatal("/merge: invalid argument: " + s);
+ if (from == ".rsrc" || to == ".rsrc")
fatal("/merge: cannot merge '.rsrc' with any section");
- if (From == ".reloc" || To == ".reloc")
+ if (from == ".reloc" || to == ".reloc")
fatal("/merge: cannot merge '.reloc' with any section");
- auto Pair = Config->Merge.insert(std::make_pair(From, To));
- bool Inserted = Pair.second;
- if (!Inserted) {
- StringRef Existing = Pair.first->second;
- if (Existing != To)
- warn(S + ": already merged into " + Existing);
+ auto pair = config->merge.insert(std::make_pair(from, to));
+ bool inserted = pair.second;
+ if (!inserted) {
+ StringRef existing = pair.first->second;
+ if (existing != to)
+ warn(s + ": already merged into " + existing);
}
}
-static uint32_t parseSectionAttributes(StringRef S) {
- uint32_t Ret = 0;
- for (char C : S.lower()) {
- switch (C) {
+static uint32_t parseSectionAttributes(StringRef s) {
+ uint32_t ret = 0;
+ for (char c : s.lower()) {
+ switch (c) {
case 'd':
- Ret |= IMAGE_SCN_MEM_DISCARDABLE;
+ ret |= IMAGE_SCN_MEM_DISCARDABLE;
break;
case 'e':
- Ret |= IMAGE_SCN_MEM_EXECUTE;
+ ret |= IMAGE_SCN_MEM_EXECUTE;
break;
case 'k':
- Ret |= IMAGE_SCN_MEM_NOT_CACHED;
+ ret |= IMAGE_SCN_MEM_NOT_CACHED;
break;
case 'p':
- Ret |= IMAGE_SCN_MEM_NOT_PAGED;
+ ret |= IMAGE_SCN_MEM_NOT_PAGED;
break;
case 'r':
- Ret |= IMAGE_SCN_MEM_READ;
+ ret |= IMAGE_SCN_MEM_READ;
break;
case 's':
- Ret |= IMAGE_SCN_MEM_SHARED;
+ ret |= IMAGE_SCN_MEM_SHARED;
break;
case 'w':
- Ret |= IMAGE_SCN_MEM_WRITE;
+ ret |= IMAGE_SCN_MEM_WRITE;
break;
default:
- fatal("/section: invalid argument: " + S);
+ fatal("/section: invalid argument: " + s);
}
}
- return Ret;
+ return ret;
}
// Parses /section option argument.
-void parseSection(StringRef S) {
- StringRef Name, Attrs;
- std::tie(Name, Attrs) = S.split(',');
- if (Name.empty() || Attrs.empty())
- fatal("/section: invalid argument: " + S);
- Config->Section[Name] = parseSectionAttributes(Attrs);
+void parseSection(StringRef s) {
+ StringRef name, attrs;
+ std::tie(name, attrs) = s.split(',');
+ if (name.empty() || attrs.empty())
+ fatal("/section: invalid argument: " + s);
+ config->section[name] = parseSectionAttributes(attrs);
}
// Parses /aligncomm option argument.
-void parseAligncomm(StringRef S) {
- StringRef Name, Align;
- std::tie(Name, Align) = S.split(',');
- if (Name.empty() || Align.empty()) {
- error("/aligncomm: invalid argument: " + S);
+void parseAligncomm(StringRef s) {
+ StringRef name, align;
+ std::tie(name, align) = s.split(',');
+ if (name.empty() || align.empty()) {
+ error("/aligncomm: invalid argument: " + s);
+ return;
+ }
+ int v;
+ if (align.getAsInteger(0, v)) {
+ error("/aligncomm: invalid argument: " + s);
return;
}
- int V;
- if (Align.getAsInteger(0, V)) {
- error("/aligncomm: invalid argument: " + S);
+ config->alignComm[name] = std::max(config->alignComm[name], 1 << v);
+}
+
+// Parses /functionpadmin option argument.
+void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) {
+ StringRef arg = a->getNumValues() ? a->getValue() : "";
+ if (!arg.empty()) {
+ // Optional padding in bytes is given.
+ if (arg.getAsInteger(0, config->functionPadMin))
+ error("/functionpadmin: invalid argument: " + arg);
return;
}
- Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V);
+ // No optional argument given.
+ // Set default padding based on machine, similar to link.exe.
+ // There is no default padding for ARM platforms.
+ if (machine == I386) {
+ config->functionPadMin = 5;
+ } else if (machine == AMD64) {
+ config->functionPadMin = 6;
+ } else {
+ error("/functionpadmin: invalid argument for this machine: " + arg);
+ }
}
// Parses a string in the form of "EMBED[,=<integer>]|NO".
// Results are directly written to Config.
-void parseManifest(StringRef Arg) {
- if (Arg.equals_lower("no")) {
- Config->Manifest = Configuration::No;
+void parseManifest(StringRef arg) {
+ if (arg.equals_lower("no")) {
+ config->manifest = Configuration::No;
return;
}
- if (!Arg.startswith_lower("embed"))
- fatal("invalid option " + Arg);
- Config->Manifest = Configuration::Embed;
- Arg = Arg.substr(strlen("embed"));
- if (Arg.empty())
+ if (!arg.startswith_lower("embed"))
+ fatal("invalid option " + arg);
+ config->manifest = Configuration::Embed;
+ arg = arg.substr(strlen("embed"));
+ if (arg.empty())
return;
- if (!Arg.startswith_lower(",id="))
- fatal("invalid option " + Arg);
- Arg = Arg.substr(strlen(",id="));
- if (Arg.getAsInteger(0, Config->ManifestID))
- fatal("invalid option " + Arg);
+ if (!arg.startswith_lower(",id="))
+ fatal("invalid option " + arg);
+ arg = arg.substr(strlen(",id="));
+ if (arg.getAsInteger(0, config->manifestID))
+ fatal("invalid option " + arg);
}
// Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
// Results are directly written to Config.
-void parseManifestUAC(StringRef Arg) {
- if (Arg.equals_lower("no")) {
- Config->ManifestUAC = false;
+void parseManifestUAC(StringRef arg) {
+ if (arg.equals_lower("no")) {
+ config->manifestUAC = false;
return;
}
for (;;) {
- Arg = Arg.ltrim();
- if (Arg.empty())
+ arg = arg.ltrim();
+ if (arg.empty())
return;
- if (Arg.startswith_lower("level=")) {
- Arg = Arg.substr(strlen("level="));
- std::tie(Config->ManifestLevel, Arg) = Arg.split(" ");
+ if (arg.startswith_lower("level=")) {
+ arg = arg.substr(strlen("level="));
+ std::tie(config->manifestLevel, arg) = arg.split(" ");
continue;
}
- if (Arg.startswith_lower("uiaccess=")) {
- Arg = Arg.substr(strlen("uiaccess="));
- std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" ");
+ if (arg.startswith_lower("uiaccess=")) {
+ arg = arg.substr(strlen("uiaccess="));
+ std::tie(config->manifestUIAccess, arg) = arg.split(" ");
continue;
}
- fatal("invalid option " + Arg);
+ fatal("invalid option " + arg);
}
}
+// Parses a string in the form of "cd|net[,(cd|net)]*"
+// Results are directly written to Config.
+void parseSwaprun(StringRef arg) {
+ do {
+ StringRef swaprun, newArg;
+ std::tie(swaprun, newArg) = arg.split(',');
+ if (swaprun.equals_lower("cd"))
+ config->swaprunCD = true;
+ else if (swaprun.equals_lower("net"))
+ config->swaprunNet = true;
+ else if (swaprun.empty())
+ error("/swaprun: missing argument");
+ else
+ error("/swaprun: invalid argument: " + swaprun);
+ // To catch trailing commas, e.g. `/spawrun:cd,`
+ if (newArg.empty() && arg.endswith(","))
+ error("/swaprun: missing argument");
+ arg = newArg;
+ } while (!arg.empty());
+}
+
// An RAII temporary file class that automatically removes a temporary file.
namespace {
class TemporaryFile {
public:
- TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") {
- SmallString<128> S;
- if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
- fatal("cannot create a temporary file: " + EC.message());
- Path = S.str();
-
- if (!Contents.empty()) {
- std::error_code EC;
- raw_fd_ostream OS(Path, EC, sys::fs::F_None);
- if (EC)
- fatal("failed to open " + Path + ": " + EC.message());
- OS << Contents;
+ TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") {
+ SmallString<128> s;
+ if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))
+ fatal("cannot create a temporary file: " + ec.message());
+ path = s.str();
+
+ if (!contents.empty()) {
+ std::error_code ec;
+ raw_fd_ostream os(path, ec, sys::fs::F_None);
+ if (ec)
+ fatal("failed to open " + path + ": " + ec.message());
+ os << contents;
}
}
- TemporaryFile(TemporaryFile &&Obj) {
- std::swap(Path, Obj.Path);
+ TemporaryFile(TemporaryFile &&obj) {
+ std::swap(path, obj.path);
}
~TemporaryFile() {
- if (Path.empty())
+ if (path.empty())
return;
- if (sys::fs::remove(Path))
- fatal("failed to remove " + Path);
+ if (sys::fs::remove(path))
+ fatal("failed to remove " + path);
}
// Returns a memory buffer of this temporary file.
@@ -330,387 +345,390 @@ public:
// so it is safe to remove the file immediately after this function
// is called (you cannot remove an opened file on Windows.)
std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
- // IsVolatileSize=true forces MemoryBuffer to not use mmap().
- return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
+ // IsVolatile=true forces MemoryBuffer to not use mmap().
+ return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false,
- /*IsVolatileSize=*/true),
- "could not open " + Path);
+ /*IsVolatile=*/true),
+ "could not open " + path);
}
- std::string Path;
+ std::string path;
};
}
static std::string createDefaultXml() {
- std::string Ret;
- raw_string_ostream OS(Ret);
+ std::string ret;
+ raw_string_ostream os(ret);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
- OS << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
+ os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
<< "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
<< " manifestVersion=\"1.0\">\n";
- if (Config->ManifestUAC) {
- OS << " <trustInfo>\n"
+ if (config->manifestUAC) {
+ os << " <trustInfo>\n"
<< " <security>\n"
<< " <requestedPrivileges>\n"
- << " <requestedExecutionLevel level=" << Config->ManifestLevel
- << " uiAccess=" << Config->ManifestUIAccess << "/>\n"
+ << " <requestedExecutionLevel level=" << config->manifestLevel
+ << " uiAccess=" << config->manifestUIAccess << "/>\n"
<< " </requestedPrivileges>\n"
<< " </security>\n"
<< " </trustInfo>\n";
}
- if (!Config->ManifestDependency.empty()) {
- OS << " <dependency>\n"
+ if (!config->manifestDependency.empty()) {
+ os << " <dependency>\n"
<< " <dependentAssembly>\n"
- << " <assemblyIdentity " << Config->ManifestDependency << " />\n"
+ << " <assemblyIdentity " << config->manifestDependency << " />\n"
<< " </dependentAssembly>\n"
<< " </dependency>\n";
}
- OS << "</assembly>\n";
- return OS.str();
+ os << "</assembly>\n";
+ return os.str();
}
-static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) {
- std::unique_ptr<MemoryBuffer> DefaultXmlCopy =
- MemoryBuffer::getMemBufferCopy(DefaultXml);
+static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
+ std::unique_ptr<MemoryBuffer> defaultXmlCopy =
+ MemoryBuffer::getMemBufferCopy(defaultXml);
- windows_manifest::WindowsManifestMerger Merger;
- if (auto E = Merger.merge(*DefaultXmlCopy.get()))
+ windows_manifest::WindowsManifestMerger merger;
+ if (auto e = merger.merge(*defaultXmlCopy.get()))
fatal("internal manifest tool failed on default xml: " +
- toString(std::move(E)));
+ toString(std::move(e)));
- for (StringRef Filename : Config->ManifestInput) {
- std::unique_ptr<MemoryBuffer> Manifest =
- check(MemoryBuffer::getFile(Filename));
- if (auto E = Merger.merge(*Manifest.get()))
- fatal("internal manifest tool failed on file " + Filename + ": " +
- toString(std::move(E)));
+ for (StringRef filename : config->manifestInput) {
+ std::unique_ptr<MemoryBuffer> manifest =
+ check(MemoryBuffer::getFile(filename));
+ if (auto e = merger.merge(*manifest.get()))
+ fatal("internal manifest tool failed on file " + filename + ": " +
+ toString(std::move(e)));
}
- return Merger.getMergedManifest().get()->getBuffer();
+ return merger.getMergedManifest().get()->getBuffer();
}
-static std::string createManifestXmlWithExternalMt(StringRef DefaultXml) {
+static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
// Create the default manifest file as a temporary file.
TemporaryFile Default("defaultxml", "manifest");
- std::error_code EC;
- raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text);
- if (EC)
- fatal("failed to open " + Default.Path + ": " + EC.message());
- OS << DefaultXml;
- OS.close();
+ std::error_code ec;
+ raw_fd_ostream os(Default.path, ec, sys::fs::F_Text);
+ if (ec)
+ fatal("failed to open " + Default.path + ": " + ec.message());
+ os << defaultXml;
+ os.close();
// Merge user-supplied manifests if they are given. Since libxml2 is not
// enabled, we must shell out to Microsoft's mt.exe tool.
- TemporaryFile User("user", "manifest");
+ TemporaryFile user("user", "manifest");
- Executor E("mt.exe");
- E.add("/manifest");
- E.add(Default.Path);
- for (StringRef Filename : Config->ManifestInput) {
- E.add("/manifest");
- E.add(Filename);
+ Executor e("mt.exe");
+ e.add("/manifest");
+ e.add(Default.path);
+ for (StringRef filename : config->manifestInput) {
+ e.add("/manifest");
+ e.add(filename);
}
- E.add("/nologo");
- E.add("/out:" + StringRef(User.Path));
- E.run();
+ e.add("/nologo");
+ e.add("/out:" + StringRef(user.path));
+ e.run();
- return CHECK(MemoryBuffer::getFile(User.Path), "could not open " + User.Path)
+ return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
.get()
->getBuffer();
}
static std::string createManifestXml() {
- std::string DefaultXml = createDefaultXml();
- if (Config->ManifestInput.empty())
- return DefaultXml;
+ std::string defaultXml = createDefaultXml();
+ if (config->manifestInput.empty())
+ return defaultXml;
if (windows_manifest::isAvailable())
- return createManifestXmlWithInternalMt(DefaultXml);
+ return createManifestXmlWithInternalMt(defaultXml);
- return createManifestXmlWithExternalMt(DefaultXml);
+ return createManifestXmlWithExternalMt(defaultXml);
}
static std::unique_ptr<WritableMemoryBuffer>
-createMemoryBufferForManifestRes(size_t ManifestSize) {
- size_t ResSize = alignTo(
+createMemoryBufferForManifestRes(size_t manifestSize) {
+ size_t resSize = alignTo(
object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
- sizeof(object::WinResHeaderSuffix) + ManifestSize,
+ sizeof(object::WinResHeaderSuffix) + manifestSize,
object::WIN_RES_DATA_ALIGNMENT);
- return WritableMemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile +
+ return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile +
".manifest.res");
}
-static void writeResFileHeader(char *&Buf) {
- memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
- Buf += sizeof(COFF::WinResMagic);
- memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
- Buf += object::WIN_RES_NULL_ENTRY_SIZE;
+static void writeResFileHeader(char *&buf) {
+ memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
+ buf += sizeof(COFF::WinResMagic);
+ memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
+ buf += object::WIN_RES_NULL_ENTRY_SIZE;
}
-static void writeResEntryHeader(char *&Buf, size_t ManifestSize) {
+static void writeResEntryHeader(char *&buf, size_t manifestSize) {
// Write the prefix.
- auto *Prefix = reinterpret_cast<object::WinResHeaderPrefix *>(Buf);
- Prefix->DataSize = ManifestSize;
- Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
+ auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);
+ prefix->DataSize = manifestSize;
+ prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
sizeof(object::WinResIDs) +
sizeof(object::WinResHeaderSuffix);
- Buf += sizeof(object::WinResHeaderPrefix);
+ buf += sizeof(object::WinResHeaderPrefix);
// Write the Type/Name IDs.
- auto *IDs = reinterpret_cast<object::WinResIDs *>(Buf);
- IDs->setType(RT_MANIFEST);
- IDs->setName(Config->ManifestID);
- Buf += sizeof(object::WinResIDs);
+ auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);
+ iDs->setType(RT_MANIFEST);
+ iDs->setName(config->manifestID);
+ buf += sizeof(object::WinResIDs);
// Write the suffix.
- auto *Suffix = reinterpret_cast<object::WinResHeaderSuffix *>(Buf);
- Suffix->DataVersion = 0;
- Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
- Suffix->Language = SUBLANG_ENGLISH_US;
- Suffix->Version = 0;
- Suffix->Characteristics = 0;
- Buf += sizeof(object::WinResHeaderSuffix);
+ auto *suffix = reinterpret_cast<object::WinResHeaderSuffix *>(buf);
+ suffix->DataVersion = 0;
+ suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
+ suffix->Language = SUBLANG_ENGLISH_US;
+ suffix->Version = 0;
+ suffix->Characteristics = 0;
+ buf += sizeof(object::WinResHeaderSuffix);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
- std::string Manifest = createManifestXml();
+ std::string manifest = createManifestXml();
- std::unique_ptr<WritableMemoryBuffer> Res =
- createMemoryBufferForManifestRes(Manifest.size());
+ std::unique_ptr<WritableMemoryBuffer> res =
+ createMemoryBufferForManifestRes(manifest.size());
- char *Buf = Res->getBufferStart();
- writeResFileHeader(Buf);
- writeResEntryHeader(Buf, Manifest.size());
+ char *buf = res->getBufferStart();
+ writeResFileHeader(buf);
+ writeResEntryHeader(buf, manifest.size());
// Copy the manifest data into the .res file.
- std::copy(Manifest.begin(), Manifest.end(), Buf);
- return std::move(Res);
+ std::copy(manifest.begin(), manifest.end(), buf);
+ return std::move(res);
}
void createSideBySideManifest() {
- std::string Path = Config->ManifestFile;
- if (Path == "")
- Path = Config->OutputFile + ".manifest";
- std::error_code EC;
- raw_fd_ostream Out(Path, EC, sys::fs::F_Text);
- if (EC)
- fatal("failed to create manifest: " + EC.message());
- Out << createManifestXml();
+ std::string path = config->manifestFile;
+ if (path == "")
+ path = config->outputFile + ".manifest";
+ std::error_code ec;
+ raw_fd_ostream out(path, ec, sys::fs::F_Text);
+ if (ec)
+ fatal("failed to create manifest: " + ec.message());
+ out << createManifestXml();
}
// Parse a string in the form of
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"
// or "<name>=<dllname>.<name>".
// Used for parsing /export arguments.
-Export parseExport(StringRef Arg) {
- Export E;
- StringRef Rest;
- std::tie(E.Name, Rest) = Arg.split(",");
- if (E.Name.empty())
+Export parseExport(StringRef arg) {
+ Export e;
+ StringRef rest;
+ std::tie(e.name, rest) = arg.split(",");
+ if (e.name.empty())
goto err;
- if (E.Name.contains('=')) {
- StringRef X, Y;
- std::tie(X, Y) = E.Name.split("=");
+ if (e.name.contains('=')) {
+ StringRef x, y;
+ std::tie(x, y) = e.name.split("=");
// If "<name>=<dllname>.<name>".
- if (Y.contains(".")) {
- E.Name = X;
- E.ForwardTo = Y;
- return E;
+ if (y.contains(".")) {
+ e.name = x;
+ e.forwardTo = y;
+ return e;
}
- E.ExtName = X;
- E.Name = Y;
- if (E.Name.empty())
+ e.extName = x;
+ e.name = y;
+ if (e.name.empty())
goto err;
}
// If "<name>=<internalname>[,@ordinal[,NONAME]][,DATA][,PRIVATE]"
- while (!Rest.empty()) {
- StringRef Tok;
- std::tie(Tok, Rest) = Rest.split(",");
- if (Tok.equals_lower("noname")) {
- if (E.Ordinal == 0)
+ while (!rest.empty()) {
+ StringRef tok;
+ std::tie(tok, rest) = rest.split(",");
+ if (tok.equals_lower("noname")) {
+ if (e.ordinal == 0)
goto err;
- E.Noname = true;
+ e.noname = true;
continue;
}
- if (Tok.equals_lower("data")) {
- E.Data = true;
+ if (tok.equals_lower("data")) {
+ e.data = true;
continue;
}
- if (Tok.equals_lower("constant")) {
- E.Constant = true;
+ if (tok.equals_lower("constant")) {
+ e.constant = true;
continue;
}
- if (Tok.equals_lower("private")) {
- E.Private = true;
+ if (tok.equals_lower("private")) {
+ e.isPrivate = true;
continue;
}
- if (Tok.startswith("@")) {
- int32_t Ord;
- if (Tok.substr(1).getAsInteger(0, Ord))
+ if (tok.startswith("@")) {
+ int32_t ord;
+ if (tok.substr(1).getAsInteger(0, ord))
goto err;
- if (Ord <= 0 || 65535 < Ord)
+ if (ord <= 0 || 65535 < ord)
goto err;
- E.Ordinal = Ord;
+ e.ordinal = ord;
continue;
}
goto err;
}
- return E;
+ return e;
err:
- fatal("invalid /export: " + Arg);
+ fatal("invalid /export: " + arg);
}
-static StringRef undecorate(StringRef Sym) {
- if (Config->Machine != I386)
- return Sym;
+static StringRef undecorate(StringRef sym) {
+ if (config->machine != I386)
+ return sym;
// In MSVC mode, a fully decorated stdcall function is exported
// as-is with the leading underscore (with type IMPORT_NAME).
// In MinGW mode, a decorated stdcall function gets the underscore
// removed, just like normal cdecl functions.
- if (Sym.startswith("_") && Sym.contains('@') && !Config->MinGW)
- return Sym;
- return Sym.startswith("_") ? Sym.substr(1) : Sym;
+ if (sym.startswith("_") && sym.contains('@') && !config->mingw)
+ return sym;
+ return sym.startswith("_") ? sym.substr(1) : sym;
}
// Convert stdcall/fastcall style symbols into unsuffixed symbols,
// with or without a leading underscore. (MinGW specific.)
-static StringRef killAt(StringRef Sym, bool Prefix) {
- if (Sym.empty())
- return Sym;
+static StringRef killAt(StringRef sym, bool prefix) {
+ if (sym.empty())
+ return sym;
// Strip any trailing stdcall suffix
- Sym = Sym.substr(0, Sym.find('@', 1));
- if (!Sym.startswith("@")) {
- if (Prefix && !Sym.startswith("_"))
- return Saver.save("_" + Sym);
- return Sym;
+ sym = sym.substr(0, sym.find('@', 1));
+ if (!sym.startswith("@")) {
+ if (prefix && !sym.startswith("_"))
+ return saver.save("_" + sym);
+ return sym;
}
// For fastcall, remove the leading @ and replace it with an
// underscore, if prefixes are used.
- Sym = Sym.substr(1);
- if (Prefix)
- Sym = Saver.save("_" + Sym);
- return Sym;
+ sym = sym.substr(1);
+ if (prefix)
+ sym = saver.save("_" + sym);
+ return sym;
}
// Performs error checking on all /export arguments.
// It also sets ordinals.
void fixupExports() {
// Symbol ordinals must be unique.
- std::set<uint16_t> Ords;
- for (Export &E : Config->Exports) {
- if (E.Ordinal == 0)
+ std::set<uint16_t> ords;
+ for (Export &e : config->exports) {
+ if (e.ordinal == 0)
continue;
- if (!Ords.insert(E.Ordinal).second)
- fatal("duplicate export ordinal: " + E.Name);
- }
-
- for (Export &E : Config->Exports) {
- Symbol *Sym = E.Sym;
- if (!E.ForwardTo.empty() || !Sym) {
- E.SymbolName = E.Name;
- } else {
- if (auto *U = dyn_cast<Undefined>(Sym))
- if (U->WeakAlias)
- Sym = U->WeakAlias;
- E.SymbolName = Sym->getName();
- }
+ if (!ords.insert(e.ordinal).second)
+ fatal("duplicate export ordinal: " + e.name);
}
- for (Export &E : Config->Exports) {
- if (!E.ForwardTo.empty()) {
- E.ExportName = undecorate(E.Name);
+ for (Export &e : config->exports) {
+ if (!e.forwardTo.empty()) {
+ e.exportName = undecorate(e.name);
} else {
- E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName);
+ e.exportName = undecorate(e.extName.empty() ? e.name : e.extName);
}
}
- if (Config->KillAt && Config->Machine == I386) {
- for (Export &E : Config->Exports) {
- E.Name = killAt(E.Name, true);
- E.ExportName = killAt(E.ExportName, false);
- E.ExtName = killAt(E.ExtName, true);
- E.SymbolName = killAt(E.SymbolName, true);
+ if (config->killAt && config->machine == I386) {
+ for (Export &e : config->exports) {
+ e.name = killAt(e.name, true);
+ e.exportName = killAt(e.exportName, false);
+ e.extName = killAt(e.extName, true);
+ e.symbolName = killAt(e.symbolName, true);
}
}
// Uniquefy by name.
- DenseMap<StringRef, Export *> Map(Config->Exports.size());
- std::vector<Export> V;
- for (Export &E : Config->Exports) {
- auto Pair = Map.insert(std::make_pair(E.ExportName, &E));
- bool Inserted = Pair.second;
- if (Inserted) {
- V.push_back(E);
+ DenseMap<StringRef, Export *> map(config->exports.size());
+ std::vector<Export> v;
+ for (Export &e : config->exports) {
+ auto pair = map.insert(std::make_pair(e.exportName, &e));
+ bool inserted = pair.second;
+ if (inserted) {
+ v.push_back(e);
continue;
}
- Export *Existing = Pair.first->second;
- if (E == *Existing || E.Name != Existing->Name)
+ Export *existing = pair.first->second;
+ if (e == *existing || e.name != existing->name)
continue;
- warn("duplicate /export option: " + E.Name);
+ warn("duplicate /export option: " + e.name);
}
- Config->Exports = std::move(V);
+ config->exports = std::move(v);
// Sort by name.
- std::sort(Config->Exports.begin(), Config->Exports.end(),
- [](const Export &A, const Export &B) {
- return A.ExportName < B.ExportName;
+ std::sort(config->exports.begin(), config->exports.end(),
+ [](const Export &a, const Export &b) {
+ return a.exportName < b.exportName;
});
}
void assignExportOrdinals() {
// Assign unique ordinals if default (= 0).
- uint16_t Max = 0;
- for (Export &E : Config->Exports)
- Max = std::max(Max, E.Ordinal);
- for (Export &E : Config->Exports)
- if (E.Ordinal == 0)
- E.Ordinal = ++Max;
+ uint16_t max = 0;
+ for (Export &e : config->exports)
+ max = std::max(max, e.ordinal);
+ for (Export &e : config->exports)
+ if (e.ordinal == 0)
+ e.ordinal = ++max;
}
// Parses a string in the form of "key=value" and check
// if value matches previous values for the same key.
-void checkFailIfMismatch(StringRef Arg) {
- StringRef K, V;
- std::tie(K, V) = Arg.split('=');
- if (K.empty() || V.empty())
- fatal("/failifmismatch: invalid argument: " + Arg);
- StringRef Existing = Config->MustMatch[K];
- if (!Existing.empty() && V != Existing)
- fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V +
- " for key " + K);
- Config->MustMatch[K] = V;
+void checkFailIfMismatch(StringRef arg, InputFile *source) {
+ StringRef k, v;
+ std::tie(k, v) = arg.split('=');
+ if (k.empty() || v.empty())
+ fatal("/failifmismatch: invalid argument: " + arg);
+ std::pair<StringRef, InputFile *> existing = config->mustMatch[k];
+ if (!existing.first.empty() && v != existing.first) {
+ std::string sourceStr = source ? toString(source) : "cmd-line";
+ std::string existingStr =
+ existing.second ? toString(existing.second) : "cmd-line";
+ fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " +
+ existingStr + " has value " + existing.first + "\n>>> " + sourceStr +
+ " has value " + v);
+ }
+ config->mustMatch[k] = {v, source};
}
// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
- object::WindowsResourceParser Parser;
-
- for (MemoryBufferRef MB : MBs) {
- std::unique_ptr<object::Binary> Bin = check(object::createBinary(MB));
- object::WindowsResource *RF = dyn_cast<object::WindowsResource>(Bin.get());
- if (!RF)
+// Does what cvtres.exe does, but in-process and cross-platform.
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) {
+ object::WindowsResourceParser parser;
+
+ for (MemoryBufferRef mb : mbs) {
+ std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
+ object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
+ if (!rf)
fatal("cannot compile non-resource file as resource");
- if (auto EC = Parser.parse(RF))
- fatal("failed to parse .res file: " + toString(std::move(EC)));
+
+ std::vector<std::string> duplicates;
+ if (auto ec = parser.parse(rf, duplicates))
+ fatal(toString(std::move(ec)));
+
+ for (const auto &dupeDiag : duplicates)
+ if (config->forceMultipleRes)
+ warn(dupeDiag);
+ else
+ error(dupeDiag);
}
- Expected<std::unique_ptr<MemoryBuffer>> E =
- llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
- if (!E)
- fatal("failed to write .res to COFF: " + toString(E.takeError()));
+ Expected<std::unique_ptr<MemoryBuffer>> e =
+ llvm::object::writeWindowsResourceCOFF(config->machine, parser,
+ config->timestamp);
+ if (!e)
+ fatal("failed to write .res to COFF: " + toString(e.takeError()));
- MemoryBufferRef MBRef = **E;
- make<std::unique_ptr<MemoryBuffer>>(std::move(*E)); // take ownership
- return MBRef;
+ MemoryBufferRef mbref = **e;
+ make<std::unique_ptr<MemoryBuffer>>(std::move(*e)); // take ownership
+ return mbref;
}
// Create OptTable
@@ -721,7 +739,7 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
#undef PREFIX
// Create table mapping all options defined in Options.td
-static const llvm::opt::OptTable::Info InfoTable[] = {
+static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
@@ -729,36 +747,36 @@ static const llvm::opt::OptTable::Info InfoTable[] = {
#undef OPTION
};
-COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {}
+COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
// Set color diagnostics according to --color-diagnostics={auto,always,never}
// or --no-color-diagnostics flags.
-static void handleColorDiagnostics(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+static void handleColorDiagnostics(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
- if (!Arg)
+ if (!arg)
return;
- if (Arg->getOption().getID() == OPT_color_diagnostics) {
- errorHandler().ColorDiagnostics = true;
- } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
- errorHandler().ColorDiagnostics = false;
+ if (arg->getOption().getID() == OPT_color_diagnostics) {
+ errorHandler().colorDiagnostics = true;
+ } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
+ errorHandler().colorDiagnostics = false;
} else {
- StringRef S = Arg->getValue();
- if (S == "always")
- errorHandler().ColorDiagnostics = true;
- else if (S == "never")
- errorHandler().ColorDiagnostics = false;
- else if (S != "auto")
- error("unknown option: --color-diagnostics=" + S);
- }
-}
-
-static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
- StringRef S = Arg->getValue();
- if (S != "windows" && S != "posix")
- error("invalid response file quoting: " + S);
- if (S == "windows")
+ StringRef s = arg->getValue();
+ if (s == "always")
+ errorHandler().colorDiagnostics = true;
+ else if (s == "never")
+ errorHandler().colorDiagnostics = false;
+ else if (s != "auto")
+ error("unknown option: --color-diagnostics=" + s);
+ }
+}
+
+static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
+ StringRef s = arg->getValue();
+ if (s != "windows" && s != "posix")
+ error("invalid response file quoting: " + s);
+ if (s == "windows")
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
@@ -767,104 +785,111 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
}
// Parses a given list of options.
-opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
+opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
// Make InputArgList from string vectors.
- unsigned MissingIndex;
- unsigned MissingCount;
+ unsigned missingIndex;
+ unsigned missingCount;
// We need to get the quoting style for response files before parsing all
// options so we parse here before and ignore all the options but
// --rsp-quoting.
- opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
+ opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
- SmallVector<const char *, 256> ExpandedArgv(Argv.data(), Argv.data() + Argv.size());
- cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv);
- Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex,
- MissingCount);
+ SmallVector<const char *, 256> expandedArgv(argv.data(),
+ argv.data() + argv.size());
+ cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
+ args = table.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()) {
- std::string Msg = "Command line:";
- for (const char *S : ExpandedArgv)
- Msg += " " + std::string(S);
- message(Msg);
+ if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
+ std::string msg = "Command line:";
+ for (const char *s : expandedArgv)
+ msg += " " + std::string(s);
+ message(msg);
}
// Save the command line after response file expansion so we can write it to
// the PDB if necessary.
- Config->Argv = {ExpandedArgv.begin(), ExpandedArgv.end()};
+ config->argv = {expandedArgv.begin(), expandedArgv.end()};
// Handle /WX early since it converts missing argument warnings to errors.
- errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false);
+ errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false);
- if (MissingCount)
- fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
+ if (missingCount)
+ fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
- handleColorDiagnostics(Args);
+ handleColorDiagnostics(args);
- for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- warn("ignoring unknown argument: " + Arg->getSpelling());
+ for (auto *arg : args.filtered(OPT_UNKNOWN)) {
+ std::string nearest;
+ if (table.findNearest(arg->getAsString(args), nearest) > 1)
+ warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
+ else
+ warn("ignoring unknown argument '" + arg->getAsString(args) +
+ "', did you mean '" + nearest + "'");
+ }
- if (Args.hasArg(OPT_lib))
+ if (args.hasArg(OPT_lib))
warn("ignoring /lib since it's not the first argument");
- return Args;
+ return args;
}
// 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;
- SmallVector<const char *, 16> Rest;
+ArgParser::parseDirectives(StringRef s) {
+ std::vector<StringRef> exports;
+ SmallVector<const char *, 16> rest;
- for (StringRef Tok : tokenize(S)) {
- if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:"))
- Exports.push_back(Tok.substr(strlen("/export:")));
+ for (StringRef tok : tokenize(s)) {
+ if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:"))
+ exports.push_back(tok.substr(strlen("/export:")));
else
- Rest.push_back(Tok.data());
+ rest.push_back(tok.data());
}
// Make InputArgList from unparsed string vectors.
- unsigned MissingIndex;
- unsigned MissingCount;
+ unsigned missingIndex;
+ unsigned missingCount;
- opt::InputArgList Args = Table.ParseArgs(Rest, MissingIndex, MissingCount);
+ opt::InputArgList args = table.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->getSpelling());
- return {std::move(Args), std::move(Exports)};
+ 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)};
}
// link.exe has an interesting feature. If LINK or _LINK_ environment
// variables exist, their contents are handled as command line strings.
// So you can pass extra arguments using them.
-opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Argv) {
+opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
// Concatenate LINK env and command line arguments, and then parse them.
- if (Optional<std::string> S = Process::GetEnv("LINK")) {
- std::vector<const char *> V = tokenize(*S);
- Argv.insert(std::next(Argv.begin()), V.begin(), V.end());
+ if (Optional<std::string> s = Process::GetEnv("LINK")) {
+ std::vector<const char *> v = tokenize(*s);
+ argv.insert(std::next(argv.begin()), v.begin(), v.end());
}
- if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
- std::vector<const char *> V = tokenize(*S);
- Argv.insert(std::next(Argv.begin()), V.begin(), V.end());
+ if (Optional<std::string> s = Process::GetEnv("_LINK_")) {
+ std::vector<const char *> v = tokenize(*s);
+ argv.insert(std::next(argv.begin()), v.begin(), v.end());
}
- return parse(Argv);
+ return parse(argv);
}
-std::vector<const char *> ArgParser::tokenize(StringRef S) {
- SmallVector<const char *, 16> Tokens;
- cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
- return std::vector<const char *>(Tokens.begin(), Tokens.end());
+std::vector<const char *> ArgParser::tokenize(StringRef s) {
+ SmallVector<const char *, 16> tokens;
+ cl::TokenizeWindowsCommandLine(s, saver, tokens);
+ return std::vector<const char *>(tokens.begin(), tokens.end());
}
-void printHelp(const char *Argv0) {
+void printHelp(const char *argv0) {
COFFOptTable().PrintHelp(outs(),
- (std::string(Argv0) + " [options] file...").c_str(),
+ (std::string(argv0) + " [options] file...").c_str(),
"LLVM Linker", false);
}
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index 34ea360fa925..2b2818de3889 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -1,9 +1,8 @@
//===- ICF.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -38,33 +37,32 @@ using namespace llvm;
namespace lld {
namespace coff {
-static Timer ICFTimer("ICF", Timer::root());
+static Timer icfTimer("ICF", Timer::root());
class ICF {
public:
- void run(ArrayRef<Chunk *> V);
+ void run(ArrayRef<Chunk *> v);
private:
- void segregate(size_t Begin, size_t End, bool Constant);
+ void segregate(size_t begin, size_t end, bool constant);
- bool assocEquals(const SectionChunk *A, const SectionChunk *B);
+ bool assocEquals(const SectionChunk *a, const SectionChunk *b);
- bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
- bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
+ bool equalsConstant(const SectionChunk *a, const SectionChunk *b);
+ bool equalsVariable(const SectionChunk *a, const SectionChunk *b);
- uint32_t getHash(SectionChunk *C);
- bool isEligible(SectionChunk *C);
+ bool isEligible(SectionChunk *c);
- size_t findBoundary(size_t Begin, size_t End);
+ size_t findBoundary(size_t begin, size_t end);
- void forEachClassRange(size_t Begin, size_t End,
- std::function<void(size_t, size_t)> Fn);
+ void forEachClassRange(size_t begin, size_t end,
+ std::function<void(size_t, size_t)> fn);
- void forEachClass(std::function<void(size_t, size_t)> Fn);
+ void forEachClass(std::function<void(size_t, size_t)> fn);
- std::vector<SectionChunk *> Chunks;
- int Cnt = 0;
- std::atomic<bool> Repeat = {false};
+ std::vector<SectionChunk *> chunks;
+ int cnt = 0;
+ std::atomic<bool> repeat = {false};
};
// Returns true if section S is subject of ICF.
@@ -78,143 +76,144 @@ private:
// merge read-only sections in a couple of cases where the address of the
// section is insignificant to the user program and the behaviour matches that
// of the Visual C++ linker.
-bool ICF::isEligible(SectionChunk *C) {
+bool ICF::isEligible(SectionChunk *c) {
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
- bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
- if (!C->isCOMDAT() || !C->Live || Writable)
+ bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
+ if (!c->isCOMDAT() || !c->live || writable)
return false;
// Code sections are eligible.
- if (C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
+ if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
return true;
// .pdata and .xdata unwind info sections are eligible.
- StringRef OutSecName = C->getSectionName().split('$').first;
- if (OutSecName == ".pdata" || OutSecName == ".xdata")
+ StringRef outSecName = c->getSectionName().split('$').first;
+ if (outSecName == ".pdata" || outSecName == ".xdata")
return true;
// So are vtables.
- if (C->Sym && C->Sym->getName().startswith("??_7"))
+ if (c->sym && c->sym->getName().startswith("??_7"))
return true;
// Anything else not in an address-significance table is eligible.
- return !C->KeepUnique;
+ return !c->keepUnique;
}
// Split an equivalence class into smaller classes.
-void ICF::segregate(size_t Begin, size_t End, bool Constant) {
- while (Begin < End) {
+void ICF::segregate(size_t begin, size_t end, bool constant) {
+ while (begin < end) {
// Divide [Begin, End) into two. Let Mid be the start index of the
// second group.
- auto Bound = std::stable_partition(
- Chunks.begin() + Begin + 1, Chunks.begin() + End, [&](SectionChunk *S) {
- if (Constant)
- return equalsConstant(Chunks[Begin], S);
- return equalsVariable(Chunks[Begin], S);
+ auto bound = std::stable_partition(
+ chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) {
+ if (constant)
+ return equalsConstant(chunks[begin], s);
+ return equalsVariable(chunks[begin], s);
});
- size_t Mid = Bound - Chunks.begin();
+ size_t mid = bound - chunks.begin();
// Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an
// equivalence class ID because every group ends with a unique index.
- for (size_t I = Begin; I < Mid; ++I)
- Chunks[I]->Class[(Cnt + 1) % 2] = Mid;
+ for (size_t i = begin; i < mid; ++i)
+ chunks[i]->eqClass[(cnt + 1) % 2] = mid;
// If we created a group, we need to iterate the main loop again.
- if (Mid != End)
- Repeat = true;
+ if (mid != end)
+ repeat = true;
- Begin = Mid;
+ begin = mid;
}
}
// 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->SectionName.startswith(".debug") &&
- C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y")
- Classes.push_back(C->Class[Cnt % 2]);
- return Classes;
+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;
};
- return ChildClasses(A) == ChildClasses(B);
+ return childClasses(a) == childClasses(b);
}
// Compare "non-moving" part of two sections, namely everything
// except relocation targets.
-bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
- if (A->Relocs.size() != B->Relocs.size())
+bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) {
+ if (a->relocsSize != b->relocsSize)
return false;
// Compare relocations.
- auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- if (R1.Type != R2.Type ||
- R1.VirtualAddress != R2.VirtualAddress) {
+ auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
+ if (r1.Type != r2.Type ||
+ r1.VirtualAddress != r2.VirtualAddress) {
return false;
}
- Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
- Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
- if (B1 == B2)
+ Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
+ Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
+ if (b1 == b2)
return true;
- if (auto *D1 = dyn_cast<DefinedRegular>(B1))
- if (auto *D2 = dyn_cast<DefinedRegular>(B2))
- return D1->getValue() == D2->getValue() &&
- D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
+ if (auto *d1 = dyn_cast<DefinedRegular>(b1))
+ if (auto *d2 = dyn_cast<DefinedRegular>(b2))
+ return d1->getValue() == d2->getValue() &&
+ d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
return false;
};
- if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
+ if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(),
+ b->getRelocs().begin(), eq))
return false;
// Compare section attributes and contents.
- return A->getOutputCharacteristics() == B->getOutputCharacteristics() &&
- A->SectionName == B->SectionName &&
- A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
- A->Checksum == B->Checksum && A->getContents() == B->getContents() &&
- assocEquals(A, B);
+ return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
+ a->getSectionName() == b->getSectionName() &&
+ a->header->SizeOfRawData == b->header->SizeOfRawData &&
+ a->checksum == b->checksum && a->getContents() == b->getContents() &&
+ assocEquals(a, b);
}
// Compare "moving" part of two sections, namely relocation targets.
-bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
+bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) {
// Compare relocations.
- auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
- Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
- if (B1 == B2)
+ auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
+ Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
+ Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
+ if (b1 == b2)
return true;
- if (auto *D1 = dyn_cast<DefinedRegular>(B1))
- if (auto *D2 = dyn_cast<DefinedRegular>(B2))
- return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
+ if (auto *d1 = dyn_cast<DefinedRegular>(b1))
+ if (auto *d2 = dyn_cast<DefinedRegular>(b2))
+ return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
return false;
};
- return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(),
- Eq) &&
- assocEquals(A, B);
+ return std::equal(a->getRelocs().begin(), a->getRelocs().end(),
+ b->getRelocs().begin(), eq) &&
+ assocEquals(a, b);
}
// Find the first Chunk after Begin that has a different class from Begin.
-size_t ICF::findBoundary(size_t Begin, size_t End) {
- for (size_t I = Begin + 1; I < End; ++I)
- if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2])
- return I;
- return End;
+size_t ICF::findBoundary(size_t begin, size_t end) {
+ for (size_t i = begin + 1; i < end; ++i)
+ if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2])
+ return i;
+ return end;
}
-void ICF::forEachClassRange(size_t Begin, size_t End,
- std::function<void(size_t, size_t)> Fn) {
- while (Begin < End) {
- size_t Mid = findBoundary(Begin, End);
- Fn(Begin, Mid);
- Begin = Mid;
+void ICF::forEachClassRange(size_t begin, size_t end,
+ std::function<void(size_t, size_t)> fn) {
+ while (begin < end) {
+ size_t mid = findBoundary(begin, end);
+ fn(begin, mid);
+ begin = mid;
}
}
// Call Fn on each class group.
-void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
+void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {
// If the number of sections are too small to use threading,
// call Fn sequentially.
- if (Chunks.size() < 1024) {
- forEachClassRange(0, Chunks.size(), Fn);
- ++Cnt;
+ if (chunks.size() < 1024) {
+ forEachClassRange(0, chunks.size(), fn);
+ ++cnt;
return;
}
@@ -222,95 +221,97 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
// The sharding must be completed before any calls to Fn are made
// so that Fn can modify the Chunks in its shard without causing data
// races.
- const size_t NumShards = 256;
- size_t Step = Chunks.size() / NumShards;
- size_t Boundaries[NumShards + 1];
- Boundaries[0] = 0;
- Boundaries[NumShards] = Chunks.size();
- parallelForEachN(1, NumShards, [&](size_t I) {
- Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size());
+ const size_t numShards = 256;
+ size_t step = chunks.size() / numShards;
+ size_t boundaries[numShards + 1];
+ boundaries[0] = 0;
+ boundaries[numShards] = chunks.size();
+ parallelForEachN(1, numShards, [&](size_t i) {
+ boundaries[i] = findBoundary((i - 1) * step, chunks.size());
});
- parallelForEachN(1, NumShards + 1, [&](size_t I) {
- if (Boundaries[I - 1] < Boundaries[I]) {
- forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
+ parallelForEachN(1, numShards + 1, [&](size_t i) {
+ if (boundaries[i - 1] < boundaries[i]) {
+ forEachClassRange(boundaries[i - 1], boundaries[i], fn);
}
});
- ++Cnt;
+ ++cnt;
}
// Merge identical COMDAT sections.
// Two sections are considered the same if their section headers,
// contents and relocations are all the same.
-void ICF::run(ArrayRef<Chunk *> Vec) {
- ScopedTimer T(ICFTimer);
+void ICF::run(ArrayRef<Chunk *> vec) {
+ ScopedTimer t(icfTimer);
// Collect only mergeable sections and group by hash value.
- uint32_t NextId = 1;
- for (Chunk *C : Vec) {
- if (auto *SC = dyn_cast<SectionChunk>(C)) {
- if (isEligible(SC))
- Chunks.push_back(SC);
+ uint32_t nextId = 1;
+ for (Chunk *c : vec) {
+ if (auto *sc = dyn_cast<SectionChunk>(c)) {
+ if (isEligible(sc))
+ chunks.push_back(sc);
else
- SC->Class[0] = NextId++;
+ sc->eqClass[0] = nextId++;
}
}
// Make sure that ICF doesn't merge sections that are being handled by string
// tail merging.
- for (auto &P : MergeChunk::Instances)
- for (SectionChunk *SC : P.second->Sections)
- SC->Class[0] = NextId++;
+ for (MergeChunk *mc : MergeChunk::instances)
+ if (mc)
+ for (SectionChunk *sc : mc->sections)
+ sc->eqClass[0] = nextId++;
// Initially, we use hash values to partition sections.
- parallelForEach(Chunks, [&](SectionChunk *SC) {
- SC->Class[1] = xxHash64(SC->getContents());
+ parallelForEach(chunks, [&](SectionChunk *sc) {
+ sc->eqClass[0] = xxHash64(sc->getContents());
});
// Combine the hashes of the sections referenced by each section into its
// hash.
- parallelForEach(Chunks, [&](SectionChunk *SC) {
- uint32_t Hash = SC->Class[1];
- for (Symbol *B : SC->symbols())
- if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
- Hash ^= Sym->getChunk()->Class[1];
- // Set MSB to 1 to avoid collisions with non-hash classs.
- SC->Class[0] = Hash | (1U << 31);
- });
+ for (unsigned cnt = 0; cnt != 2; ++cnt) {
+ parallelForEach(chunks, [&](SectionChunk *sc) {
+ uint32_t hash = sc->eqClass[cnt % 2];
+ for (Symbol *b : sc->symbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ hash += sym->getChunk()->eqClass[cnt % 2];
+ // Set MSB to 1 to avoid collisions with non-hash classs.
+ sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
+ });
+ }
// From now on, sections in Chunks are ordered so that sections in
// the same group are consecutive in the vector.
- std::stable_sort(Chunks.begin(), Chunks.end(),
- [](SectionChunk *A, SectionChunk *B) {
- return A->Class[0] < B->Class[0];
- });
+ llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) {
+ return a->eqClass[0] < b->eqClass[0];
+ });
// Compare static contents and assign unique IDs for each static content.
- forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
+ forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
// Split groups by comparing relocations until convergence is obtained.
do {
- Repeat = false;
+ repeat = false;
forEachClass(
- [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
- } while (Repeat);
+ [&](size_t begin, size_t end) { segregate(begin, end, false); });
+ } while (repeat);
- log("ICF needed " + Twine(Cnt) + " iterations");
+ log("ICF needed " + Twine(cnt) + " iterations");
// Merge sections in the same classs.
- forEachClass([&](size_t Begin, size_t End) {
- if (End - Begin == 1)
+ forEachClass([&](size_t begin, size_t end) {
+ if (end - begin == 1)
return;
- log("Selected " + Chunks[Begin]->getDebugName());
- for (size_t I = Begin + 1; I < End; ++I) {
- log(" Removed " + Chunks[I]->getDebugName());
- Chunks[Begin]->replace(Chunks[I]);
+ log("Selected " + chunks[begin]->getDebugName());
+ for (size_t i = begin + 1; i < end; ++i) {
+ log(" Removed " + chunks[i]->getDebugName());
+ chunks[begin]->replace(chunks[i]);
}
});
}
// Entry point to ICF.
-void doICF(ArrayRef<Chunk *> Chunks) { ICF().run(Chunks); }
+void doICF(ArrayRef<Chunk *> chunks) { ICF().run(chunks); }
} // namespace coff
} // namespace lld
diff --git a/COFF/ICF.h b/COFF/ICF.h
index 9c54e0c9ec2d..0b3c8fa2ff2e 100644
--- a/COFF/ICF.h
+++ b/COFF/ICF.h
@@ -1,9 +1,8 @@
//===- ICF.h --------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -18,7 +17,7 @@ namespace coff {
class Chunk;
-void doICF(ArrayRef<Chunk *> Chunks);
+void doICF(ArrayRef<Chunk *> chunks);
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 236c90ef0388..c00d5c5b494e 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -1,15 +1,15 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "Chunks.h"
#include "Config.h"
+#include "DebugTypes.h"
#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
@@ -20,6 +20,10 @@
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Casting.h"
@@ -35,6 +39,7 @@
using namespace llvm;
using namespace llvm::COFF;
+using namespace llvm::codeview;
using namespace llvm::object;
using namespace llvm::support::endian;
@@ -44,80 +49,80 @@ using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
-std::vector<ObjFile *> ObjFile::Instances;
-std::vector<ImportFile *> ImportFile::Instances;
-std::vector<BitcodeFile *> BitcodeFile::Instances;
+std::vector<ObjFile *> ObjFile::instances;
+std::vector<ImportFile *> ImportFile::instances;
+std::vector<BitcodeFile *> BitcodeFile::instances;
/// Checks that Source is compatible with being a weak alias to Target.
/// If Source is Undefined and has no weak alias set, makes it a weak
/// alias to Target.
-static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
- Symbol *Source, Symbol *Target) {
- if (auto *U = dyn_cast<Undefined>(Source)) {
- if (U->WeakAlias && U->WeakAlias != Target) {
+static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
+ Symbol *source, Symbol *target) {
+ if (auto *u = dyn_cast<Undefined>(source)) {
+ if (u->weakAlias && u->weakAlias != target) {
// Weak aliases as produced by GCC are named in the form
// .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name
// of another symbol emitted near the weak symbol.
// Just use the definition from the first object file that defined
// this weak symbol.
- if (Config->MinGW)
+ if (config->mingw)
return;
- Symtab->reportDuplicate(Source, F);
+ symtab->reportDuplicate(source, f);
}
- U->WeakAlias = Target;
+ u->weakAlias = target;
}
}
-ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
+ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
void ArchiveFile::parse() {
// Parse a MemoryBufferRef as an archive file.
- File = CHECK(Archive::create(MB), this);
+ file = CHECK(Archive::create(mb), this);
// Read the symbol table to construct Lazy objects.
- for (const Archive::Symbol &Sym : File->symbols())
- Symtab->addLazy(this, Sym);
+ for (const Archive::Symbol &sym : file->symbols())
+ symtab->addLazy(this, sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
-void ArchiveFile::addMember(const Archive::Symbol *Sym) {
- const Archive::Child &C =
- CHECK(Sym->getMember(),
- "could not get the member for symbol " + Sym->getName());
+void ArchiveFile::addMember(const Archive::Symbol *sym) {
+ const Archive::Child &c =
+ CHECK(sym->getMember(),
+ "could not get the member for symbol " + sym->getName());
// Return an empty buffer if we have already returned the same buffer.
- if (!Seen.insert(C.getChildOffset()).second)
+ if (!seen.insert(c.getChildOffset()).second)
return;
- Driver->enqueueArchiveMember(C, Sym->getName(), getName());
+ driver->enqueueArchiveMember(c, sym->getName(), getName());
}
-std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
- std::vector<MemoryBufferRef> V;
- Error Err = Error::success();
- for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
- Archive::Child C =
- CHECK(COrErr,
- File->getFileName() + ": could not get the child of the archive");
- MemoryBufferRef MBRef =
- CHECK(C.getMemoryBufferRef(),
- File->getFileName() +
+std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
+ std::vector<MemoryBufferRef> v;
+ Error err = Error::success();
+ for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) {
+ Archive::Child c =
+ CHECK(cOrErr,
+ file->getFileName() + ": could not get the child of the archive");
+ MemoryBufferRef mbref =
+ CHECK(c.getMemoryBufferRef(),
+ file->getFileName() +
": could not get the buffer for a child of the archive");
- V.push_back(MBRef);
+ v.push_back(mbref);
}
- if (Err)
- fatal(File->getFileName() +
- ": Archive::children failed: " + toString(std::move(Err)));
- return V;
+ if (err)
+ fatal(file->getFileName() +
+ ": Archive::children failed: " + toString(std::move(err)));
+ return v;
}
void ObjFile::parse() {
// Parse a memory buffer as a COFF file.
- std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), this);
+ std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
- if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
- Bin.release();
- COFFObj.reset(Obj);
+ if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) {
+ bin.release();
+ coffObj.reset(obj);
} else {
fatal(toString(this) + " is not a COFF file");
}
@@ -125,6 +130,15 @@ void ObjFile::parse() {
// Read section and symbol tables.
initializeChunks();
initializeSymbols();
+ initializeFlags();
+ 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;
}
// We set SectionChunk pointers in the SparseChunks vector to this value
@@ -133,45 +147,42 @@ void ObjFile::parse() {
// an associative section definition together with the parent comdat's leader,
// we set the pointer to either nullptr (to mark the section as discarded) or a
// valid SectionChunk for that section.
-static SectionChunk *const PendingComdat = reinterpret_cast<SectionChunk *>(1);
+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;
- if (auto EC = COFFObj->getSection(I, Sec))
- fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
-
- if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
- SparseChunks[I] = PendingComdat;
+ 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);
+ if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
+ sparseChunks[i] = pendingComdat;
else
- SparseChunks[I] = readSection(I, nullptr, "");
+ sparseChunks[i] = readSection(i, nullptr, "");
}
}
-SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
- const coff_aux_section_definition *Def,
- StringRef LeaderName) {
- const coff_section *Sec;
- if (auto EC = COFFObj->getSection(SectionNumber, Sec))
- fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
-
- StringRef Name;
- if (auto EC = COFFObj->getSectionName(Sec, Name))
- fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
- EC.message());
-
- if (Name == ".drectve") {
- ArrayRef<uint8_t> Data;
- COFFObj->getSectionContents(Sec, Data);
- Directives = std::string((const char *)Data.data(), Data.size());
+SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
+ const coff_aux_section_definition *def,
+ StringRef leaderName) {
+ const coff_section *sec = getSection(sectionNumber);
+
+ StringRef name;
+ if (Expected<StringRef> e = coffObj->getSectionName(sec))
+ name = *e;
+ else
+ fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " +
+ toString(e.takeError()));
+
+ if (name == ".drectve") {
+ ArrayRef<uint8_t> data;
+ cantFail(coffObj->getSectionContents(sec, data));
+ directives = StringRef((const char *)data.data(), data.size());
return nullptr;
}
- if (Name == ".llvm_addrsig") {
- AddrsigSec = Sec;
+ if (name == ".llvm_addrsig") {
+ addrsigSec = sec;
return nullptr;
}
@@ -186,377 +197,648 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
// and then write it to a separate .pdb file.
// Ignore DWARF debug info unless /debug is given.
- if (!Config->Debug && Name.startswith(".debug_"))
+ if (!config->debug && name.startswith(".debug_"))
return nullptr;
- if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
+ if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
return nullptr;
- auto *C = make<SectionChunk>(this, Sec);
- if (Def)
- C->Checksum = Def->CheckSum;
+ auto *c = make<SectionChunk>(this, sec);
+ if (def)
+ c->checksum = def->CheckSum;
+
+ // link.exe uses the presence of .rsrc$01 for LNK4078, so match that.
+ if (name == ".rsrc$01")
+ isResourceObjFile = true;
// CodeView sections are stored to a different vector because they are not
// linked in the regular manner.
- if (C->isCodeView())
- DebugChunks.push_back(C);
- else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y")
- GuardFidChunks.push_back(C);
- else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y")
- GuardLJmpChunks.push_back(C);
- else if (Name == ".sxdata")
- SXDataChunks.push_back(C);
- else if (Config->TailMerge && Sec->NumberOfRelocations == 0 &&
- Name == ".rdata" && LeaderName.startswith("??_C@"))
+ if (c->isCodeView())
+ debugChunks.push_back(c);
+ else if (name == ".gfids$y")
+ guardFidChunks.push_back(c);
+ else if (name == ".gljmp$y")
+ guardLJmpChunks.push_back(c);
+ else if (name == ".sxdata")
+ 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
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
// for string literals) are subject to string tail merging.
- MergeChunk::addSection(C);
+ MergeChunk::addSection(c);
else
- Chunks.push_back(C);
+ chunks.push_back(c);
- return C;
+ return c;
}
void ObjFile::readAssociativeDefinition(
- COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
- readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj()));
+ COFFSymbolRef sym, const coff_aux_section_definition *def) {
+ readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
}
-void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym,
- const coff_aux_section_definition *Def,
- uint32_t ParentSection) {
- SectionChunk *Parent = SparseChunks[ParentSection];
+void ObjFile::readAssociativeDefinition(COFFSymbolRef sym,
+ const coff_aux_section_definition *def,
+ uint32_t parentIndex) {
+ SectionChunk *parent = sparseChunks[parentIndex];
+ int32_t sectionNumber = sym.getSectionNumber();
+
+ auto diag = [&]() {
+ StringRef name, parentName;
+ coffObj->getSymbolName(sym, name);
+
+ const coff_section *parentSec = getSection(parentIndex);
+ if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
+ parentName = *e;
+ error(toString(this) + ": associative comdat " + name + " (sec " +
+ Twine(sectionNumber) + ") has invalid reference to section " +
+ parentName + " (sec " + Twine(parentIndex) + ")");
+ };
- // If the parent is pending, it probably means that its section definition
- // appears after us in the symbol table. Leave the associated section as
- // pending; we will handle it during the second pass in initializeSymbols().
- if (Parent == PendingComdat)
+ if (parent == pendingComdat) {
+ // This can happen if an associative comdat refers to another associative
+ // comdat that appears after it (invalid per COFF spec) or to a section
+ // without any symbols.
+ diag();
return;
+ }
// Check whether the parent is prevailing. If it is, so are we, and we read
// the section; otherwise mark it as discarded.
- int32_t SectionNumber = Sym.getSectionNumber();
- if (Parent) {
- SparseChunks[SectionNumber] = readSection(SectionNumber, Def, "");
- if (SparseChunks[SectionNumber])
- Parent->addAssociative(SparseChunks[SectionNumber]);
+ if (parent) {
+ SectionChunk *c = readSection(sectionNumber, def, "");
+ sparseChunks[sectionNumber] = c;
+ if (c) {
+ c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE;
+ parent->addAssociative(c);
+ }
} else {
- SparseChunks[SectionNumber] = nullptr;
+ sparseChunks[sectionNumber] = nullptr;
}
}
void ObjFile::recordPrevailingSymbolForMingw(
- COFFSymbolRef Sym, DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
+ COFFSymbolRef sym, DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
// For comdat symbols in executable sections, where this is the copy
// 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.
- int32_t SectionNumber = Sym.getSectionNumber();
- SectionChunk *SC = SparseChunks[SectionNumber];
- if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- PrevailingSectionMap[Name] = SectionNumber;
+ 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("_");
+ prevailingSectionMap[name] = sectionNumber;
}
}
void ObjFile::maybeAssociateSEHForMingw(
- COFFSymbolRef Sym, const coff_aux_section_definition *Def,
- const DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) {
- // For MinGW, treat .[px]data$<func> as implicitly associative to
- // the symbol <func>.
- auto ParentSym = PrevailingSectionMap.find(Name);
- if (ParentSym != PrevailingSectionMap.end())
- readAssociativeDefinition(Sym, Def, ParentSym->second);
+ COFFSymbolRef sym, const coff_aux_section_definition *def,
+ const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ 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
+ // associative to the symbol <func>.
+ auto parentSym = prevailingSectionMap.find(name);
+ if (parentSym != prevailingSectionMap.end())
+ readAssociativeDefinition(sym, def, parentSym->second);
}
}
-Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
- SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
- if (Sym.isExternal()) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- if (SC)
- return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
+Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
+ SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
+ if (sym.isExternal()) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ if (sc)
+ return symtab->addRegular(this, name, sym.getGeneric(), sc);
// For MinGW symbols named .weak.* that point to a discarded section,
// don't create an Undefined symbol. If nothing ever refers to the symbol,
// everything should be fine. If something actually refers to the symbol
// (e.g. the undefined weak alias), linking will fail due to undefined
// references at the end.
- if (Config->MinGW && Name.startswith(".weak."))
+ if (config->mingw && name.startswith(".weak."))
return nullptr;
- return Symtab->addUndefined(Name, this, false);
+ return symtab->addUndefined(name, this, false);
}
- if (SC)
+ if (sc)
return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
- /*IsExternal*/ false, Sym.getGeneric(), SC);
+ /*IsExternal*/ false, sym.getGeneric(), sc);
return nullptr;
}
void ObjFile::initializeSymbols() {
- uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
- Symbols.resize(NumSymbols);
-
- SmallVector<std::pair<Symbol *, uint32_t>, 8> WeakAliases;
- std::vector<uint32_t> PendingIndexes;
- PendingIndexes.reserve(NumSymbols);
-
- DenseMap<StringRef, uint32_t> PrevailingSectionMap;
- std::vector<const coff_aux_section_definition *> ComdatDefs(
- COFFObj->getNumberOfSections() + 1);
-
- for (uint32_t I = 0; I < NumSymbols; ++I) {
- COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
- bool PrevailingComdat;
- if (COFFSym.isUndefined()) {
- Symbols[I] = createUndefined(COFFSym);
- } else if (COFFSym.isWeakExternal()) {
- Symbols[I] = createUndefined(COFFSym);
- uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
- WeakAliases.emplace_back(Symbols[I], TagIndex);
- } else if (Optional<Symbol *> OptSym =
- createDefined(COFFSym, ComdatDefs, PrevailingComdat)) {
- Symbols[I] = *OptSym;
- if (Config->MinGW && PrevailingComdat)
- recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap);
+ uint32_t numSymbols = coffObj->getNumberOfSymbols();
+ symbols.resize(numSymbols);
+
+ SmallVector<std::pair<Symbol *, uint32_t>, 8> weakAliases;
+ std::vector<uint32_t> pendingIndexes;
+ pendingIndexes.reserve(numSymbols);
+
+ DenseMap<StringRef, uint32_t> prevailingSectionMap;
+ std::vector<const coff_aux_section_definition *> comdatDefs(
+ coffObj->getNumberOfSections() + 1);
+
+ for (uint32_t i = 0; i < numSymbols; ++i) {
+ COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
+ bool prevailingComdat;
+ if (coffSym.isUndefined()) {
+ symbols[i] = createUndefined(coffSym);
+ } else if (coffSym.isWeakExternal()) {
+ symbols[i] = createUndefined(coffSym);
+ uint32_t tagIndex = coffSym.getAux<coff_aux_weak_external>()->TagIndex;
+ weakAliases.emplace_back(symbols[i], tagIndex);
+ } else if (Optional<Symbol *> optSym =
+ createDefined(coffSym, comdatDefs, prevailingComdat)) {
+ symbols[i] = *optSym;
+ if (config->mingw && prevailingComdat)
+ recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap);
} else {
// createDefined() returns None if a symbol belongs to a section that
// was pending at the point when the symbol was read. This can happen in
// two cases:
// 1) section definition symbol for a comdat leader;
- // 2) symbol belongs to a comdat section associated with a section whose
- // section definition symbol appears later in the symbol table.
+ // 2) symbol belongs to a comdat section associated with another section.
// In both of these cases, we can expect the section to be resolved by
// the time we finish visiting the remaining symbols in the symbol
// table. So we postpone the handling of this symbol until that time.
- PendingIndexes.push_back(I);
+ pendingIndexes.push_back(i);
}
- I += COFFSym.getNumberOfAuxSymbols();
+ i += coffSym.getNumberOfAuxSymbols();
}
- for (uint32_t I : PendingIndexes) {
- COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
- if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
- if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
- readAssociativeDefinition(Sym, Def);
- else if (Config->MinGW)
- maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap);
+ for (uint32_t i : pendingIndexes) {
+ COFFSymbolRef sym = check(coffObj->getSymbol(i));
+ if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
+ if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ readAssociativeDefinition(sym, def);
+ else if (config->mingw)
+ maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
- if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- log("comdat section " + Name +
+ if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ log("comdat section " + name +
" without leader and unassociated, discarding");
continue;
}
- Symbols[I] = createRegular(Sym);
+ symbols[i] = createRegular(sym);
}
- for (auto &KV : WeakAliases) {
- Symbol *Sym = KV.first;
- uint32_t Idx = KV.second;
- checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]);
+ for (auto &kv : weakAliases) {
+ Symbol *sym = kv.first;
+ uint32_t idx = kv.second;
+ checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
}
}
-Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- return Symtab->addUndefined(Name, this, Sym.isWeakExternal());
+Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ return symtab->addUndefined(name, this, sym.isWeakExternal());
+}
+
+void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
+ bool &prevailing, DefinedRegular *leader) {
+ if (prevailing)
+ return;
+ // There's already an existing comdat for this symbol: `Leader`.
+ // Use the comdats's selection field to determine if the new
+ // symbol in `Sym` should be discarded, produce a duplicate symbol
+ // error, etc.
+
+ SectionChunk *leaderChunk = nullptr;
+ COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY;
+
+ if (leader->data) {
+ leaderChunk = leader->getChunk();
+ leaderSelection = leaderChunk->selection;
+ } else {
+ // FIXME: comdats from LTO files don't know their selection; treat them
+ // as "any".
+ selection = leaderSelection;
+ }
+
+ if ((selection == IMAGE_COMDAT_SELECT_ANY &&
+ leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) ||
+ (selection == IMAGE_COMDAT_SELECT_LARGEST &&
+ leaderSelection == IMAGE_COMDAT_SELECT_ANY)) {
+ // cl.exe picks "any" for vftables when building with /GR- and
+ // "largest" when building with /GR. To be able to link object files
+ // compiled with each flag, "any" and "largest" are merged as "largest".
+ leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST;
+ }
+
+ // Other than that, comdat selections must match. This is a bit more
+ // strict than link.exe which allows merging "any" and "largest" if "any"
+ // is the first symbol the linker sees, and it allows merging "largest"
+ // with everything (!) if "largest" is the first symbol the linker sees.
+ // Making this symmetric independent of which selection is seen first
+ // seems better though.
+ // (This behavior matches ModuleLinker::getComdatResult().)
+ if (selection != leaderSelection) {
+ log(("conflicting comdat type for " + toString(*leader) + ": " +
+ Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
+ " and " + Twine((int)selection) + " in " + toString(this))
+ .str());
+ symtab->reportDuplicate(leader, this);
+ return;
+ }
+
+ switch (selection) {
+ case IMAGE_COMDAT_SELECT_NODUPLICATES:
+ symtab->reportDuplicate(leader, this);
+ break;
+
+ case IMAGE_COMDAT_SELECT_ANY:
+ // Nothing to do.
+ break;
+
+ case IMAGE_COMDAT_SELECT_SAME_SIZE:
+ if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData)
+ symtab->reportDuplicate(leader, this);
+ break;
+
+ case IMAGE_COMDAT_SELECT_EXACT_MATCH: {
+ SectionChunk newChunk(this, getSection(sym));
+ // link.exe only compares section contents here and doesn't complain
+ // if the two comdat sections have e.g. different alignment.
+ // Match that.
+ if (leaderChunk->getContents() != newChunk.getContents())
+ symtab->reportDuplicate(leader, this);
+ break;
+ }
+
+ case IMAGE_COMDAT_SELECT_ASSOCIATIVE:
+ // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE.
+ // (This means lld-link doesn't produce duplicate symbol errors for
+ // associative comdats while link.exe does, but associate comdats
+ // are never extern in practice.)
+ llvm_unreachable("createDefined not called for associative comdats");
+
+ 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);
+ // 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,
+ // or doing one pass that determines what the final largest comdat
+ // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading
+ // only the largest one.
+ replaceSymbol<DefinedRegular>(leader, this, name, /*IsCOMDAT*/ true,
+ /*IsExternal*/ true, sym.getGeneric(),
+ nullptr);
+ prevailing = true;
+ }
+ break;
+
+ case IMAGE_COMDAT_SELECT_NEWEST:
+ llvm_unreachable("should have been rejected earlier");
+ }
}
Optional<Symbol *> ObjFile::createDefined(
- COFFSymbolRef Sym,
- std::vector<const coff_aux_section_definition *> &ComdatDefs,
- bool &Prevailing) {
- Prevailing = false;
- auto GetName = [&]() {
- StringRef S;
- COFFObj->getSymbolName(Sym, S);
- return S;
+ COFFSymbolRef sym,
+ std::vector<const coff_aux_section_definition *> &comdatDefs,
+ bool &prevailing) {
+ prevailing = false;
+ auto getName = [&]() {
+ StringRef s;
+ coffObj->getSymbolName(sym, s);
+ return s;
};
- if (Sym.isCommon()) {
- auto *C = make<CommonChunk>(Sym);
- Chunks.push_back(C);
- return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(),
- C);
+ if (sym.isCommon()) {
+ auto *c = make<CommonChunk>(sym);
+ chunks.push_back(c);
+ return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(),
+ c);
}
- if (Sym.isAbsolute()) {
- StringRef Name = GetName();
+ if (sym.isAbsolute()) {
+ StringRef name = getName();
// Skip special symbols.
- if (Name == "@comp.id")
+ if (name == "@comp.id")
return nullptr;
- if (Name == "@feat.00") {
- Feat00Flags = Sym.getValue();
+ if (name == "@feat.00") {
+ feat00Flags = sym.getValue();
return nullptr;
}
- if (Sym.isExternal())
- return Symtab->addAbsolute(Name, Sym);
- return make<DefinedAbsolute>(Name, Sym);
+ if (sym.isExternal())
+ return symtab->addAbsolute(name, sym);
+ return make<DefinedAbsolute>(name, sym);
}
- int32_t SectionNumber = Sym.getSectionNumber();
- if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
+ int32_t sectionNumber = sym.getSectionNumber();
+ if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
return nullptr;
- if (llvm::COFF::isReservedSectionNumber(SectionNumber))
- fatal(toString(this) + ": " + GetName() +
- " should not refer to special section " + Twine(SectionNumber));
-
- if ((uint32_t)SectionNumber >= SparseChunks.size())
- fatal(toString(this) + ": " + GetName() +
- " should not refer to non-existent section " + Twine(SectionNumber));
-
- // Handle comdat leader symbols.
- if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
- ComdatDefs[SectionNumber] = nullptr;
- Symbol *Leader;
- if (Sym.isExternal()) {
- std::tie(Leader, Prevailing) =
- Symtab->addComdat(this, GetName(), Sym.getGeneric());
+ if (llvm::COFF::isReservedSectionNumber(sectionNumber))
+ fatal(toString(this) + ": " + getName() +
+ " should not refer to special section " + Twine(sectionNumber));
+
+ if ((uint32_t)sectionNumber >= sparseChunks.size())
+ fatal(toString(this) + ": " + getName() +
+ " should not refer to non-existent section " + Twine(sectionNumber));
+
+ // Comdat handling.
+ // A comdat symbol consists of two symbol table entries.
+ // The first symbol entry has the name of the section (e.g. .text), fixed
+ // values for the other fields, and one auxilliary record.
+ // The second symbol entry has the name of the comdat symbol, called the
+ // "comdat leader".
+ // When this function is called for the first symbol entry of a comdat,
+ // it sets comdatDefs and returns None, and when it's called for the second
+ // symbol entry it reads comdatDefs and then sets it back to nullptr.
+
+ // Handle comdat leader.
+ if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) {
+ comdatDefs[sectionNumber] = nullptr;
+ DefinedRegular *leader;
+
+ if (sym.isExternal()) {
+ std::tie(leader, prevailing) =
+ symtab->addComdat(this, getName(), sym.getGeneric());
} else {
- Leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
- /*IsExternal*/ false, Sym.getGeneric());
- Prevailing = true;
+ leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
+ /*IsExternal*/ false, sym.getGeneric());
+ prevailing = true;
}
- if (Prevailing) {
- SectionChunk *C = readSection(SectionNumber, Def, GetName());
- SparseChunks[SectionNumber] = C;
- C->Sym = cast<DefinedRegular>(Leader);
- cast<DefinedRegular>(Leader)->Data = &C->Repl;
- } else {
- SparseChunks[SectionNumber] = nullptr;
+ if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES ||
+ // Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe
+ // doesn't understand IMAGE_COMDAT_SELECT_NEWEST either.
+ def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) {
+ fatal("unknown comdat type " + std::to_string((int)def->Selection) +
+ " for " + getName() + " in " + toString(this));
}
- return Leader;
- }
+ COMDATType selection = (COMDATType)def->Selection;
- // Read associative section definitions and prepare to handle the comdat
- // leader symbol by setting the section's ComdatDefs pointer if we encounter a
- // non-associative comdat.
- if (SparseChunks[SectionNumber] == PendingComdat) {
- if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
- if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
- readAssociativeDefinition(Sym, Def);
- else
- ComdatDefs[SectionNumber] = Def;
+ if (leader->isCOMDAT)
+ handleComdatSelection(sym, selection, prevailing, leader);
+
+ if (prevailing) {
+ SectionChunk *c = readSection(sectionNumber, def, getName());
+ sparseChunks[sectionNumber] = c;
+ c->sym = cast<DefinedRegular>(leader);
+ c->selection = selection;
+ cast<DefinedRegular>(leader)->data = &c->repl;
+ } else {
+ sparseChunks[sectionNumber] = nullptr;
}
+ return leader;
}
- // readAssociativeDefinition() writes to SparseChunks, so need to check again.
- if (SparseChunks[SectionNumber] == PendingComdat)
+ // Prepare to handle the comdat leader symbol by setting the section's
+ // ComdatDefs pointer if we encounter a non-associative comdat.
+ if (sparseChunks[sectionNumber] == pendingComdat) {
+ if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
+ if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ comdatDefs[sectionNumber] = def;
+ }
return None;
+ }
- return createRegular(Sym);
+ return createRegular(sym);
}
MachineTypes ObjFile::getMachineType() {
- if (COFFObj)
- return static_cast<MachineTypes>(COFFObj->getMachine());
+ if (coffObj)
+ return static_cast<MachineTypes>(coffObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
}
-StringRef ltrim1(StringRef S, const char *Chars) {
- if (!S.empty() && strchr(Chars, S[0]))
- return S.substr(1);
- return S;
+ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
+ if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName))
+ return sec->consumeDebugMagic();
+ return {};
+}
+
+// OBJ files systematically store critical informations in a .debug$S stream,
+// even if the TU was compiled with no debug info. At least two records are
+// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
+// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
+// currently used to initialize the hotPatchable member.
+void ObjFile::initializeFlags() {
+ ArrayRef<uint8_t> data = getDebugSection(".debug$S");
+ if (data.empty())
+ return;
+
+ DebugSubsectionArray subsections;
+
+ BinaryStreamReader reader(data, support::little);
+ ExitOnError exitOnErr;
+ exitOnErr(reader.readArray(subsections, data.size()));
+
+ for (const DebugSubsectionRecord &ss : subsections) {
+ if (ss.kind() != DebugSubsectionKind::Symbols)
+ continue;
+
+ unsigned offset = 0;
+
+ // Only parse the first two records. We are only looking for S_OBJNAME
+ // and S_COMPILE3, and they usually appear at the beginning of the
+ // stream.
+ for (unsigned i = 0; i < 2; ++i) {
+ Expected<CVSymbol> sym = readSymbolFromStream(ss.getRecordData(), offset);
+ if (!sym) {
+ consumeError(sym.takeError());
+ return;
+ }
+ if (sym->kind() == SymbolKind::S_COMPILE3) {
+ auto cs =
+ cantFail(SymbolDeserializer::deserializeAs<Compile3Sym>(sym.get()));
+ hotPatchable =
+ (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None;
+ }
+ if (sym->kind() == SymbolKind::S_OBJNAME) {
+ auto objName = cantFail(SymbolDeserializer::deserializeAs<ObjNameSym>(
+ sym.get()));
+ pchSignature = objName.Signature;
+ }
+ offset += sym->length();
+ }
+ }
+}
+
+// Depending on the compilation flags, OBJs can refer to external files,
+// necessary to merge this OBJ into the final PDB. We currently support two
+// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu.
+// And PDB type servers, when compiling with /Zi. This function extracts these
+// dependencies and makes them available as a TpiSource interface (see
+// DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular
+// output even with /Yc and /Yu and with /Zi.
+void ObjFile::initializeDependencies() {
+ if (!config->debug)
+ return;
+
+ bool isPCH = false;
+
+ ArrayRef<uint8_t> data = getDebugSection(".debug$P");
+ if (!data.empty())
+ isPCH = true;
+ else
+ data = getDebugSection(".debug$T");
+
+ if (data.empty())
+ return;
+
+ CVTypeArray types;
+ BinaryStreamReader reader(data, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+
+ CVTypeArray::Iterator firstType = types.begin();
+ if (firstType == types.end())
+ return;
+
+ debugTypes.emplace(types);
+
+ if (isPCH) {
+ debugTypesObj = makePrecompSource(this);
+ return;
+ }
+
+ if (firstType->kind() == LF_TYPESERVER2) {
+ TypeServer2Record ts = cantFail(
+ TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
+ debugTypesObj = makeUseTypeServerSource(this, &ts);
+ return;
+ }
+
+ if (firstType->kind() == LF_PRECOMP) {
+ PrecompRecord precomp = cantFail(
+ TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
+ debugTypesObj = makeUsePrecompSource(this, &precomp);
+ return;
+ }
+
+ debugTypesObj = makeTpiSource(this);
+}
+
+StringRef ltrim1(StringRef s, const char *chars) {
+ if (!s.empty() && strchr(chars, s[0]))
+ return s.substr(1);
+ return s;
}
void ImportFile::parse() {
- const char *Buf = MB.getBufferStart();
- const char *End = MB.getBufferEnd();
- const auto *Hdr = reinterpret_cast<const coff_import_header *>(Buf);
+ const char *buf = mb.getBufferStart();
+ const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
// Check if the total size is valid.
- if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData))
+ if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData)
fatal("broken import library");
// Read names and create an __imp_ symbol.
- 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);
- StringRef ExtName;
- switch (Hdr->getNameType()) {
+ 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);
+ StringRef extName;
+ switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
- ExtName = "";
+ extName = "";
break;
case IMPORT_NAME:
- ExtName = Name;
+ extName = name;
break;
case IMPORT_NAME_NOPREFIX:
- ExtName = ltrim1(Name, "?@_");
+ extName = ltrim1(name, "?@_");
break;
case IMPORT_NAME_UNDECORATE:
- ExtName = ltrim1(Name, "?@_");
- ExtName = ExtName.substr(0, ExtName.find('@'));
+ extName = ltrim1(name, "?@_");
+ extName = extName.substr(0, extName.find('@'));
break;
}
- this->Hdr = Hdr;
- ExternalName = ExtName;
+ this->hdr = hdr;
+ externalName = extName;
- ImpSym = Symtab->addImportData(ImpName, this);
+ impSym = symtab->addImportData(impName, this);
// If this was a duplicate, we logged an error but may continue;
- // in this case, ImpSym is nullptr.
- if (!ImpSym)
+ // in this case, impSym is nullptr.
+ if (!impSym)
return;
- if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
- static_cast<void>(Symtab->addImportData(Name, this));
+ if (hdr->getType() == llvm::COFF::IMPORT_CONST)
+ static_cast<void>(symtab->addImportData(name, this));
// If type is function, we need to create a thunk which jump to an
// address pointed by the __imp_ symbol. (This allows you to call
// DLL functions just like regular non-DLL functions.)
- if (Hdr->getType() == llvm::COFF::IMPORT_CODE)
- ThunkSym = Symtab->addImportThunk(
- Name, cast_or_null<DefinedImportData>(ImpSym), Hdr->Machine);
+ if (hdr->getType() == llvm::COFF::IMPORT_CODE)
+ thunkSym = symtab->addImportThunk(
+ name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
+}
+
+BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive)
+ : InputFile(BitcodeKind, mb) {
+ std::string path = mb.getBufferIdentifier().str();
+ if (config->thinLTOIndexOnly)
+ path = replaceThinLTOSuffix(mb.getBufferIdentifier());
+
+ // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
+ // name. If two archives define two members with the same name, this
+ // causes a collision which result in only one of the objects being taken
+ // 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.
+ MemoryBufferRef mbref(
+ mb.getBuffer(),
+ saver.save(archiveName + path +
+ (archiveName.empty() ? "" : utostr(offsetInArchive))));
+
+ obj = check(lto::InputFile::create(mbref));
}
void BitcodeFile::parse() {
- Obj = check(lto::InputFile::create(MemoryBufferRef(
- MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
- std::vector<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size());
- for (size_t I = 0; I != Obj->getComdatTable().size(); ++I)
- Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I]));
- for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
- StringRef SymName = Saver.save(ObjSym.getName());
- int ComdatIndex = ObjSym.getComdatIndex();
- Symbol *Sym;
- if (ObjSym.isUndefined()) {
- Sym = Symtab->addUndefined(SymName, this, false);
- } else if (ObjSym.isCommon()) {
- Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
- } else if (ObjSym.isWeak() && ObjSym.isIndirect()) {
+ std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
+ for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
+ // FIXME: lto::InputFile doesn't keep enough data to do correct comdat
+ // selection handling.
+ comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i]));
+ for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
+ StringRef symName = saver.save(objSym.getName());
+ int comdatIndex = objSym.getComdatIndex();
+ Symbol *sym;
+ if (objSym.isUndefined()) {
+ sym = symtab->addUndefined(symName, this, false);
+ } else if (objSym.isCommon()) {
+ sym = symtab->addCommon(this, symName, objSym.getCommonSize());
+ } else if (objSym.isWeak() && objSym.isIndirect()) {
// Weak external.
- Sym = Symtab->addUndefined(SymName, this, true);
- std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
- Symbol *Alias = Symtab->addUndefined(Saver.save(Fallback));
- checkAndSetWeakAlias(Symtab, this, Sym, Alias);
- } else if (ComdatIndex != -1) {
- if (SymName == Obj->getComdatTable()[ComdatIndex])
- Sym = Comdat[ComdatIndex].first;
- else if (Comdat[ComdatIndex].second)
- Sym = Symtab->addRegular(this, SymName);
+ sym = symtab->addUndefined(symName, this, true);
+ std::string fallback = objSym.getCOFFWeakExternalFallback();
+ Symbol *alias = symtab->addUndefined(saver.save(fallback));
+ checkAndSetWeakAlias(symtab, this, sym, alias);
+ } else if (comdatIndex != -1) {
+ if (symName == obj->getComdatTable()[comdatIndex])
+ sym = comdat[comdatIndex].first;
+ else if (comdat[comdatIndex].second)
+ sym = symtab->addRegular(this, symName);
else
- Sym = Symtab->addUndefined(SymName, this, false);
+ sym = symtab->addUndefined(symName, this, false);
} else {
- Sym = Symtab->addRegular(this, SymName);
+ sym = symtab->addRegular(this, symName);
}
- Symbols.push_back(Sym);
+ symbols.push_back(sym);
+ if (objSym.isUsed())
+ config->gcroot.push_back(sym);
}
- Directives = Obj->getCOFFLinkerOpts();
+ directives = obj->getCOFFLinkerOpts();
}
MachineTypes BitcodeFile::getMachineType() {
- switch (Triple(Obj->getTargetTriple()).getArch()) {
+ switch (Triple(obj->getTargetTriple()).getArch()) {
case Triple::x86_64:
return AMD64;
case Triple::x86:
@@ -569,22 +851,31 @@ MachineTypes BitcodeFile::getMachineType() {
return IMAGE_FILE_MACHINE_UNKNOWN;
}
}
+
+std::string replaceThinLTOSuffix(StringRef path) {
+ StringRef suffix = config->thinLTOObjectSuffixReplace.first;
+ StringRef repl = config->thinLTOObjectSuffixReplace.second;
+
+ if (path.consume_back(suffix))
+ return (path + repl).str();
+ return path;
+}
} // namespace coff
} // 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);
+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 lld::toString(const coff::InputFile *File) {
- if (!File)
+std::string lld::toString(const coff::InputFile *file) {
+ if (!file)
return "<internal>";
- if (File->ParentName.empty())
- return File->getName();
+ if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
+ return file->getName();
- return (getBasename(File->ParentName) + "(" + getBasename(File->getName()) +
+ return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
")")
.str();
}
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index ec802f2d0300..dfad9814a397 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -1,9 +1,8 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -33,7 +32,7 @@ class DbiModuleDescriptorBuilder;
namespace lld {
namespace coff {
-std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *File);
+std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
using llvm::COFF::MachineTypes;
@@ -47,20 +46,22 @@ class Chunk;
class Defined;
class DefinedImportData;
class DefinedImportThunk;
+class DefinedRegular;
class Lazy;
class SectionChunk;
class Symbol;
class Undefined;
+class TpiSource;
// The root class of input files.
class InputFile {
public:
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
- Kind kind() const { return FileKind; }
+ Kind kind() const { return fileKind; }
virtual ~InputFile() {}
// Returns the filename.
- StringRef getName() const { return MB.getBufferIdentifier(); }
+ StringRef getName() const { return mb.getBufferIdentifier(); }
// Reads a file (the constructor doesn't do that).
virtual void parse() = 0;
@@ -68,158 +69,195 @@ public:
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
- MemoryBufferRef MB;
+ MemoryBufferRef mb;
// An archive file name if this file is created from an archive.
- StringRef ParentName;
+ StringRef parentName;
// Returns .drectve section contents if exist.
- StringRef getDirectives() { return StringRef(Directives).trim(); }
+ StringRef getDirectives() { return directives; }
protected:
- InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
+ InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {}
- std::string Directives;
+ StringRef directives;
private:
- const Kind FileKind;
+ const Kind fileKind;
};
// .lib or .a file.
class ArchiveFile : public InputFile {
public:
- explicit ArchiveFile(MemoryBufferRef M);
- static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
+ explicit ArchiveFile(MemoryBufferRef m);
+ static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
void parse() override;
// Enqueues an archive member load for the given symbol. If we've already
// enqueued a load for the same archive member, this function does nothing,
// which ensures that we don't load the same member more than once.
- void addMember(const Archive::Symbol *Sym);
+ void addMember(const Archive::Symbol *sym);
private:
- std::unique_ptr<Archive> File;
- std::string Filename;
- llvm::DenseSet<uint64_t> Seen;
+ std::unique_ptr<Archive> file;
+ llvm::DenseSet<uint64_t> seen;
};
// .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile {
public:
- explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
- static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
+ explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
+ static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override;
MachineTypes getMachineType() override;
- ArrayRef<Chunk *> getChunks() { return Chunks; }
- ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
- ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; }
- ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; }
- ArrayRef<SectionChunk *> getGuardLJmpChunks() { return GuardLJmpChunks; }
- ArrayRef<Symbol *> getSymbols() { return Symbols; }
-
- // Returns a Symbol object for the SymbolIndex'th symbol in the
+ ArrayRef<Chunk *> getChunks() { return chunks; }
+ ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
+ ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; }
+ ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
+ ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
+ ArrayRef<Symbol *> getSymbols() { return symbols; }
+
+ ArrayRef<uint8_t> getDebugSection(StringRef secName);
+
+ // Returns a Symbol object for the symbolIndex'th symbol in the
// underlying object file.
- Symbol *getSymbol(uint32_t SymbolIndex) {
- return Symbols[SymbolIndex];
+ Symbol *getSymbol(uint32_t symbolIndex) {
+ return symbols[symbolIndex];
}
// Returns the underlying COFF file.
- COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
+ COFFObjectFile *getCOFFObj() { return coffObj.get(); }
- // Whether the object was already merged into the final PDB or not
- bool wasProcessedForPDB() const { return !!ModuleDBI; }
+ // Add a symbol for a range extension thunk. Return the new symbol table
+ // index. This index can be used to modify a relocation.
+ uint32_t addRangeThunkSymbol(Symbol *thunk) {
+ symbols.push_back(thunk);
+ return symbols.size() - 1;
+ }
- static std::vector<ObjFile *> Instances;
+ static std::vector<ObjFile *> instances;
// Flags in the absolute @feat.00 symbol if it is present. These usually
// indicate if an object was compiled with certain security features enabled
// like stack guard, safeseh, /guard:cf, or other things.
- uint32_t Feat00Flags = 0;
+ uint32_t feat00Flags = 0;
// True if this object file is compatible with SEH. COFF-specific and
// x86-only. COFF spec 5.10.1. The .sxdata section.
- bool hasSafeSEH() { return Feat00Flags & 0x1; }
+ bool hasSafeSEH() { return feat00Flags & 0x1; }
// True if this file was compiled with /guard:cf.
- bool hasGuardCF() { return Feat00Flags & 0x800; }
+ bool hasGuardCF() { return feat00Flags & 0x800; }
// Pointer to the PDB module descriptor builder. Various debug info records
// will reference object files by "module index", which is here. Things like
// source files and section contributions are also recorded here. Will be null
// if we are not producing a PDB.
- llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr;
+ llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr;
- const coff_section *AddrsigSec = nullptr;
+ const coff_section *addrsigSec = nullptr;
// When using Microsoft precompiled headers, this is the PCH's key.
// The same key is used by both the precompiled object, and objects using the
// precompiled object. Any difference indicates out-of-date objects.
- llvm::Optional<uint32_t> PCHSignature;
+ llvm::Optional<uint32_t> pchSignature;
+
+ // Whether this is an object file created from .res files.
+ bool isResourceObjFile = false;
+
+ // Whether this file was compiled with /hotpatch.
+ bool hotPatchable = false;
+
+ // Whether the object was already merged into the final PDB.
+ bool mergedIntoPDB = false;
+
+ // If the OBJ has a .debug$T stream, this tells how it will be handled.
+ TpiSource *debugTypesObj = nullptr;
+
+ // The .debug$T stream if there's one.
+ llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
private:
+ const coff_section* getSection(uint32_t i);
+ const coff_section *getSection(COFFSymbolRef sym) {
+ return getSection(sym.getSectionNumber());
+ }
+
void initializeChunks();
void initializeSymbols();
+ void initializeFlags();
+ void initializeDependencies();
SectionChunk *
- readSection(uint32_t SectionNumber,
- const llvm::object::coff_aux_section_definition *Def,
- StringRef LeaderName);
+ readSection(uint32_t sectionNumber,
+ const llvm::object::coff_aux_section_definition *def,
+ StringRef leaderName);
void readAssociativeDefinition(
- COFFSymbolRef COFFSym,
- const llvm::object::coff_aux_section_definition *Def);
+ COFFSymbolRef coffSym,
+ const llvm::object::coff_aux_section_definition *def);
void readAssociativeDefinition(
- COFFSymbolRef COFFSym,
- const llvm::object::coff_aux_section_definition *Def,
- uint32_t ParentSection);
+ COFFSymbolRef coffSym,
+ const llvm::object::coff_aux_section_definition *def,
+ uint32_t parentSection);
void recordPrevailingSymbolForMingw(
- COFFSymbolRef COFFSym,
- llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+ COFFSymbolRef coffSym,
+ llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap);
void maybeAssociateSEHForMingw(
- COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def,
- const llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+ COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def,
+ const llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap);
+
+ // Given a new symbol Sym with comdat selection Selection, if the new
+ // symbol is not (yet) Prevailing and the existing comdat leader set to
+ // Leader, emits a diagnostic if the new symbol and its selection doesn't
+ // match the existing symbol and its selection. If either old or new
+ // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace
+ // the existing leader. In that case, Prevailing is set to true.
+ void handleComdatSelection(COFFSymbolRef sym,
+ llvm::COFF::COMDATType &selection,
+ bool &prevailing, DefinedRegular *leader);
llvm::Optional<Symbol *>
- createDefined(COFFSymbolRef Sym,
+ createDefined(COFFSymbolRef sym,
std::vector<const llvm::object::coff_aux_section_definition *>
- &ComdatDefs,
- bool &PrevailingComdat);
- Symbol *createRegular(COFFSymbolRef Sym);
- Symbol *createUndefined(COFFSymbolRef Sym);
+ &comdatDefs,
+ bool &prevailingComdat);
+ Symbol *createRegular(COFFSymbolRef sym);
+ Symbol *createUndefined(COFFSymbolRef sym);
- std::unique_ptr<COFFObjectFile> COFFObj;
+ std::unique_ptr<COFFObjectFile> coffObj;
// List of all chunks defined by this file. This includes both section
// chunks and non-section chunks for common symbols.
- std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> chunks;
// CodeView debug info sections.
- std::vector<SectionChunk *> DebugChunks;
+ std::vector<SectionChunk *> debugChunks;
// 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;
+ 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;
+ 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;
+ std::vector<Symbol *> symbols;
};
// This type represents import library members that contain DLL names
@@ -227,23 +265,23 @@ private:
// for details about the format.
class ImportFile : public InputFile {
public:
- explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M) {}
+ explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {}
- static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
+ static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
- static std::vector<ImportFile *> Instances;
+ static std::vector<ImportFile *> instances;
- Symbol *ImpSym = nullptr;
- Symbol *ThunkSym = nullptr;
- std::string DLLName;
+ Symbol *impSym = nullptr;
+ Symbol *thunkSym = nullptr;
+ std::string dllName;
private:
void parse() override;
public:
- StringRef ExternalName;
- const coff_import_header *Hdr;
- Chunk *Location = nullptr;
+ StringRef externalName;
+ const coff_import_header *hdr;
+ Chunk *location = nullptr;
// We want to eliminate dllimported symbols if no one actually refers them.
// These "Live" bits are used to keep track of which import library members
@@ -253,28 +291,31 @@ public:
// symbols provided by this import library member. We also track whether the
// imported symbol is used separately from whether the thunk is used in order
// to avoid creating unnecessary thunks.
- bool Live = !Config->DoGC;
- bool ThunkLive = !Config->DoGC;
+ bool live = !config->doGC;
+ bool thunkLive = !config->doGC;
};
// Used for LTO.
class BitcodeFile : public InputFile {
public:
- explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
- static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- ArrayRef<Symbol *> getSymbols() { return Symbols; }
+ BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive);
+ static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
+ ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override;
- static std::vector<BitcodeFile *> Instances;
- std::unique_ptr<llvm::lto::InputFile> Obj;
+ static std::vector<BitcodeFile *> instances;
+ std::unique_ptr<llvm::lto::InputFile> obj;
private:
void parse() override;
- std::vector<Symbol *> Symbols;
+ std::vector<Symbol *> symbols;
};
+
+std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff
-std::string toString(const coff::InputFile *File);
+std::string toString(const coff::InputFile *file);
} // namespace lld
#endif
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index 92d9ff0937c0..eb3c60d66077 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -1,9 +1,8 @@
//===- LTO.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -11,6 +10,7 @@
#include "Config.h"
#include "InputFiles.h"
#include "Symbols.h"
+#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
@@ -18,6 +18,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Caching.h"
#include "llvm/LTO/Config.h"
@@ -41,112 +42,165 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::coff;
-static std::unique_ptr<lto::LTO> createLTO() {
- lto::Config C;
- C.Options = InitTargetOptionsFromCodeGenFlags();
+// 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) {
+ std::error_code ec;
+ auto ret =
+ llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
+ if (ec) {
+ error("cannot open " + file + ": " + ec.message());
+ return nullptr;
+ }
+ return ret;
+}
+
+static std::string getThinLTOOutputFile(StringRef path) {
+ return lto::getThinLTOOutputFile(path,
+ config->thinLTOPrefixReplace.first,
+ config->thinLTOPrefixReplace.second);
+}
+
+static lto::Config createConfig() {
+ lto::Config c;
+ c.Options = initTargetOptionsFromCodeGenFlags();
// Always emit a section per function/datum with LTO. LLVM LTO should get most
// of the benefit of linker GC, but there are still opportunities for ICF.
- C.Options.FunctionSections = true;
- C.Options.DataSections = true;
+ c.Options.FunctionSections = true;
+ c.Options.DataSections = true;
// Use static reloc model on 32-bit x86 because it usually results in more
// compact code, and because there are also known code generation bugs when
// using the PIC model (see PR34306).
- if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386)
- C.RelocModel = Reloc::Static;
+ if (config->machine == COFF::IMAGE_FILE_MACHINE_I386)
+ c.RelocModel = Reloc::Static;
else
- C.RelocModel = Reloc::PIC_;
- C.DisableVerify = true;
- C.DiagHandler = diagnosticHandler;
- C.OptLevel = Config->LTOO;
- C.CPU = GetCPUStr();
- C.MAttrs = GetMAttrs();
-
- if (Config->SaveTemps)
- checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".",
+ c.RelocModel = Reloc::PIC_;
+ c.DisableVerify = true;
+ c.DiagHandler = diagnosticHandler;
+ c.OptLevel = config->ltoo;
+ c.CPU = getCPUStr();
+ c.MAttrs = getMAttrs();
+ c.CGOptLevel = args::getCGOptLevel(config->ltoo);
+
+ if (config->saveTemps)
+ checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
/*UseInputModulePath*/ true));
- lto::ThinBackend Backend;
- if (Config->ThinLTOJobs != 0)
- Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
- return llvm::make_unique<lto::LTO>(std::move(C), Backend,
- Config->LTOPartitions);
+ return c;
}
-BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
+BitcodeCompiler::BitcodeCompiler() {
+ // Initialize indexFile.
+ if (!config->thinLTOIndexOnlyArg.empty())
+ indexFile = openFile(config->thinLTOIndexOnlyArg);
+
+ // Initialize ltoObj.
+ lto::ThinBackend backend;
+ if (config->thinLTOIndexOnly) {
+ auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
+ backend = lto::createWriteIndexesThinBackend(
+ config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
+ } else if (config->thinLTOJobs != 0) {
+ backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ }
+
+ ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
+ config->ltoPartitions);
+}
BitcodeCompiler::~BitcodeCompiler() = default;
-static void undefine(Symbol *S) { replaceSymbol<Undefined>(S, S->getName()); }
+static void undefine(Symbol *s) { replaceSymbol<Undefined>(s, s->getName()); }
+
+void BitcodeCompiler::add(BitcodeFile &f) {
+ lto::InputFile &obj = *f.obj;
+ unsigned symNum = 0;
+ std::vector<Symbol *> symBodies = f.getSymbols();
+ std::vector<lto::SymbolResolution> resols(symBodies.size());
-void BitcodeCompiler::add(BitcodeFile &F) {
- lto::InputFile &Obj = *F.Obj;
- unsigned SymNum = 0;
- std::vector<Symbol *> SymBodies = F.getSymbols();
- std::vector<lto::SymbolResolution> Resols(SymBodies.size());
+ if (config->thinLTOIndexOnly)
+ thinIndices.insert(obj.getName());
// Provide a resolution to the LTO API for each symbol.
- for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
- Symbol *Sym = SymBodies[SymNum];
- lto::SymbolResolution &R = Resols[SymNum];
- ++SymNum;
+ for (const lto::InputFile::Symbol &objSym : obj.symbols()) {
+ Symbol *sym = symBodies[symNum];
+ lto::SymbolResolution &r = resols[symNum];
+ ++symNum;
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
// reports two symbols for module ASM defined. Without this check, lld
// flags an undefined in IR with a definition in ASM as prevailing.
// Once IRObjectFile is fixed to report only one symbol this hack can
// be removed.
- R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F;
- R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
- if (R.Prevailing)
- undefine(Sym);
+ r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
+ r.VisibleToRegularObj = sym->isUsedInRegularObj;
+ if (r.Prevailing)
+ undefine(sym);
}
- checkError(LTOObj->add(std::move(F.Obj), Resols));
+ checkError(ltoObj->add(std::move(f.obj), resols));
}
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
std::vector<StringRef> BitcodeCompiler::compile() {
- unsigned MaxTasks = LTOObj->getMaxTasks();
- Buf.resize(MaxTasks);
- Files.resize(MaxTasks);
+ unsigned maxTasks = ltoObj->getMaxTasks();
+ buf.resize(maxTasks);
+ files.resize(maxTasks);
// The /lldltocache option specifies the path to a directory in which to cache
// native object files for ThinLTO incremental builds. If a path was
// specified, configure LTO to use it as the cache directory.
- lto::NativeObjectCache Cache;
- if (!Config->LTOCache.empty())
- Cache = check(lto::localCache(
- Config->LTOCache, [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- Files[Task] = std::move(MB);
+ lto::NativeObjectCache cache;
+ if (!config->ltoCache.empty())
+ cache = check(lto::localCache(
+ config->ltoCache, [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
}));
- checkError(LTOObj->run(
- [&](size_t Task) {
+ checkError(ltoObj->run(
+ [&](size_t task) {
return llvm::make_unique<lto::NativeObjectStream>(
- llvm::make_unique<raw_svector_ostream>(Buf[Task]));
+ llvm::make_unique<raw_svector_ostream>(buf[task]));
},
- Cache));
+ 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");
+ }
+
+ // ThinLTO with index only option is required to generate only the index
+ // files. After that, we exit from linker and ThinLTO backend runs in a
+ // distributed environment.
+ if (config->thinLTOIndexOnly) {
+ if (indexFile)
+ indexFile->close();
+ return {};
+ }
- if (!Config->LTOCache.empty())
- pruneCache(Config->LTOCache, Config->LTOCachePolicy);
+ if (!config->ltoCache.empty())
+ pruneCache(config->ltoCache, config->ltoCachePolicy);
- std::vector<StringRef> Ret;
- for (unsigned I = 0; I != MaxTasks; ++I) {
- if (Buf[I].empty())
+ std::vector<StringRef> ret;
+ for (unsigned i = 0; i != maxTasks; ++i) {
+ if (buf[i].empty())
continue;
- if (Config->SaveTemps) {
- if (I == 0)
- saveBuffer(Buf[I], Config->OutputFile + ".lto.obj");
+ if (config->saveTemps) {
+ if (i == 0)
+ saveBuffer(buf[i], config->outputFile + ".lto.obj");
else
- saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.obj");
+ saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj");
}
- Ret.emplace_back(Buf[I].data(), Buf[I].size());
+ ret.emplace_back(buf[i].data(), buf[i].size());
}
- for (std::unique_ptr<MemoryBuffer> &File : Files)
- if (File)
- Ret.push_back(File->getBuffer());
+ for (std::unique_ptr<MemoryBuffer> &file : files)
+ if (file)
+ ret.push_back(file->getBuffer());
- return Ret;
+ return ret;
}
diff --git a/COFF/LTO.h b/COFF/LTO.h
index f00924654780..2a0cfa061c95 100644
--- a/COFF/LTO.h
+++ b/COFF/LTO.h
@@ -1,9 +1,8 @@
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -22,7 +21,9 @@
#define LLD_COFF_LTO_H
#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>
@@ -43,13 +44,15 @@ public:
BitcodeCompiler();
~BitcodeCompiler();
- void add(BitcodeFile &F);
+ void add(BitcodeFile &f);
std::vector<StringRef> compile();
private:
- std::unique_ptr<llvm::lto::LTO> LTOObj;
- std::vector<SmallString<0>> Buf;
- std::vector<std::unique_ptr<MemoryBuffer>> Files;
+ std::unique_ptr<llvm::lto::LTO> ltoObj;
+ std::vector<SmallString<0>> buf;
+ std::vector<std::unique_ptr<MemoryBuffer>> files;
+ std::unique_ptr<llvm::raw_fd_ostream> indexFile;
+ llvm::DenseSet<StringRef> thinIndices;
};
}
}
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
index fd4894250223..f98cf8fa6130 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -1,9 +1,8 @@
//===- MapFile.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -24,7 +23,7 @@
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "llvm/Support/Parallel.h"
+#include "lld/Common/Threads.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -33,93 +32,93 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::coff;
-typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>
- SymbolMapTy;
+using SymbolMapTy =
+ DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
-static const std::string Indent8 = " "; // 8 spaces
-static const std::string Indent16 = " "; // 16 spaces
+static const std::string indent8 = " "; // 8 spaces
+static const std::string 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);
+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;
+ 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);
+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();
+ for (auto &it : ret) {
+ SmallVectorImpl<DefinedRegular *> &v = it.second;
+ std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
+ return a->getRVA() < b->getRVA();
});
}
- return Ret;
+ 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());
- for_each_n(parallel::par, (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]);
+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;
+ 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 coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
- if (Config->MapFile.empty())
+void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
+ if (config->mapFile.empty())
return;
- std::error_code EC;
- raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
- if (EC)
- fatal("cannot open " + Config->MapFile + ": " + EC.message());
+ std::error_code ec;
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
+ if (ec)
+ fatal("cannot open " + config->mapFile + ": " + 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);
+ 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";
+ 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 (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)
+ for (Chunk *c : sec->chunks) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
continue;
- writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment);
- OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName()
+ 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';
+ for (DefinedRegular *sym : sectionSyms[sc])
+ os << symStr[sym] << '\n';
}
}
}
diff --git a/COFF/MapFile.h b/COFF/MapFile.h
index 0d0d68ce3ead..2bf01bd07285 100644
--- a/COFF/MapFile.h
+++ b/COFF/MapFile.h
@@ -1,9 +1,8 @@
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -15,7 +14,7 @@
namespace lld {
namespace coff {
class OutputSection;
-void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections);
+void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections);
}
}
diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp
index 18b1c9c2529f..6d34cb864e3c 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -1,9 +1,8 @@
//===- MarkLive.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -16,57 +15,57 @@
namespace lld {
namespace coff {
-static Timer GCTimer("GC", Timer::root());
+static Timer gctimer("GC", Timer::root());
// Set live bit on for each reachable chunk. Unmarked (unreachable)
// COMDAT chunks will be ignored by Writer, so they will be excluded
// from the final output.
-void markLive(ArrayRef<Chunk *> Chunks) {
- ScopedTimer T(GCTimer);
+void markLive(ArrayRef<Chunk *> chunks) {
+ ScopedTimer t(gctimer);
// We build up a worklist of sections which have been marked as live. We only
// push into the worklist when we discover an unmarked section, and we mark
// as we push, so sections never appear twice in the list.
- SmallVector<SectionChunk *, 256> Worklist;
+ SmallVector<SectionChunk *, 256> worklist;
// COMDAT section chunks are dead by default. Add non-COMDAT chunks.
- for (Chunk *C : Chunks)
- if (auto *SC = dyn_cast<SectionChunk>(C))
- if (SC->Live)
- Worklist.push_back(SC);
+ for (Chunk *c : chunks)
+ if (auto *sc = dyn_cast<SectionChunk>(c))
+ if (sc->live)
+ worklist.push_back(sc);
- auto Enqueue = [&](SectionChunk *C) {
- if (C->Live)
+ auto enqueue = [&](SectionChunk *c) {
+ if (c->live)
return;
- C->Live = true;
- Worklist.push_back(C);
+ c->live = true;
+ worklist.push_back(c);
};
- auto AddSym = [&](Symbol *B) {
- if (auto *Sym = dyn_cast<DefinedRegular>(B))
- Enqueue(Sym->getChunk());
- else if (auto *Sym = dyn_cast<DefinedImportData>(B))
- Sym->File->Live = true;
- else if (auto *Sym = dyn_cast<DefinedImportThunk>(B))
- Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true;
+ auto addSym = [&](Symbol *b) {
+ if (auto *sym = dyn_cast<DefinedRegular>(b))
+ enqueue(sym->getChunk());
+ else if (auto *sym = dyn_cast<DefinedImportData>(b))
+ sym->file->live = true;
+ else if (auto *sym = dyn_cast<DefinedImportThunk>(b))
+ sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true;
};
// Add GC root chunks.
- for (Symbol *B : Config->GCRoot)
- AddSym(B);
+ for (Symbol *b : config->gcroot)
+ addSym(b);
- while (!Worklist.empty()) {
- SectionChunk *SC = Worklist.pop_back_val();
- assert(SC->Live && "We mark as live when pushing onto the worklist!");
+ while (!worklist.empty()) {
+ SectionChunk *sc = worklist.pop_back_val();
+ assert(sc->live && "We mark as live when pushing onto the worklist!");
// Mark all symbols listed in the relocation table for this section.
- for (Symbol *B : SC->symbols())
- if (B)
- AddSym(B);
+ for (Symbol *b : sc->symbols())
+ if (b)
+ addSym(b);
// Mark associative sections if any.
- for (SectionChunk *C : SC->children())
- Enqueue(C);
+ for (SectionChunk &c : sc->children())
+ enqueue(&c);
}
}
diff --git a/COFF/MarkLive.h b/COFF/MarkLive.h
index 5b652dd48196..e4e4c31c7c79 100644
--- a/COFF/MarkLive.h
+++ b/COFF/MarkLive.h
@@ -1,9 +1,8 @@
//===- MarkLive.h -----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -16,7 +15,9 @@
namespace lld {
namespace coff {
-void markLive(ArrayRef<Chunk *> Chunks);
+class Chunk;
+
+void markLive(ArrayRef<Chunk *> chunks);
} // namespace coff
} // namespace lld
diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp
index b2c8c4eadca4..2ca8ca0c058c 100644
--- a/COFF/MinGW.cpp
+++ b/COFF/MinGW.cpp
@@ -1,9 +1,8 @@
//===- MinGW.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -19,8 +18,35 @@ using namespace lld::coff;
using namespace llvm;
using namespace llvm::COFF;
-void AutoExporter::initSymbolExcludes() {
- ExcludeSymbolPrefixes = {
+AutoExporter::AutoExporter() {
+ excludeLibs = {
+ "libgcc",
+ "libgcc_s",
+ "libstdc++",
+ "libmingw32",
+ "libmingwex",
+ "libg2c",
+ "libsupc++",
+ "libobjc",
+ "libgcj",
+ "libclang_rt.builtins",
+ "libclang_rt.builtins-aarch64",
+ "libclang_rt.builtins-arm",
+ "libclang_rt.builtins-i386",
+ "libclang_rt.builtins-x86_64",
+ "libc++",
+ "libc++abi",
+ "libunwind",
+ "libmsvcrt",
+ "libucrtbase",
+ };
+
+ excludeObjects = {
+ "crt0.o", "crt1.o", "crt1u.o", "crt2.o", "crt2u.o", "dllcrt1.o",
+ "dllcrt2.o", "gcrt0.o", "gcrt1.o", "gcrt2.o", "crtbegin.o", "crtend.o",
+ };
+
+ excludeSymbolPrefixes = {
// Import symbols
"__imp_",
"__IMPORT_DESCRIPTOR_",
@@ -32,12 +58,14 @@ void AutoExporter::initSymbolExcludes() {
// Artifical symbols such as .refptr
".",
};
- ExcludeSymbolSuffixes = {
+
+ excludeSymbolSuffixes = {
"_iname",
"_NULL_THUNK_DATA",
};
- if (Config->Machine == I386) {
- ExcludeSymbols = {
+
+ if (config->machine == I386) {
+ excludeSymbols = {
"__NULL_IMPORT_DESCRIPTOR",
"__pei386_runtime_relocator",
"_do_pseudo_reloc",
@@ -52,9 +80,9 @@ void AutoExporter::initSymbolExcludes() {
"_DllEntryPoint@12",
"_DllMainCRTStartup@12",
};
- ExcludeSymbolPrefixes.insert("__head_");
+ excludeSymbolPrefixes.insert("__head_");
} else {
- ExcludeSymbols = {
+ excludeSymbols = {
"__NULL_IMPORT_DESCRIPTOR",
"_pei386_runtime_relocator",
"do_pseudo_reloc",
@@ -69,108 +97,70 @@ void AutoExporter::initSymbolExcludes() {
"DllEntryPoint",
"DllMainCRTStartup",
};
- ExcludeSymbolPrefixes.insert("_head_");
+ excludeSymbolPrefixes.insert("_head_");
}
}
-AutoExporter::AutoExporter() {
- ExcludeLibs = {
- "libgcc",
- "libgcc_s",
- "libstdc++",
- "libmingw32",
- "libmingwex",
- "libg2c",
- "libsupc++",
- "libobjc",
- "libgcj",
- "libclang_rt.builtins",
- "libclang_rt.builtins-aarch64",
- "libclang_rt.builtins-arm",
- "libclang_rt.builtins-i386",
- "libclang_rt.builtins-x86_64",
- "libc++",
- "libc++abi",
- "libunwind",
- "libmsvcrt",
- "libucrtbase",
- };
- ExcludeObjects = {
- "crt0.o",
- "crt1.o",
- "crt1u.o",
- "crt2.o",
- "crt2u.o",
- "dllcrt1.o",
- "dllcrt2.o",
- "gcrt0.o",
- "gcrt1.o",
- "gcrt2.o",
- "crtbegin.o",
- "crtend.o",
- };
-}
-
-void AutoExporter::addWholeArchive(StringRef Path) {
- StringRef LibName = sys::path::filename(Path);
+void AutoExporter::addWholeArchive(StringRef path) {
+ StringRef libName = sys::path::filename(path);
// Drop the file extension, to match the processing below.
- LibName = LibName.substr(0, LibName.rfind('.'));
- ExcludeLibs.erase(LibName);
+ libName = libName.substr(0, libName.rfind('.'));
+ excludeLibs.erase(libName);
}
-bool AutoExporter::shouldExport(Defined *Sym) const {
- if (!Sym || !Sym->isLive() || !Sym->getChunk())
+bool AutoExporter::shouldExport(Defined *sym) const {
+ if (!sym || !sym->isLive() || !sym->getChunk())
return false;
// Only allow the symbol kinds that make sense to export; in particular,
// disallow import symbols.
- if (!isa<DefinedRegular>(Sym) && !isa<DefinedCommon>(Sym))
+ if (!isa<DefinedRegular>(sym) && !isa<DefinedCommon>(sym))
return false;
- if (ExcludeSymbols.count(Sym->getName()))
+ if (excludeSymbols.count(sym->getName()))
return false;
- for (StringRef Prefix : ExcludeSymbolPrefixes.keys())
- if (Sym->getName().startswith(Prefix))
+ for (StringRef prefix : excludeSymbolPrefixes.keys())
+ if (sym->getName().startswith(prefix))
return false;
- for (StringRef Suffix : ExcludeSymbolSuffixes.keys())
- if (Sym->getName().endswith(Suffix))
+ for (StringRef suffix : excludeSymbolSuffixes.keys())
+ if (sym->getName().endswith(suffix))
return false;
// If a corresponding __imp_ symbol exists and is defined, don't export it.
- if (Symtab->find(("__imp_" + Sym->getName()).str()))
+ if (symtab->find(("__imp_" + sym->getName()).str()))
return false;
// Check that file is non-null before dereferencing it, symbols not
// originating in regular object files probably shouldn't be exported.
- if (!Sym->getFile())
+ if (!sym->getFile())
return false;
- StringRef LibName = sys::path::filename(Sym->getFile()->ParentName);
+ StringRef libName = sys::path::filename(sym->getFile()->parentName);
// Drop the file extension.
- LibName = LibName.substr(0, LibName.rfind('.'));
- if (!LibName.empty())
- return !ExcludeLibs.count(LibName);
+ libName = libName.substr(0, libName.rfind('.'));
+ if (!libName.empty())
+ return !excludeLibs.count(libName);
- StringRef FileName = sys::path::filename(Sym->getFile()->getName());
- return !ExcludeObjects.count(FileName);
+ StringRef fileName = sys::path::filename(sym->getFile()->getName());
+ return !excludeObjects.count(fileName);
}
-void coff::writeDefFile(StringRef Name) {
- std::error_code EC;
- raw_fd_ostream OS(Name, EC, sys::fs::F_None);
- if (EC)
- fatal("cannot open " + Name + ": " + EC.message());
-
- OS << "EXPORTS\n";
- for (Export &E : Config->Exports) {
- OS << " " << E.ExportName << " "
- << "@" << E.Ordinal;
- if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) {
- if (Def && Def->getChunk() &&
- !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
- OS << " DATA";
+void coff::writeDefFile(StringRef name) {
+ std::error_code ec;
+ raw_fd_ostream os(name, ec, sys::fs::F_None);
+ if (ec)
+ fatal("cannot open " + name + ": " + ec.message());
+
+ os << "EXPORTS\n";
+ for (Export &e : config->exports) {
+ os << " " << e.exportName << " "
+ << "@" << e.ordinal;
+ if (auto *def = dyn_cast_or_null<Defined>(e.sym)) {
+ if (def && def->getChunk() &&
+ !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
+ os << " DATA";
}
- OS << "\n";
+ os << "\n";
}
}
diff --git a/COFF/MinGW.h b/COFF/MinGW.h
index f9c5e3e5c2cc..578a277561ad 100644
--- a/COFF/MinGW.h
+++ b/COFF/MinGW.h
@@ -1,9 +1,8 @@
//===- MinGW.h --------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -23,20 +22,18 @@ class AutoExporter {
public:
AutoExporter();
- void initSymbolExcludes();
-
- void addWholeArchive(StringRef Path);
+ void addWholeArchive(StringRef path);
- llvm::StringSet<> ExcludeSymbols;
- llvm::StringSet<> ExcludeSymbolPrefixes;
- llvm::StringSet<> ExcludeSymbolSuffixes;
- llvm::StringSet<> ExcludeLibs;
- llvm::StringSet<> ExcludeObjects;
+ llvm::StringSet<> excludeSymbols;
+ llvm::StringSet<> excludeSymbolPrefixes;
+ llvm::StringSet<> excludeSymbolSuffixes;
+ llvm::StringSet<> excludeLibs;
+ llvm::StringSet<> excludeObjects;
- bool shouldExport(Defined *Sym) const;
+ bool shouldExport(Defined *sym) const;
};
-void writeDefFile(StringRef Name);
+void writeDefFile(StringRef name);
} // namespace coff
} // namespace lld
diff --git a/COFF/Options.td b/COFF/Options.td
index acf1bc5c8b1d..024b7be8f78d 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -3,11 +3,11 @@ include "llvm/Option/OptParser.td"
// link.exe accepts options starting with either a dash or a slash.
// Flag that takes no arguments.
-class F<string name> : Flag<["/", "-", "-?"], name>;
+class F<string name> : Flag<["/", "-", "/?", "-?"], name>;
// Flag that takes one argument after ":".
class P<string name, string help> :
- Joined<["/", "-", "-?"], name#":">, HelpText<help>;
+ Joined<["/", "-", "/?", "-?"], name#":">, HelpText<help>;
// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the
// flag on and using it suffixed by ":no" turns it off.
@@ -32,6 +32,9 @@ def errorlimit : P<"errorlimit",
def export : P<"export", "Export a function">;
// No help text because /failifmismatch is not intended to be used by the user.
def failifmismatch : P<"failifmismatch", "">;
+def filealign : P<"filealign", "Section alignment in the output file">;
+def functionpadmin : F<"functionpadmin">;
+def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">;
def guard : P<"guard", "Control flow guard">;
def heap : P<"heap", "Size of the heap">;
def ignore : P<"ignore", "Specify warning codes to ignore">;
@@ -64,7 +67,8 @@ def timestamp : P<"timestamp", "Specify the PE header timestamp">;
def version : P<"version", "Specify a version number in the PE header">;
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
-def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
+def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
+ Alias<nodefaultlib>;
def manifest : F<"manifest">, HelpText<"Create .manifest file">;
def manifest_colon : P<
@@ -82,11 +86,11 @@ def manifestinput : P<
// We cannot use multiclass P because class name "incl" is different
// from its command line option name. We do this because "include" is
// a reserved keyword in tablegen.
-def incl : Joined<["/", "-"], "include:">,
+def incl : Joined<["/", "-", "/?", "-?"], "include:">,
HelpText<"Force symbol to be added to symbol table as undefined one">;
// "def" is also a keyword.
-def deffile : Joined<["/", "-"], "def:">,
+def deffile : Joined<["/", "-", "/?", "-?"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
@@ -101,8 +105,12 @@ def noentry : F<"noentry">,
def profile : F<"profile">;
def repro : F<"Brepro">,
HelpText<"Use a hash of the executable as the PE header timestamp">;
-def swaprun_cd : F<"swaprun:cd">;
-def swaprun_net : F<"swaprun:net">;
+def swaprun : P<"swaprun",
+ "Comma-separated list of 'cd' or 'net'">;
+def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
+ HelpText<"Make loader run output binary from swap instead of from CD">;
+def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
+ HelpText<"Make loader run output binary from swap instead of from network">;
def verbose : F<"verbose">;
def wholearchive_flag : F<"wholearchive">;
@@ -112,6 +120,8 @@ def force_unresolved : F<"force:unresolved">,
HelpText<"Allow undefined symbols when creating executables">;
def force_multiple : F<"force:multiple">,
HelpText<"Allow multiply defined symbols when creating executables">;
+def force_multipleres : F<"force:multipleres">,
+ HelpText<"Allow multiply defined resources when creating executables">;
defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
defm allowbind : B<"allowbind", "Enable DLL binding (default)",
@@ -147,37 +157,58 @@ defm tsaware : B<"tsaware",
"Create non-Terminal Server aware executable">;
def help : F<"help">;
-def help_q : Flag<["/?", "-?"], "">, Alias<help>;
+
+// /?? and -?? must be before /? and -? to not confuse lib/Options.
+def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions
+def exclude_all_symbols : F<"exclude-all-symbols">;
def export_all_symbols : F<"export-all-symbols">;
+defm demangle : B<"demangle",
+ "Demangle symbols in output (default)",
+ "Do not demangle symbols in output">;
+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 output_def : Joined<["/", "-"], "output-def:">;
+def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
"Base path used to make relative source file path absolute in PDB">;
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
+def thinlto_emit_imports_files :
+ F<"thinlto-emit-imports-files">,
+ HelpText<"Emit .imports files with -thinlto-index-only">;
+def thinlto_index_only :
+ F<"thinlto-index-only">,
+ HelpText<"Instead of linking, emit ThinLTO index files">;
+def thinlto_index_only_arg : P<
+ "thinlto-index-only",
+ "-thinlto-index-only and also write native module names to file">;
+def thinlto_object_suffix_replace : P<
+ "thinlto-object-suffix-replace",
+ "'old;new' replace old suffix with new suffix in ThinLTO index">;
+def thinlto_prefix_replace: P<
+ "thinlto-prefix-replace",
+ "'old;new' replace old prefix with new prefix in ThinLTO outputs">;
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">;
// Flags for debugging
def lldmap : F<"lldmap">;
-def lldmap_file : Joined<["/", "-"], "lldmap:">;
+def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
def show_timing : F<"time">;
+def summary : F<"summary">;
//==============================================================================
// The flags below do nothing. They are defined only for link.exe compatibility.
//==============================================================================
-class QF<string name> : Joined<["/", "-", "-?"], name#":">;
+class QF<string name> : Joined<["/", "-", "/?", "-?"], name#":">;
-multiclass QB<string name> {
- def "" : F<name>;
- def _no : F<name#":no">;
-}
-
-def functionpadmin : F<"functionpadmin">;
def ignoreidl : F<"ignoreidl">;
def nologo : F<"nologo">;
def throwingnew : F<"throwingnew">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 7862b6ce4cc5..a55e5136e040 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -1,21 +1,23 @@
//===- PDB.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "PDB.h"
#include "Chunks.h"
#include "Config.h"
+#include "DebugTypes.h"
#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "TypeMerger.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Timer.h"
+#include "lld/Common/Threads.h"
#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
@@ -53,7 +55,6 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JamCRC.h"
-#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -65,44 +66,34 @@ using namespace llvm::codeview;
using llvm::object::coff_section;
-static ExitOnError ExitOnErr;
+static ExitOnError exitOnErr;
-static Timer TotalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
+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 TpiStreamLayoutTimer("TPI Stream Layout", TotalPdbLinkTimer);
-static Timer DiskCommitTimer("Commit to Disk", TotalPdbLinkTimer);
+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 tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
+static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
namespace {
-/// Map from type index and item index in a type server PDB to the
-/// corresponding index in the destination PDB.
-struct CVIndexMap {
- SmallVector<TypeIndex, 0> TPIMap;
- SmallVector<TypeIndex, 0> IPIMap;
- bool IsTypeServerMap = false;
- bool IsPrecompiledTypeMap = false;
-};
-
class DebugSHandler;
class PDBLinker {
friend DebugSHandler;
public:
- PDBLinker(SymbolTable *Symtab)
- : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
- IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {
+ PDBLinker(SymbolTable *symtab)
+ : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) {
// 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.
- PDBStrTab.insert("");
+ pdbStrTab.insert("");
}
/// Emit the basic PDB structure: initial streams, headers, etc.
- void initialize(llvm::codeview::DebugInfo *BuildId);
+ void initialize(llvm::codeview::DebugInfo *buildId);
/// Add natvis files specified on the command line.
void addNatvisFiles();
@@ -110,10 +101,13 @@ public:
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
+ /// 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);
+ 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.
@@ -125,20 +119,17 @@ public:
///
/// 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);
+ /// caller-provided objectIndexMap.
+ Expected<const CVIndexMap &> mergeDebugT(ObjFile *file,
+ CVIndexMap *objectIndexMap);
/// Reads and makes available a PDB.
- Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *File,
- const CVType &FirstType);
+ 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.
- Expected<const CVIndexMap &>
- mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType,
- CVIndexMap *ObjectIndexMap);
+ Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap);
/// Reads and makes available a precompiled headers object.
///
@@ -149,109 +140,87 @@ public:
///
/// If the precompiled headers object was already loaded, this function will
/// simply return its (remapped) TPI map.
- Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *File,
- PrecompRecord Precomp);
+ Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *file);
/// Adds a precompiled headers object signature -> TPI mapping.
std::pair<CVIndexMap &, bool /*already there*/>
- registerPrecompiledHeaders(uint32_t Signature);
+ registerPrecompiledHeaders(uint32_t signature);
- void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap,
- std::vector<ulittle32_t *> &StringTableRefs,
- BinaryStreamRef SymData);
+ void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
+ std::vector<ulittle32_t *> &stringTableRefs,
+ BinaryStreamRef symData);
/// Add the section map and section contributions to the PDB.
- void addSections(ArrayRef<OutputSection *> OutputSections,
- ArrayRef<uint8_t> SectionTable);
-
- /// Get the type table or the global type table if /DEBUG:GHASH is enabled.
- TypeCollection &getTypeTable() {
- if (Config->DebugGHashes)
- return GlobalTypeTable;
- return TypeTable;
- }
-
- /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
- TypeCollection &getIDTable() {
- if (Config->DebugGHashes)
- return GlobalIDTable;
- return IDTable;
- }
+ void addSections(ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable);
/// Write the PDB to disk and store the Guid generated for it in *Guid.
- void commit(codeview::GUID *Guid);
-
-private:
- BumpPtrAllocator Alloc;
-
- SymbolTable *Symtab;
+ void commit(codeview::GUID *guid);
- pdb::PDBFileBuilder Builder;
+ // Print statistics regarding the final PDB
+ void printStats();
- /// Type records that will go into the PDB TPI stream.
- MergingTypeTableBuilder TypeTable;
+private:
+ BumpPtrAllocator alloc;
- /// Item records that will go into the PDB IPI stream.
- MergingTypeTableBuilder IDTable;
+ SymbolTable *symtab;
- /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
- GlobalTypeTableBuilder GlobalTypeTable;
+ pdb::PDBFileBuilder builder;
- /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
- GlobalTypeTableBuilder GlobalIDTable;
+ TypeMerger tMerger;
/// PDBs use a single global string table for filenames in the file checksum
/// table.
- DebugStringTableSubsection PDBStrTab;
-
- llvm::SmallString<128> NativePath;
+ DebugStringTableSubsection pdbStrTab;
- /// A list of other PDBs which are loaded during the linking process and which
- /// we need to keep around since the linking operation may reference pointers
- /// inside of these PDBs.
- llvm::SmallVector<std::unique_ptr<pdb::NativeSession>, 2> LoadedPDBs;
+ llvm::SmallString<128> nativePath;
- std::vector<pdb::SecMapEntry> SectionMap;
+ std::vector<pdb::SecMapEntry> sectionMap;
/// Type index mappings of type server PDBs that we've loaded so far.
- std::map<codeview::GUID, CVIndexMap> TypeServerIndexMappings;
+ 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;
+ std::map<uint32_t, CVIndexMap> precompTypeIndexMappings;
- /// List of TypeServer PDBs which cannot be loaded.
- /// Cached to prevent repeated load attempts.
- std::map<codeview::GUID, std::string> MissingTypeServerPDBs;
+ // For statistics
+ uint64_t globalSymbols = 0;
+ uint64_t moduleSymbols = 0;
+ uint64_t publicSymbols = 0;
};
class DebugSHandler {
- PDBLinker &Linker;
+ PDBLinker &linker;
/// The object file whose .debug$S sections we're processing.
- ObjFile &File;
+ 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;
+ 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
/// collect them all here and re-write them after all subsections have been
/// discovered and processed.
- std::vector<DebugFrameDataSubsectionRef> NewFpoFrames;
+ std::vector<DebugFrameDataSubsectionRef> newFpoFrames;
/// Pointers to raw memory that we determine have string table references
/// that need to be re-written. We first process all .debug$S subsections
@@ -259,13 +228,17 @@ class DebugSHandler {
/// up this list as we go. At the end, we use the string table (which must
/// have been discovered by now else it is an error) to re-write these
/// references.
- std::vector<ulittle32_t *> StringTableReferences;
+ std::vector<ulittle32_t *> stringTableReferences;
public:
- DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap)
- : Linker(Linker), File(File), IndexMap(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(lld::coff::SectionChunk &DebugS);
void finish();
};
}
@@ -273,7 +246,7 @@ public:
// Visual Studio's debugger requires absolute paths in various places in the
// PDB to work without additional configuration:
// https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box
-static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) {
+static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
// The default behavior is to produce paths that are valid within the context
// of the machine that you perform the link on. If the linker is running on
// a POSIX system, we will output absolute POSIX paths. If the linker is
@@ -281,408 +254,256 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) {
// user desires any other kind of behavior, they should explicitly pass
// /pdbsourcepath, in which case we will treat the exact string the user
// passed in as the gospel and not normalize, canonicalize it.
- if (sys::path::is_absolute(FileName, sys::path::Style::windows) ||
- sys::path::is_absolute(FileName, sys::path::Style::posix))
+ if (sys::path::is_absolute(fileName, sys::path::Style::windows) ||
+ sys::path::is_absolute(fileName, sys::path::Style::posix))
return;
// It's not absolute in any path syntax. Relative paths necessarily refer to
// the local file system, so we can make it native without ending up with a
// nonsensical path.
- sys::path::native(FileName);
- if (Config->PDBSourcePath.empty()) {
- sys::fs::make_absolute(FileName);
+ if (config->pdbSourcePath.empty()) {
+ sys::path::native(fileName);
+ sys::fs::make_absolute(fileName);
return;
}
- // Only apply native and dot removal to the relative file path. We want to
- // leave the path the user specified untouched since we assume they specified
- // it for a reason.
- sys::path::remove_dots(FileName, /*remove_dot_dots=*/true);
-
- SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
- sys::path::append(AbsoluteFileName, FileName);
- FileName = std::move(AbsoluteFileName);
-}
-
-static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
- StringRef Name) {
- for (SectionChunk *C : Sections)
- if (C->getSectionName() == Name)
- return C;
- return nullptr;
-}
-static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
- StringRef SecName) {
- // First 4 bytes are section magic.
- if (Data.size() < 4)
- fatal(SecName + " too short");
- if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
- fatal(SecName + " has an invalid magic");
- return Data.slice(4);
-}
-
-static ArrayRef<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) {
- if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
- return consumeDebugMagic(Sec->getContents(), SecName);
- return {};
+ // Try to guess whether /PDBSOURCEPATH is a unix path or a windows path.
+ // Since PDB's are more of a Windows thing, we make this conservative and only
+ // decide that it's a unix path if we're fairly certain. Specifically, if
+ // it starts with a forward slash.
+ SmallString<128> absoluteFileName = config->pdbSourcePath;
+ sys::path::Style guessedStyle = absoluteFileName.startswith("/")
+ ? sys::path::Style::posix
+ : sys::path::Style::windows;
+ sys::path::append(absoluteFileName, guessedStyle, fileName);
+ sys::path::native(absoluteFileName, guessedStyle);
+ sys::path::remove_dots(absoluteFileName, true, guessedStyle);
+
+ 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))
+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);
+ 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 = findByName(File->getDebugChunks(), ".debug$H");
- if (!Sec)
+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))
+ ArrayRef<uint8_t> contents = sec->getContents();
+ if (!canUseDebugH(contents))
return None;
- return Contents;
+ return contents;
}
static ArrayRef<GloballyHashedType>
-getHashesFromDebugH(ArrayRef<uint8_t> DebugH) {
- assert(canUseDebugH(DebugH));
+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};
+ 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) {
+static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
+ TypeCollection &typeTable) {
// Start the TPI or IPI stream header.
- TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+ tpiBuilder.setVersionHeader(pdb::PdbTpiV80);
// Flatten the in memory type table and hash each type.
- TypeTable.ForEachRecord([&](TypeIndex TI, const CVType &Type) {
- auto Hash = pdb::hashTypeRecord(Type);
- if (auto E = Hash.takeError())
+ typeTable.ForEachRecord([&](TypeIndex ti, const CVType &type) {
+ auto hash = pdb::hashTypeRecord(type);
+ if (auto e = hash.takeError())
fatal("type hashing error");
- TpiBuilder.addTypeRecord(Type.RecordData, *Hash);
- });
-}
-
-// OBJs usually start their symbol stream with a S_OBJNAME record. This record
-// also contains the signature/key of the current PCH session. The signature
-// must be same for all objects which depend on the precompiled object.
-// Recompiling the precompiled headers will generate a new PCH key and thus
-// invalidate all the dependent objects.
-static uint32_t extractPCHSignature(ObjFile *File) {
- auto DbgIt = find_if(File->getDebugChunks(), [](SectionChunk *C) {
- return C->getSectionName() == ".debug$S";
+ tpiBuilder.addTypeRecord(type.RecordData, *hash);
});
- if (!DbgIt)
- return 0;
-
- ArrayRef<uint8_t> Contents =
- consumeDebugMagic((*DbgIt)->getContents(), ".debug$S");
- DebugSubsectionArray Subsections;
- BinaryStreamReader Reader(Contents, support::little);
- ExitOnErr(Reader.readArray(Subsections, Contents.size()));
-
- for (const DebugSubsectionRecord &SS : Subsections) {
- if (SS.kind() != DebugSubsectionKind::Symbols)
- continue;
-
- // If it's there, the S_OBJNAME record shall come first in the stream.
- Expected<CVSymbol> Sym = readSymbolFromStream(SS.getRecordData(), 0);
- if (!Sym) {
- consumeError(Sym.takeError());
- continue;
- }
- if (auto ObjName = SymbolDeserializer::deserializeAs<ObjNameSym>(Sym.get()))
- return ObjName->Signature;
- }
- return 0;
}
Expected<const CVIndexMap &>
-PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) {
- ScopedTimer T(TypeMergingTimer);
+PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) {
+ ScopedTimer t(typeMergingTimer);
- bool IsPrecompiledHeader = false;
-
- ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
- if (Data.empty()) {
- // Try again, Microsoft precompiled headers use .debug$P instead of
- // .debug$T
- Data = getDebugSection(File, ".debug$P");
- IsPrecompiledHeader = true;
- }
- if (Data.empty())
- return *ObjectIndexMap; // no debug info
+ 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 (IsPrecompiledHeader) {
- uint32_t PCHSignature = extractPCHSignature(File);
- if (PCHSignature == 0)
+ 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() + ")");
+ 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)
+ if (!objectIndexMap->isPrecompiledTypeMap) {
+ auto r = registerPrecompiledHeaders(pchSignature);
+ if (r.second)
fatal(
"A precompiled headers OBJ with the same signature was already "
"provided! (" +
- File->getName() + ")");
+ file->getName() + ")");
- ObjectIndexMap = &R.first;
+ objectIndexMap = &r.first;
}
}
- BinaryByteStream Stream(Data, support::little);
- CVTypeArray Types;
- BinaryStreamReader Reader(Stream);
- if (auto EC = Reader.readArray(Types, Reader.getLength()))
- fatal("Reader::readArray failed: " + toString(std::move(EC)));
-
- auto FirstType = Types.begin();
- if (FirstType == Types.end())
- return *ObjectIndexMap;
-
- if (FirstType->kind() == LF_TYPESERVER2) {
+ 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, *FirstType);
- } else if (FirstType->kind() == LF_PRECOMP) {
+ return maybeMergeTypeServerPDB(file);
+ }
+
+ CVTypeArray &types = *file->debugTypes;
+
+ 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.
- auto E = mergeInPrecompHeaderObj(File, *FirstType, ObjectIndexMap);
- if (!E)
- return E.takeError();
-
- // Drop LF_PRECOMP record from the input stream, as it needs to be replaced
- // with the precompiled headers object type stream.
- // Note that we can't just call Types.drop_front(), as we explicitly want to
- // rebase the stream.
- Types.setUnderlyingStream(
- Types.getUnderlyingStream().drop_front(FirstType->RecordData.size()));
+ 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);
+ 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;
+ ownedHashes = GloballyHashedType::hashTypes(types);
+ hashes = ownedHashes;
}
- if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable,
- ObjectIndexMap->TPIMap, Types, Hashes,
- File->PCHSignature))
+ if (auto err = mergeTypeAndIdRecords(
+ tMerger.globalIDTable, tMerger.globalTypeTable,
+ objectIndexMap->tpiMap, types, hashes, file->pchSignature))
fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(Err)));
+ toString(std::move(err)));
} else {
- if (auto Err =
- mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap,
- Types, File->PCHSignature))
+ if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable,
+ objectIndexMap->tpiMap, types,
+ file->pchSignature))
fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(Err)));
+ toString(std::move(err)));
}
- return *ObjectIndexMap;
+ return *objectIndexMap;
}
-static Expected<std::unique_ptr<pdb::NativeSession>>
-tryToLoadPDB(const codeview::GUID &GuidFromObj, StringRef TSPath) {
- // Ensure the file exists before anything else. We want to return ENOENT,
- // "file not found", even if the path points to a removable device (in which
- // case the return message would be EAGAIN, "resource unavailable try again")
- if (!llvm::sys::fs::exists(TSPath))
- return errorCodeToError(std::error_code(ENOENT, std::generic_category()));
-
- ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
- TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
- if (!MBOrErr)
- return errorCodeToError(MBOrErr.getError());
-
- std::unique_ptr<pdb::IPDBSession> ThisSession;
- if (auto EC = pdb::NativeSession::createFromPdb(
- MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)),
- /*RequiresNullTerminator=*/false),
- ThisSession))
- return std::move(EC);
-
- std::unique_ptr<pdb::NativeSession> NS(
- static_cast<pdb::NativeSession *>(ThisSession.release()));
- pdb::PDBFile &File = NS->getPDBFile();
- auto ExpectedInfo = File.getPDBInfoStream();
- // All PDB Files should have an Info stream.
- if (!ExpectedInfo)
- return ExpectedInfo.takeError();
-
- // 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() != GuidFromObj)
- return make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date);
-
- return std::move(NS);
-}
+Expected<const CVIndexMap &> PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) {
+ Expected<llvm::pdb::NativeSession *> pdbSession = findTypeServerSource(file);
+ if (!pdbSession)
+ return pdbSession.takeError();
-Expected<const CVIndexMap &>
-PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) {
- TypeServer2Record TS;
- if (auto EC =
- TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), TS))
- fatal("error reading record: " + toString(std::move(EC)));
-
- const codeview::GUID &TSId = TS.getGuid();
- StringRef TSPath = TS.getName();
-
- // First, check if the PDB has previously failed to load.
- auto PrevErr = MissingTypeServerPDBs.find(TSId);
- if (PrevErr != MissingTypeServerPDBs.end())
- return createFileError(
- TSPath,
- make_error<StringError>(PrevErr->second, inconvertibleErrorCode()));
+ pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile();
+ pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
- // Second, check if we already loaded a PDB with this GUID. Return the type
- // index mapping if we have it.
- auto Insertion = TypeServerIndexMappings.insert({TSId, CVIndexMap()});
- CVIndexMap &IndexMap = Insertion.first->second;
- if (!Insertion.second)
- return IndexMap;
+ 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;
-
- // Check for a PDB at:
- // 1. The given file path
- // 2. Next to the object file or archive file
- auto ExpectedSession = handleExpected(
- tryToLoadPDB(TSId, 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 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 tryToLoadPDB(TSId, Path);
- },
- [&](std::unique_ptr<ECError> EC) -> Error {
- auto SysErr = EC->convertToErrorCode();
- // Only re-try loading if the previous error was "No such file or
- // directory"
- if (SysErr.category() == std::generic_category() &&
- SysErr.value() == ENOENT)
- return Error::success();
- return Error(std::move(EC));
- });
-
- if (auto E = ExpectedSession.takeError()) {
- TypeServerIndexMappings.erase(TSId);
-
- // Flatten the error to a string, for later display, if the error occurs
- // again on the same PDB.
- std::string ErrMsg;
- raw_string_ostream S(ErrMsg);
- S << E;
- MissingTypeServerPDBs.emplace(TSId, S.str());
-
- return createFileError(TSPath, std::move(E));
+ 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;
}
- pdb::NativeSession *Session = ExpectedSession->get();
-
- // Keep a strong reference to this PDB, so that it's safe to hold pointers
- // into the file.
- LoadedPDBs.push_back(std::move(*ExpectedSession));
-
- auto ExpectedTpi = Session->getPDBFile().getPDBTpiStream();
- if (auto E = ExpectedTpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(E)));
- auto ExpectedIpi = Session->getPDBFile().getPDBIpiStream();
- if (auto E = ExpectedIpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(E)));
-
- if (Config->DebugGHashes) {
+ 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());
- auto IpiHashes =
- GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes);
-
- Optional<uint32_t> EndPrecomp;
+ 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(GlobalTypeTable, IndexMap.TPIMap,
- ExpectedTpi->typeArray(), TpiHashes, EndPrecomp))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+ if (auto err =
+ mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap,
+ expectedTpi->typeArray(), tpiHashes, endPrecomp))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
// Merge IPI.
- if (auto Err =
- mergeIdRecords(GlobalIDTable, IndexMap.TPIMap, IndexMap.IPIMap,
- ExpectedIpi->typeArray(), IpiHashes))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ 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(TypeTable, IndexMap.TPIMap,
- ExpectedTpi->typeArray()))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+ if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap,
+ expectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
// Merge IPI.
- if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
- ExpectedIpi->typeArray()))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ if (maybeIpi) {
+ if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap,
+ indexMap.ipiMap, maybeIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
}
- return IndexMap;
+ return indexMap;
}
-Expected<const CVIndexMap &>
-PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType,
- CVIndexMap *ObjectIndexMap) {
- PrecompRecord Precomp;
- if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType),
- Precomp))
- fatal("error reading record: " + toString(std::move(EC)));
+Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file,
+ CVIndexMap *objectIndexMap) {
+ const PrecompRecord &precomp =
+ retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
- auto E = aquirePrecompObj(File, Precomp);
- if (!E)
- return E.takeError();
+ Expected<const CVIndexMap &> e = aquirePrecompObj(file);
+ if (!e)
+ return e.takeError();
- const CVIndexMap &PrecompIndexMap = *E;
- assert(PrecompIndexMap.IsPrecompiledTypeMap);
+ const CVIndexMap &precompIndexMap = *e;
+ assert(precompIndexMap.isPrecompiledTypeMap);
- if (PrecompIndexMap.TPIMap.empty())
- return PrecompIndexMap;
+ if (precompIndexMap.tpiMap.empty())
+ return Error::success();
- assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
- assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size());
+ 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 *ObjectIndexMap;
+ objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(),
+ precompIndexMap.tpiMap.begin() +
+ precomp.getTypesCount());
+ return Error::success();
}
static bool equals_path(StringRef path1, StringRef path2) {
@@ -694,101 +515,103 @@ static bool equals_path(StringRef path1, StringRef path2) {
}
// Find by name an OBJ provided on the command line
-static ObjFile *findObjByName(StringRef FileNameOnly) {
- SmallString<128> CurrentPath;
+static ObjFile *findObjByName(StringRef fileNameOnly) {
+ SmallString<128> currentPath;
- for (ObjFile *F : ObjFile::Instances) {
- StringRef CurrentFileName = sys::path::filename(F->getName());
+ for (ObjFile *f : ObjFile::instances) {
+ StringRef currentFileName = sys::path::filename(f->getName());
// Compare based solely on the file name (link.exe behavior)
- if (equals_path(CurrentFileName, FileNameOnly))
- return F;
+ if (equals_path(currentFileName, fileNameOnly))
+ 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};
+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};
+ indexMap.isPrecompiledTypeMap = true;
+ return {indexMap, false};
}
-Expected<const CVIndexMap &>
-PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) {
+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;
+ auto r = registerPrecompiledHeaders(precomp.getSignature());
+ if (r.second)
+ return r.first;
- CVIndexMap &IndexMap = 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);
+ 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 = findObjByName(PrecompFileName);
- if (!PrecompFile)
+ auto precompFile = findObjByName(precompFileName);
+ if (!precompFile)
return createFileError(
- PrecompFileName.str(),
+ precompFileName.str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
- addObjFile(PrecompFile, &IndexMap);
+ addObjFile(precompFile, &indexMap);
- if (!PrecompFile->PCHSignature)
- fatal(PrecompFile->getName() + " is not a precompiled headers object");
+ if (!precompFile->pchSignature)
+ fatal(precompFile->getName() + " is not a precompiled headers object");
- if (Precomp.getSignature() != PrecompFile->PCHSignature.getValueOr(0))
+ if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0))
return createFileError(
- Precomp.getPrecompFilePath().str(),
+ precomp.getPrecompFilePath().str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
- return IndexMap;
+ return indexMap;
}
-static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
- if (TI.isSimple())
+static bool remapTypeIndex(TypeIndex &ti, ArrayRef<TypeIndex> typeIndexMap) {
+ if (ti.isSimple())
return true;
- if (TI.toArrayIndex() >= TypeIndexMap.size())
+ if (ti.toArrayIndex() >= typeIndexMap.size())
return false;
- TI = TypeIndexMap[TI.toArrayIndex()];
+ ti = typeIndexMap[ti.toArrayIndex()];
return true;
}
-static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
- MutableArrayRef<uint8_t> RecordBytes,
- const CVIndexMap &IndexMap,
- ArrayRef<TiReference> TypeRefs) {
- MutableArrayRef<uint8_t> Contents =
- RecordBytes.drop_front(sizeof(RecordPrefix));
- for (const TiReference &Ref : TypeRefs) {
- unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
- if (Contents.size() < Ref.Offset + ByteSize)
+static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind,
+ MutableArrayRef<uint8_t> recordBytes,
+ const CVIndexMap &indexMap,
+ ArrayRef<TiReference> typeRefs) {
+ MutableArrayRef<uint8_t> contents =
+ recordBytes.drop_front(sizeof(RecordPrefix));
+ for (const TiReference &ref : typeRefs) {
+ unsigned byteSize = ref.Count * sizeof(TypeIndex);
+ if (contents.size() < ref.Offset + byteSize)
fatal("symbol record too short");
// This can be an item index or a type index. Choose the appropriate map.
- ArrayRef<TypeIndex> TypeOrItemMap = IndexMap.TPIMap;
- bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef;
- if (IsItemIndex && IndexMap.IsTypeServerMap)
- TypeOrItemMap = IndexMap.IPIMap;
-
- MutableArrayRef<TypeIndex> TIs(
- reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
- for (TypeIndex &TI : TIs) {
- if (!remapTypeIndex(TI, TypeOrItemMap)) {
- log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " +
- File->getName() + " with bad " + (IsItemIndex ? "item" : "type") +
- " index 0x" + utohexstr(TI.getIndex()));
- TI = TypeIndex(SimpleTypeKind::NotTranslated);
+ ArrayRef<TypeIndex> typeOrItemMap = indexMap.tpiMap;
+ bool isItemIndex = ref.Kind == TiRefKind::IndexRef;
+ if (isItemIndex && indexMap.isTypeServerMap)
+ typeOrItemMap = indexMap.ipiMap;
+
+ MutableArrayRef<TypeIndex> tIs(
+ reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count);
+ for (TypeIndex &ti : tIs) {
+ if (!remapTypeIndex(ti, typeOrItemMap)) {
+ log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " +
+ file->getName() + " with bad " + (isItemIndex ? "item" : "type") +
+ " index 0x" + utohexstr(ti.getIndex()));
+ ti = TypeIndex(SimpleTypeKind::NotTranslated);
continue;
}
}
@@ -796,26 +619,26 @@ static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
}
static void
-recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> Contents,
- uint32_t Offset,
- std::vector<ulittle32_t *> &StrTableRefs) {
- Contents =
- Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t));
- ulittle32_t *Index = reinterpret_cast<ulittle32_t *>(Contents.data());
- StrTableRefs.push_back(Index);
+recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> contents,
+ uint32_t offset,
+ std::vector<ulittle32_t *> &strTableRefs) {
+ contents =
+ contents.drop_front(offset).take_front(sizeof(support::ulittle32_t));
+ ulittle32_t *index = reinterpret_cast<ulittle32_t *>(contents.data());
+ strTableRefs.push_back(index);
}
static void
-recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents,
- std::vector<ulittle32_t *> &StrTableRefs) {
+recordStringTableReferences(SymbolKind kind, MutableArrayRef<uint8_t> contents,
+ std::vector<ulittle32_t *> &strTableRefs) {
// For now we only handle S_FILESTATIC, but we may need the same logic for
// S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any
// PDBs that contain these types of records, so because of the uncertainty
// they are omitted here until we can prove that it's necessary.
- switch (Kind) {
+ switch (kind) {
case SymbolKind::S_FILESTATIC:
// FileStaticSym::ModFileOffset
- recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs);
+ recordStringTableReferenceAtOffset(contents, 8, strTableRefs);
break;
case SymbolKind::S_DEFRANGE:
case SymbolKind::S_DEFRANGE_SUBFIELD:
@@ -827,21 +650,21 @@ recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents,
}
}
-static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) {
- const RecordPrefix *Prefix =
- reinterpret_cast<const RecordPrefix *>(RecordData.data());
- return static_cast<SymbolKind>(uint16_t(Prefix->RecordKind));
+static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) {
+ const RecordPrefix *prefix =
+ reinterpret_cast<const RecordPrefix *>(recordData.data());
+ return static_cast<SymbolKind>(uint16_t(prefix->RecordKind));
}
/// 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) {
- RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(RecordData.data());
+static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+ TypeCollection &iDTable) {
+ RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
- SymbolKind Kind = symbolKind(RecordData);
+ SymbolKind kind = symbolKind(recordData);
- if (Kind == SymbolKind::S_PROC_ID_END) {
- Prefix->RecordKind = SymbolKind::S_END;
+ if (kind == SymbolKind::S_PROC_ID_END) {
+ prefix->RecordKind = SymbolKind::S_END;
return;
}
@@ -850,89 +673,89 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
// to the PDB file's ID stream index space, but we need to convert this to a
// symbol that refers to the type stream index space. So we remap again from
// ID index space to type index space.
- if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) {
- SmallVector<TiReference, 1> Refs;
- auto Content = RecordData.drop_front(sizeof(RecordPrefix));
- CVSymbol Sym(Kind, RecordData);
- discoverTypeIndicesInSymbol(Sym, Refs);
- assert(Refs.size() == 1);
- assert(Refs.front().Count == 1);
-
- TypeIndex *TI =
- reinterpret_cast<TypeIndex *>(Content.data() + Refs[0].Offset);
- // `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
+ if (kind == SymbolKind::S_GPROC32_ID || kind == SymbolKind::S_LPROC32_ID) {
+ SmallVector<TiReference, 1> refs;
+ auto content = recordData.drop_front(sizeof(RecordPrefix));
+ CVSymbol sym(recordData);
+ discoverTypeIndicesInSymbol(sym, refs);
+ assert(refs.size() == 1);
+ assert(refs.front().Count == 1);
+
+ TypeIndex *ti =
+ reinterpret_cast<TypeIndex *>(content.data() + refs[0].Offset);
+ // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
// the IPI stream, whose `FunctionType` member refers to the TPI stream.
// 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];
+ if (!ti->isSimple() && !ti->isNoneType()) {
+ CVType funcIdData = iDTable.getType(*ti);
+ SmallVector<TypeIndex, 2> indices;
+ discoverTypeIndices(funcIdData, indices);
+ assert(indices.size() == 2);
+ *ti = indices[1];
}
- Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
+ kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
: SymbolKind::S_LPROC32;
- Prefix->RecordKind = uint16_t(Kind);
+ prefix->RecordKind = uint16_t(kind);
}
}
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
/// The object file may not be aligned.
static MutableArrayRef<uint8_t>
-copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef<uint8_t> &AlignedMem) {
- size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
- assert(Size >= 4 && "record too short");
- assert(Size <= MaxRecordLength && "record too long");
- assert(AlignedMem.size() >= Size && "didn't preallocate enough");
+copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef<uint8_t> &alignedMem) {
+ size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb));
+ assert(size >= 4 && "record too short");
+ assert(size <= MaxRecordLength && "record too long");
+ assert(alignedMem.size() >= size && "didn't preallocate enough");
// Copy the symbol record and zero out any padding bytes.
- MutableArrayRef<uint8_t> NewData = AlignedMem.take_front(Size);
- AlignedMem = AlignedMem.drop_front(Size);
- memcpy(NewData.data(), Sym.data().data(), Sym.length());
- memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
+ MutableArrayRef<uint8_t> newData = alignedMem.take_front(size);
+ alignedMem = alignedMem.drop_front(size);
+ memcpy(newData.data(), sym.data().data(), sym.length());
+ memset(newData.data() + sym.length(), 0, size - sym.length());
// Update the record prefix length. It should point to the beginning of the
// next record.
- auto *Prefix = reinterpret_cast<RecordPrefix *>(NewData.data());
- Prefix->RecordLen = Size - 2;
- return NewData;
+ auto *prefix = reinterpret_cast<RecordPrefix *>(newData.data());
+ prefix->RecordLen = size - 2;
+ return newData;
}
struct ScopeRecord {
- ulittle32_t PtrParent;
- ulittle32_t PtrEnd;
+ ulittle32_t ptrParent;
+ ulittle32_t ptrEnd;
};
struct SymbolScope {
- ScopeRecord *OpeningRecord;
- uint32_t ScopeOffset;
+ ScopeRecord *openingRecord;
+ uint32_t scopeOffset;
};
-static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
- uint32_t CurOffset, CVSymbol &Sym) {
- assert(symbolOpensScope(Sym.kind()));
- SymbolScope S;
- S.ScopeOffset = CurOffset;
- S.OpeningRecord = const_cast<ScopeRecord *>(
- reinterpret_cast<const ScopeRecord *>(Sym.content().data()));
- S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset;
- Stack.push_back(S);
+static void scopeStackOpen(SmallVectorImpl<SymbolScope> &stack,
+ uint32_t curOffset, CVSymbol &sym) {
+ assert(symbolOpensScope(sym.kind()));
+ SymbolScope s;
+ s.scopeOffset = curOffset;
+ s.openingRecord = const_cast<ScopeRecord *>(
+ reinterpret_cast<const ScopeRecord *>(sym.content().data()));
+ s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset;
+ stack.push_back(s);
}
-static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
- uint32_t CurOffset, ObjFile *File) {
- if (Stack.empty()) {
- warn("symbol scopes are not balanced in " + File->getName());
+static void scopeStackClose(SmallVectorImpl<SymbolScope> &stack,
+ uint32_t curOffset, InputFile *file) {
+ if (stack.empty()) {
+ warn("symbol scopes are not balanced in " + file->getName());
return;
}
- SymbolScope S = Stack.pop_back_val();
- S.OpeningRecord->PtrEnd = CurOffset;
+ SymbolScope s = stack.pop_back_val();
+ s.openingRecord->ptrEnd = curOffset;
}
-static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
- switch (Sym.kind()) {
+static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) {
+ switch (sym.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_CONSTANT:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
@@ -944,7 +767,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
return false;
// S_UDT records go in the module stream if it is not a global S_UDT.
case SymbolKind::S_UDT:
- return !IsGlobalScope;
+ return !isGlobalScope;
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
case SymbolKind::S_LDATA32:
default:
@@ -952,8 +775,8 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
}
}
-static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) {
- switch (Sym.kind()) {
+static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
+ switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_GDATA32:
// S_LDATA32 goes in both the module stream and the globals stream.
@@ -968,36 +791,36 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) {
return true;
// S_UDT records go in the globals stream if it is a global S_UDT.
case SymbolKind::S_UDT:
- return IsGlobalScope;
+ return isGlobalScope;
default:
return false;
}
}
-static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex,
- unsigned SymOffset, const CVSymbol &Sym) {
- switch (Sym.kind()) {
+static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex,
+ unsigned symOffset, const CVSymbol &sym) {
+ switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_UDT:
case SymbolKind::S_GDATA32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
- Builder.addGlobalSymbol(Sym);
+ builder.addGlobalSymbol(sym);
break;
case SymbolKind::S_GPROC32:
case SymbolKind::S_LPROC32: {
- SymbolRecordKind K = SymbolRecordKind::ProcRefSym;
- if (Sym.kind() == SymbolKind::S_LPROC32)
- K = SymbolRecordKind::LocalProcRef;
- ProcRefSym PS(K);
- PS.Module = ModIndex;
+ SymbolRecordKind k = SymbolRecordKind::ProcRefSym;
+ if (sym.kind() == SymbolKind::S_LPROC32)
+ k = SymbolRecordKind::LocalProcRef;
+ ProcRefSym ps(k);
+ ps.Module = modIndex;
// For some reason, MSVC seems to add one to this value.
- ++PS.Module;
- PS.Name = getSymbolName(Sym);
- PS.SumName = 0;
- PS.SymOffset = SymOffset;
- Builder.addGlobalSymbol(PS);
+ ++ps.Module;
+ ps.Name = getSymbolName(sym);
+ ps.SumName = 0;
+ ps.SymOffset = symOffset;
+ builder.addGlobalSymbol(ps);
break;
}
default:
@@ -1005,229 +828,304 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex,
}
}
-void PDBLinker::mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap,
- std::vector<ulittle32_t *> &StringTableRefs,
- BinaryStreamRef SymData) {
- ArrayRef<uint8_t> SymsBuffer;
- cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer));
- SmallVector<SymbolScope, 4> Scopes;
+void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
+ std::vector<ulittle32_t *> &stringTableRefs,
+ BinaryStreamRef symData) {
+ ArrayRef<uint8_t> symsBuffer;
+ cantFail(symData.readBytes(0, symData.getLength(), symsBuffer));
+ SmallVector<SymbolScope, 4> scopes;
// Iterate every symbol to check if any need to be realigned, and if so, how
// much space we need to allocate for them.
- bool NeedsRealignment = false;
- unsigned TotalRealignedSize = 0;
- auto EC = forEachCodeViewRecord<CVSymbol>(
- SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
- unsigned RealignedSize =
- alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
- NeedsRealignment |= RealignedSize != Sym.length();
- TotalRealignedSize += RealignedSize;
+ bool needsRealignment = false;
+ unsigned totalRealignedSize = 0;
+ auto ec = forEachCodeViewRecord<CVSymbol>(
+ symsBuffer, [&](CVSymbol sym) -> llvm::Error {
+ unsigned realignedSize =
+ alignTo(sym.length(), alignOf(CodeViewContainer::Pdb));
+ needsRealignment |= realignedSize != sym.length();
+ totalRealignedSize += realignedSize;
return Error::success();
});
// If any of the symbol record lengths was corrupt, ignore them all, warn
// about it, and move on.
- if (EC) {
- warn("corrupt symbol records in " + File->getName());
- consumeError(std::move(EC));
+ if (ec) {
+ warn("corrupt symbol records in " + file->getName());
+ consumeError(std::move(ec));
return;
}
// If any symbol needed realignment, allocate enough contiguous memory for
// them all. Typically symbol subsections are small enough that this will not
// cause fragmentation.
- MutableArrayRef<uint8_t> AlignedSymbolMem;
- if (NeedsRealignment) {
- void *AlignedData =
- Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb));
- AlignedSymbolMem = makeMutableArrayRef(
- reinterpret_cast<uint8_t *>(AlignedData), TotalRealignedSize);
+ MutableArrayRef<uint8_t> alignedSymbolMem;
+ if (needsRealignment) {
+ void *alignedData =
+ alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
+ alignedSymbolMem = makeMutableArrayRef(
+ reinterpret_cast<uint8_t *>(alignedData), totalRealignedSize);
}
// Iterate again, this time doing the real work.
- unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset();
- ArrayRef<uint8_t> BulkSymbols;
+ unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset();
+ ArrayRef<uint8_t> bulkSymbols;
cantFail(forEachCodeViewRecord<CVSymbol>(
- SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
+ symsBuffer, [&](CVSymbol sym) -> llvm::Error {
// Align the record if required.
- MutableArrayRef<uint8_t> RecordBytes;
- if (NeedsRealignment) {
- RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem);
- Sym = CVSymbol(Sym.kind(), RecordBytes);
+ MutableArrayRef<uint8_t> recordBytes;
+ if (needsRealignment) {
+ recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem);
+ sym = CVSymbol(recordBytes);
} else {
// Otherwise, we can actually mutate the symbol directly, since we
// copied it to apply relocations.
- RecordBytes = makeMutableArrayRef(
- const_cast<uint8_t *>(Sym.data().data()), Sym.length());
+ recordBytes = makeMutableArrayRef(
+ const_cast<uint8_t *>(sym.data().data()), sym.length());
}
// Discover type index references in the record. Skip it if we don't
// know where they are.
- SmallVector<TiReference, 32> TypeRefs;
- if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
+ SmallVector<TiReference, 32> typeRefs;
+ if (!discoverTypeIndicesInSymbol(sym, typeRefs)) {
log("ignoring unknown symbol record with kind 0x" +
- utohexstr(Sym.kind()));
+ utohexstr(sym.kind()));
return Error::success();
}
// Re-map all the type index references.
- remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap,
- TypeRefs);
+ remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap,
+ typeRefs);
// An object file may have S_xxx_ID symbols, but these get converted to
// "real" symbols in a PDB.
- translateIdSymbols(RecordBytes, getIDTable());
- Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes);
+ translateIdSymbols(recordBytes, tMerger.getIDTable());
+ sym = CVSymbol(recordBytes);
// If this record refers to an offset in the object file's string table,
// add that item to the global PDB string table and re-write the index.
- recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs);
+ recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs);
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
- if (symbolOpensScope(Sym.kind()))
- scopeStackOpen(Scopes, CurSymOffset, Sym);
- else if (symbolEndsScope(Sym.kind()))
- scopeStackClose(Scopes, CurSymOffset, File);
+ if (symbolOpensScope(sym.kind()))
+ scopeStackOpen(scopes, curSymOffset, sym);
+ else if (symbolEndsScope(sym.kind()))
+ scopeStackClose(scopes, curSymOffset, file);
// Add the symbol to the globals stream if necessary. Do this before
// 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()))
- addGlobalSymbol(Builder.getGsiBuilder(),
- File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym);
+ if (symbolGoesInGlobalsStream(sym, scopes.empty())) {
+ addGlobalSymbol(builder.getGsiBuilder(),
+ file->moduleDBI->getModuleIndex(), curSymOffset, sym);
+ ++globalSymbols;
+ }
- if (symbolGoesInModuleStream(Sym, Scopes.empty())) {
+ if (symbolGoesInModuleStream(sym, scopes.empty())) {
// Add symbols to the module in bulk. If this symbol is contiguous
// with the previous run of symbols to add, combine the ranges. If
// not, close the previous range of symbols and start a new one.
- if (Sym.data().data() == BulkSymbols.end()) {
- BulkSymbols = makeArrayRef(BulkSymbols.data(),
- BulkSymbols.size() + Sym.length());
+ if (sym.data().data() == bulkSymbols.end()) {
+ bulkSymbols = makeArrayRef(bulkSymbols.data(),
+ bulkSymbols.size() + sym.length());
} else {
- File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
- BulkSymbols = RecordBytes;
+ file->moduleDBI->addSymbolsInBulk(bulkSymbols);
+ bulkSymbols = recordBytes;
}
- CurSymOffset += Sym.length();
+ curSymOffset += sym.length();
+ ++moduleSymbols;
}
return Error::success();
}));
// Add any remaining symbols we've accumulated.
- File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
+ 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.OutputSectionOff == 0 &&
+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.readRelocTargets();
- DebugChunk.writeTo(Buffer);
- return makeArrayRef(Buffer, DebugChunk.getSize());
+ debugChunk.writeTo(buffer);
+ return makeArrayRef(buffer, debugChunk.getSize());
}
-static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) {
- OutputSection *OS = C->getOutputSection();
- pdb::SectionContrib SC;
- memset(&SC, 0, sizeof(SC));
- SC.ISect = OS->SectionIndex;
- SC.Off = C->getRVA() - OS->getRVA();
- SC.Size = C->getSize();
- if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
- SC.Characteristics = SecChunk->Header->Characteristics;
- SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
- ArrayRef<uint8_t> Contents = SecChunk->getContents();
- JamCRC CRC(0);
- ArrayRef<char> CharContents = makeArrayRef(
- reinterpret_cast<const char *>(Contents.data()), Contents.size());
- CRC.update(CharContents);
- SC.DataCrc = CRC.getCRC();
+static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
+ OutputSection *os = c ? c->getOutputSection() : nullptr;
+ pdb::SectionContrib sc;
+ memset(&sc, 0, sizeof(sc));
+ sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex;
+ sc.Off = c && os ? c->getRVA() - os->getRVA() : 0;
+ sc.Size = c ? c->getSize() : -1;
+ if (auto *secChunk = dyn_cast_or_null<SectionChunk>(c)) {
+ sc.Characteristics = secChunk->header->Characteristics;
+ sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
+ ArrayRef<uint8_t> contents = secChunk->getContents();
+ JamCRC crc(0);
+ ArrayRef<char> charContents = makeArrayRef(
+ reinterpret_cast<const char *>(contents.data()), contents.size());
+ crc.update(charContents);
+ sc.DataCrc = crc.getCRC();
} else {
- SC.Characteristics = OS->Header.Characteristics;
- // FIXME: When we start creating DBI for import libraries, use those here.
- SC.Imod = Modi;
+ sc.Characteristics = os ? os->header.Characteristics : 0;
+ sc.Imod = modi;
}
- SC.RelocCrc = 0; // FIXME
+ sc.RelocCrc = 0; // FIXME
- return SC;
+ return sc;
}
static uint32_t
-translateStringTableIndex(uint32_t ObjIndex,
- const DebugStringTableSubsectionRef &ObjStrTable,
- DebugStringTableSubsection &PdbStrTable) {
- auto ExpectedString = ObjStrTable.getString(ObjIndex);
- if (!ExpectedString) {
+translateStringTableIndex(uint32_t objIndex,
+ const DebugStringTableSubsectionRef &objStrTable,
+ DebugStringTableSubsection &pdbStrTable) {
+ auto expectedString = objStrTable.getString(objIndex);
+ if (!expectedString) {
warn("Invalid string table reference");
- consumeError(ExpectedString.takeError());
+ consumeError(expectedString.takeError());
return 0;
}
- return PdbStrTable.insert(*ExpectedString);
+ return pdbStrTable.insert(*expectedString);
}
-void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) {
- DebugSubsectionArray Subsections;
+void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
+ DebugSubsectionArray subsections;
+
+ ArrayRef<uint8_t> relocatedDebugContents = SectionChunk::consumeDebugMagic(
+ relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName());
- ArrayRef<uint8_t> RelocatedDebugContents = consumeDebugMagic(
- relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName());
+ BinaryStreamReader reader(relocatedDebugContents, support::little);
+ exitOnErr(reader.readArray(subsections, relocatedDebugContents.size()));
- BinaryStreamReader Reader(RelocatedDebugContents, support::little);
- ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
+ for (const DebugSubsectionRecord &ss : subsections) {
+ // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++
+ // runtime have subsections with this bit set.
+ if (uint32_t(ss.kind()) & codeview::SubsectionIgnoreFlag)
+ continue;
- for (const DebugSubsectionRecord &SS : Subsections) {
- switch (SS.kind()) {
+ 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:
- assert(!Checksums.valid() &&
+ assert(!checksums.valid() &&
"Encountered multiple checksum subsections!");
- ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ exitOnErr(checksums.initialize(ss.getRecordData()));
break;
case DebugSubsectionKind::Lines:
// We can add the relocated line table directly to the PDB without
// modification because the file checksum offsets will stay the same.
- File.ModuleDBI->addDebugSubsection(SS);
+ file.moduleDBI->addDebugSubsection(ss);
+ break;
+ case DebugSubsectionKind::InlineeLines:
+ assert(!inlineeLines.valid() &&
+ "Encountered multiple inlinee lines subsections!");
+ exitOnErr(inlineeLines.initialize(ss.getRecordData()));
break;
case DebugSubsectionKind::FrameData: {
// We need to re-write string table indices here, so save off all
// frame data subsections until we've processed the entire list of
// subsections so that we can be sure we have the string table.
- DebugFrameDataSubsectionRef FDS;
- ExitOnErr(FDS.initialize(SS.getRecordData()));
- NewFpoFrames.push_back(std::move(FDS));
+ DebugFrameDataSubsectionRef fds;
+ exitOnErr(fds.initialize(ss.getRecordData()));
+ newFpoFrames.push_back(std::move(fds));
break;
}
case DebugSubsectionKind::Symbols: {
- Linker.mergeSymbolRecords(&File, IndexMap, StringTableReferences,
- SS.getRecordData());
+ linker.mergeSymbolRecords(&file, indexMap, stringTableReferences,
+ ss.getRecordData());
break;
}
+
+ case DebugSubsectionKind::CrossScopeImports:
+ case DebugSubsectionKind::CrossScopeExports:
+ // These appear to relate to cross-module optimization, so we might use
+ // these for ThinLTO.
+ break;
+
+ case DebugSubsectionKind::ILLines:
+ case DebugSubsectionKind::FuncMDTokenMap:
+ case DebugSubsectionKind::TypeMDTokenMap:
+ case DebugSubsectionKind::MergedAssemblyInput:
+ // These appear to relate to .Net assembly info.
+ break;
+
+ case DebugSubsectionKind::CoffSymbolRVA:
+ // Unclear what this is for.
+ break;
+
default:
- // FIXME: Process the rest of the subsections.
+ warn("ignoring unknown debug$S subsection kind 0x" +
+ utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file));
break;
}
}
}
+static Expected<StringRef>
+getFileName(const DebugStringTableSubsectionRef &strings,
+ const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) {
+ auto iter = checksums.getArray().at(fileID);
+ if (iter == checksums.getArray().end())
+ return make_error<CodeViewError>(cv_error_code::no_records);
+ uint32_t offset = iter->FileNameOffset;
+ return strings.getString(offset);
+}
+
+std::shared_ptr<DebugInlineeLinesSubsection>
+DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) {
+ auto newInlineeLines = std::make_shared<DebugInlineeLinesSubsection>(
+ *newChecksums, inlineeLines.hasExtraFiles());
+
+ for (const InlineeSourceLine &line : inlineeLines) {
+ TypeIndex inlinee = line.Header->Inlinee;
+ uint32_t fileID = line.Header->FileID;
+ uint32_t sourceLine = line.Header->SourceLineNum;
+
+ ArrayRef<TypeIndex> typeOrItemMap =
+ indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap;
+ if (!remapTypeIndex(inlinee, typeOrItemMap)) {
+ log("ignoring 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;
+}
+
void DebugSHandler::finish() {
- pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder();
+ pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder();
// 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 (Checksums.valid())
+ if (!cVStrTab.valid()) {
+ if (checksums.valid())
fatal(".debug$S sections with a checksums subsection must also contain a "
"string table subsection");
- if (!StringTableReferences.empty())
+ if (!stringTableReferences.empty())
warn("No StringTable subsection was encountered, but there are string "
"table references");
return;
@@ -1235,186 +1133,231 @@ void DebugSHandler::finish() {
// Rewrite string table indices in the Fpo Data and symbol records to refer to
// the global PDB string table instead of the object file string table.
- for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) {
- const ulittle32_t *Reloc = FDS.getRelocPtr();
- for (codeview::FrameData FD : FDS) {
- FD.RvaStart += *Reloc;
- FD.FrameFunc =
- translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab);
- DbiBuilder.addNewFpoData(FD);
+ for (DebugFrameDataSubsectionRef &fds : newFpoFrames) {
+ const ulittle32_t *reloc = fds.getRelocPtr();
+ for (codeview::FrameData fd : fds) {
+ fd.RvaStart += *reloc;
+ fd.FrameFunc =
+ translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab);
+ dbiBuilder.addNewFpoData(fd);
}
}
- for (ulittle32_t *Ref : StringTableReferences)
- *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab);
+ for (ulittle32_t *ref : stringTableReferences)
+ *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.
- auto NewChecksums = make_unique<DebugChecksumsSubsection>(Linker.PDBStrTab);
- for (FileChecksumEntry &FC : Checksums) {
- SmallString<128> FileName =
- ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
- pdbMakeAbsolute(FileName);
- ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile(
- *File.ModuleDBI, FileName));
- NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
+ auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
+ for (FileChecksumEntry &fc : checksums) {
+ SmallString<128> filename =
+ exitOnErr(cVStrTab.getString(fc.FileNameOffset));
+ pdbMakeAbsolute(filename);
+ exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
+ newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
}
- File.ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+
+ // Rewrite inlinee item indices if present.
+ if (inlineeLines.valid())
+ file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get()));
+
+ file.moduleDBI->addDebugSubsection(std::move(newChecksums));
}
-void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) {
- if (File->wasProcessedForPDB())
+void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
+ if (file->mergedIntoPDB)
return;
- // Add a module descriptor for every object file. We need to put an absolute
- // 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.
- bool InArchive = !File->ParentName.empty();
- SmallString<128> Path = InArchive ? File->ParentName : File->getName();
- pdbMakeAbsolute(Path);
- StringRef Name = InArchive ? File->getName() : StringRef(Path);
-
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- File->ModuleDBI = &ExitOnErr(DbiBuilder.addModuleInfo(Name));
- File->ModuleDBI->setObjFileName(Path);
-
- auto 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;
- }
+ file->mergedIntoPDB = true;
// 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);
+ CVIndexMap objectIndexMap;
+ auto indexMapResult =
+ mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
- if (!IndexMapResult) {
- if (!Config->WarnDebugInfoUnusable) {
- consumeError(IndexMapResult.takeError());
+ if (!indexMapResult) {
+ if (!config->warnDebugInfoUnusable) {
+ consumeError(indexMapResult.takeError());
return;
}
- StringRef FileName = sys::path::filename(Path);
- warn("Cannot use debug info for '" + FileName + "' [LNK4099]\n" +
+ warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" +
">>> failed to load reference " +
- StringRef(toString(IndexMapResult.takeError())));
+ StringRef(toString(indexMapResult.takeError())));
return;
}
- ScopedTimer T(SymbolMergingTimer);
+ ScopedTimer t(symbolMergingTimer);
- DebugSHandler DSH(*this, *File, *IndexMapResult);
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ DebugSHandler dsh(*this, *file, *indexMapResult);
// Now do all live .debug$S and .debug$F sections.
- for (SectionChunk *DebugChunk : File->getDebugChunks()) {
- if (!DebugChunk->Live || DebugChunk->getSize() == 0)
+ for (SectionChunk *debugChunk : file->getDebugChunks()) {
+ if (!debugChunk->live || debugChunk->getSize() == 0)
continue;
- if (DebugChunk->getSectionName() == ".debug$S") {
- DSH.handleDebugS(*DebugChunk);
+ if (debugChunk->getSectionName() == ".debug$S") {
+ dsh.handleDebugS(*debugChunk);
continue;
}
- if (DebugChunk->getSectionName() == ".debug$F") {
- ArrayRef<uint8_t> RelocatedDebugContents =
- relocateDebugChunk(Alloc, *DebugChunk);
+ if (debugChunk->getSectionName() == ".debug$F") {
+ ArrayRef<uint8_t> relocatedDebugContents =
+ relocateDebugChunk(alloc, *debugChunk);
- FixedStreamArray<object::FpoData> FpoRecords;
- BinaryStreamReader Reader(RelocatedDebugContents, support::little);
- uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData);
- ExitOnErr(Reader.readArray(FpoRecords, Count));
+ FixedStreamArray<object::FpoData> fpoRecords;
+ BinaryStreamReader reader(relocatedDebugContents, support::little);
+ uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData);
+ exitOnErr(reader.readArray(fpoRecords, count));
// These are already relocated and don't refer to the string table, so we
// can just copy it.
- for (const object::FpoData &FD : FpoRecords)
- DbiBuilder.addOldFpoData(FD);
+ for (const object::FpoData &fd : fpoRecords)
+ dbiBuilder.addOldFpoData(fd);
continue;
}
}
// Do any post-processing now that all .debug$S sections have been processed.
- DSH.finish();
+ dsh.finish();
}
-static PublicSym32 createPublic(Defined *Def) {
- PublicSym32 Pub(SymbolKind::S_PUB32);
- Pub.Name = Def->getName();
- if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
- if (D->getCOFFSymbol().isFunctionDefinition())
- Pub.Flags = PublicSymFlags::Function;
- } else if (isa<DefinedImportThunk>(Def)) {
- Pub.Flags = PublicSymFlags::Function;
+// Add a module descriptor for every object file. We need to put an absolute
+// 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) {
+ 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);
+
+ file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
+ file->moduleDBI->setObjFileName(objName);
+
+ 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;
+ }
}
+}
- OutputSection *OS = Def->getChunk()->getOutputSection();
- assert(OS && "all publics should be in final image");
- Pub.Offset = Def->getRVA() - OS->getRVA();
- Pub.Segment = OS->SectionIndex;
- return Pub;
+static PublicSym32 createPublic(Defined *def) {
+ PublicSym32 pub(SymbolKind::S_PUB32);
+ pub.Name = def->getName();
+ if (auto *d = dyn_cast<DefinedCOFF>(def)) {
+ if (d->getCOFFSymbol().isFunctionDefinition())
+ pub.Flags = PublicSymFlags::Function;
+ } else if (isa<DefinedImportThunk>(def)) {
+ pub.Flags = PublicSymFlags::Function;
+ }
+
+ OutputSection *os = def->getChunk()->getOutputSection();
+ assert(os && "all publics should be in final image");
+ pub.Offset = def->getRVA() - os->getRVA();
+ pub.Segment = os->sectionIndex;
+ return pub;
}
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
// TpiData.
void PDBLinker::addObjectsToPDB() {
- ScopedTimer T1(AddObjectsTimer);
- for (ObjFile *File : ObjFile::Instances)
- addObjFile(File);
+ ScopedTimer t1(addObjectsTimer);
+
+ createModuleDBI(builder);
- Builder.getStringTableBuilder().setStrings(PDBStrTab);
- T1.stop();
+ for (ObjFile *file : ObjFile::instances)
+ addObjFile(file);
+
+ builder.getStringTableBuilder().setStrings(pdbStrTab);
+ t1.stop();
// Construct TPI and IPI stream contents.
- ScopedTimer T2(TpiStreamLayoutTimer);
- addTypeInfo(Builder.getTpiBuilder(), getTypeTable());
- addTypeInfo(Builder.getIpiBuilder(), getIDTable());
- T2.stop();
+ ScopedTimer t2(tpiStreamLayoutTimer);
+ addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
+ addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
+ t2.stop();
- ScopedTimer T3(GlobalsLayoutTimer);
+ ScopedTimer t3(globalsLayoutTimer);
// Compute the public and global symbols.
- auto &GsiBuilder = Builder.getGsiBuilder();
- std::vector<PublicSym32> Publics;
- Symtab->forEachSymbol([&Publics](Symbol *S) {
+ auto &gsiBuilder = builder.getGsiBuilder();
+ std::vector<PublicSym32> publics;
+ symtab->forEachSymbol([&publics](Symbol *s) {
// Only emit defined, live symbols that have a chunk.
- auto *Def = dyn_cast<Defined>(S);
- if (Def && Def->isLive() && Def->getChunk())
- Publics.push_back(createPublic(Def));
+ auto *def = dyn_cast<Defined>(s);
+ if (def && def->isLive() && def->getChunk())
+ publics.push_back(createPublic(def));
});
- if (!Publics.empty()) {
+ if (!publics.empty()) {
+ publicSymbols = publics.size();
// Sort the public symbols and add them to the stream.
- sort(parallel::par, Publics.begin(), Publics.end(),
- [](const PublicSym32 &L, const PublicSym32 &R) {
- return L.Name < R.Name;
- });
- for (const PublicSym32 &Pub : Publics)
- GsiBuilder.addPublicSymbol(Pub);
+ parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) {
+ return l.Name < r.Name;
+ });
+ for (const PublicSym32 &pub : publics)
+ gsiBuilder.addPublicSymbol(pub);
}
}
+void PDBLinker::printStats() {
+ if (!config->showSummary)
+ return;
+
+ SmallString<256> buffer;
+ raw_svector_ostream stream(buffer);
+
+ stream << center_justify("Summary", 80) << '\n'
+ << std::string(80, '-') << '\n';
+
+ auto print = [&](uint64_t v, StringRef s) {
+ stream << format_decimal(v, 15) << " " << s << '\n';
+ };
+
+ 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(tMerger.getTypeTable().size() + tMerger.getIDTable().size(),
+ "Merged TPI records");
+ print(pdbStrTab.size(), "Output PDB strings");
+ print(globalSymbols, "Global symbol records");
+ print(moduleSymbols, "Module symbol records");
+ print(publicSymbols, "Public symbol records");
+
+ message(buffer);
+}
+
void PDBLinker::addNatvisFiles() {
- for (StringRef File : Config->NatvisFiles) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> DataOrErr =
- MemoryBuffer::getFile(File);
- if (!DataOrErr) {
- warn("Cannot open input file: " + File);
+ for (StringRef file : config->natvisFiles) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
+ MemoryBuffer::getFile(file);
+ if (!dataOrErr) {
+ warn("Cannot open input file: " + file);
continue;
}
- Builder.addInjectedSource(File, std::move(*DataOrErr));
+ builder.addInjectedSource(file, std::move(*dataOrErr));
}
}
-static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) {
- switch (Machine) {
+static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) {
+ switch (machine) {
case COFF::IMAGE_FILE_MACHINE_AMD64:
return codeview::CPUType::X64;
case COFF::IMAGE_FILE_MACHINE_ARM:
@@ -1433,201 +1376,329 @@ static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) {
// Mimic MSVC which surrounds arguments containing whitespace with quotes.
// Double double-quotes are handled, so that the resulting string can be
// executed again on the cmd-line.
-static std::string quote(ArrayRef<StringRef> Args) {
- std::string R;
- R.reserve(256);
- for (StringRef A : Args) {
- if (!R.empty())
- R.push_back(' ');
- bool HasWS = A.find(' ') != StringRef::npos;
- bool HasQ = A.find('"') != StringRef::npos;
- if (HasWS || HasQ)
- R.push_back('"');
- if (HasQ) {
- SmallVector<StringRef, 4> S;
- A.split(S, '"');
- R.append(join(S, "\"\""));
+static std::string quote(ArrayRef<StringRef> args) {
+ std::string r;
+ r.reserve(256);
+ for (StringRef a : args) {
+ if (!r.empty())
+ r.push_back(' ');
+ bool hasWS = a.find(' ') != StringRef::npos;
+ bool hasQ = a.find('"') != StringRef::npos;
+ if (hasWS || hasQ)
+ r.push_back('"');
+ if (hasQ) {
+ SmallVector<StringRef, 4> s;
+ a.split(s, '"');
+ r.append(join(s, "\"\""));
} else {
- R.append(A);
+ r.append(a);
}
- if (HasWS || HasQ)
- R.push_back('"');
+ if (hasWS || hasQ)
+ r.push_back('"');
}
- return R;
+ return r;
}
-static void addCommonLinkerModuleSymbols(StringRef Path,
- pdb::DbiModuleDescriptorBuilder &Mod,
- BumpPtrAllocator &Allocator) {
- ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
- Compile3Sym CS(SymbolRecordKind::Compile3Sym);
- EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
-
- ONS.Name = "* Linker *";
- ONS.Signature = 0;
-
- CS.Machine = toCodeViewMachine(Config->Machine);
+static void fillLinkerVerRecord(Compile3Sym &cs) {
+ cs.Machine = toCodeViewMachine(config->machine);
// Interestingly, if we set the string to 0.0.0.0, then when trying to view
// local variables WinDbg emits an error that private symbols are not present.
// By setting this to a valid MSVC linker version string, local variables are
// displayed properly. As such, even though it is not representative of
// LLVM's version information, we need this for compatibility.
- CS.Flags = CompileSym3Flags::None;
- CS.VersionBackendBuild = 25019;
- CS.VersionBackendMajor = 14;
- CS.VersionBackendMinor = 10;
- CS.VersionBackendQFE = 0;
+ cs.Flags = CompileSym3Flags::None;
+ cs.VersionBackendBuild = 25019;
+ cs.VersionBackendMajor = 14;
+ cs.VersionBackendMinor = 10;
+ cs.VersionBackendQFE = 0;
// MSVC also sets the frontend to 0.0.0.0 since this is specifically for the
// linker module (which is by definition a backend), so we don't need to do
// anything here. Also, it seems we can use "LLVM Linker" for the linker name
// without any problems. Only the backend version has to be hardcoded to a
// magic number.
- CS.VersionFrontendBuild = 0;
- CS.VersionFrontendMajor = 0;
- CS.VersionFrontendMinor = 0;
- CS.VersionFrontendQFE = 0;
- CS.Version = "LLVM Linker";
- CS.setLanguage(SourceLanguage::Link);
-
- ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front();
- std::string ArgStr = quote(Args);
- EBS.Fields.push_back("cwd");
+ cs.VersionFrontendBuild = 0;
+ cs.VersionFrontendMajor = 0;
+ cs.VersionFrontendMinor = 0;
+ cs.VersionFrontendQFE = 0;
+ cs.Version = "LLVM Linker";
+ cs.setLanguage(SourceLanguage::Link);
+}
+
+static void addCommonLinkerModuleSymbols(StringRef path,
+ pdb::DbiModuleDescriptorBuilder &mod,
+ BumpPtrAllocator &allocator) {
+ ObjNameSym ons(SymbolRecordKind::ObjNameSym);
+ EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
+ Compile3Sym cs(SymbolRecordKind::Compile3Sym);
+ fillLinkerVerRecord(cs);
+
+ ons.Name = "* Linker *";
+ ons.Signature = 0;
+
+ ArrayRef<StringRef> args = makeArrayRef(config->argv).drop_front();
+ std::string argStr = quote(args);
+ ebs.Fields.push_back("cwd");
SmallString<64> cwd;
- if (Config->PDBSourcePath.empty())
+ if (config->pdbSourcePath.empty())
sys::fs::current_path(cwd);
else
- cwd = Config->PDBSourcePath;
- EBS.Fields.push_back(cwd);
- EBS.Fields.push_back("exe");
- SmallString<64> exe = Config->Argv[0];
+ cwd = config->pdbSourcePath;
+ ebs.Fields.push_back(cwd);
+ ebs.Fields.push_back("exe");
+ SmallString<64> exe = config->argv[0];
pdbMakeAbsolute(exe);
- EBS.Fields.push_back(exe);
- EBS.Fields.push_back("pdb");
- EBS.Fields.push_back(Path);
- EBS.Fields.push_back("cmd");
- EBS.Fields.push_back(ArgStr);
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ONS, Allocator, CodeViewContainer::Pdb));
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- CS, Allocator, CodeViewContainer::Pdb));
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- EBS, Allocator, CodeViewContainer::Pdb));
+ ebs.Fields.push_back(exe);
+ ebs.Fields.push_back("pdb");
+ ebs.Fields.push_back(path);
+ ebs.Fields.push_back("cmd");
+ ebs.Fields.push_back(argStr);
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ons, allocator, CodeViewContainer::Pdb));
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ cs, allocator, CodeViewContainer::Pdb));
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ebs, allocator, CodeViewContainer::Pdb));
+}
+
+static void addLinkerModuleCoffGroup(PartialSection *sec,
+ pdb::DbiModuleDescriptorBuilder &mod,
+ OutputSection &os,
+ BumpPtrAllocator &allocator) {
+ // If there's a section, there's at least one chunk
+ assert(!sec->chunks.empty());
+ const Chunk *firstChunk = *sec->chunks.begin();
+ const Chunk *lastChunk = *sec->chunks.rbegin();
+
+ // Emit COFF group
+ CoffGroupSym cgs(SymbolRecordKind::CoffGroupSym);
+ cgs.Name = sec->name;
+ cgs.Segment = os.sectionIndex;
+ cgs.Offset = firstChunk->getRVA() - os.getRVA();
+ cgs.Size = lastChunk->getRVA() + lastChunk->getSize() - firstChunk->getRVA();
+ cgs.Characteristics = sec->characteristics;
+
+ // Somehow .idata sections & sections groups in the debug symbol stream have
+ // the "write" flag set. However the section header for the corresponding
+ // .idata section doesn't have it.
+ if (cgs.Name.startswith(".idata"))
+ cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE;
+
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ cgs, allocator, CodeViewContainer::Pdb));
+}
+
+static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
+ OutputSection &os,
+ BumpPtrAllocator &allocator) {
+ SectionSym sym(SymbolRecordKind::SectionSym);
+ sym.Alignment = 12; // 2^12 = 4KB
+ sym.Characteristics = os.header.Characteristics;
+ sym.Length = os.getVirtualSize();
+ sym.Name = os.name;
+ sym.Rva = os.getRVA();
+ sym.SectionNumber = os.sectionIndex;
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ sym, allocator, 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
+ if (config->mingw)
+ return;
+
+ // Output COFF groups for individual chunks of this section.
+ for (PartialSection *sec : os.contribSections) {
+ addLinkerModuleCoffGroup(sec, mod, os, allocator);
+ }
}
-static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
- OutputSection &OS,
- BumpPtrAllocator &Allocator) {
- SectionSym Sym(SymbolRecordKind::SectionSym);
- Sym.Alignment = 12; // 2^12 = 4KB
- Sym.Characteristics = OS.Header.Characteristics;
- Sym.Length = OS.getVirtualSize();
- Sym.Name = OS.Name;
- Sym.Rva = OS.getRVA();
- Sym.SectionNumber = OS.SectionIndex;
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- Sym, Allocator, CodeViewContainer::Pdb));
+// Add all import files as modules to the PDB.
+void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
+ if (ImportFile::instances.empty())
+ return;
+
+ std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
+
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
+ continue;
+
+ if (!file->thunkSym)
+ continue;
+
+ if (!file->thunkLive)
+ continue;
+
+ std::string dll = StringRef(file->dllName).lower();
+ llvm::pdb::DbiModuleDescriptorBuilder *&mod = dllToModuleDbi[dll];
+ if (!mod) {
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ SmallString<128> libPath = file->parentName;
+ pdbMakeAbsolute(libPath);
+ sys::path::native(libPath);
+
+ // Name modules similar to MSVC's link.exe.
+ // The first module is the simple dll filename
+ llvm::pdb::DbiModuleDescriptorBuilder &firstMod =
+ exitOnErr(dbiBuilder.addModuleInfo(file->dllName));
+ firstMod.setObjFileName(libPath);
+ pdb::SectionContrib sc =
+ createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
+ firstMod.setFirstSectionContrib(sc);
+
+ // The second module is where the import stream goes.
+ mod = &exitOnErr(dbiBuilder.addModuleInfo("Import:" + file->dllName));
+ mod->setObjFileName(libPath);
+ }
+
+ DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
+ Chunk *thunkChunk = thunk->getChunk();
+ OutputSection *thunkOS = thunkChunk->getOutputSection();
+
+ ObjNameSym ons(SymbolRecordKind::ObjNameSym);
+ Compile3Sym cs(SymbolRecordKind::Compile3Sym);
+ Thunk32Sym ts(SymbolRecordKind::Thunk32Sym);
+ ScopeEndSym es(SymbolRecordKind::ScopeEndSym);
+
+ ons.Name = file->dllName;
+ ons.Signature = 0;
+
+ fillLinkerVerRecord(cs);
+
+ ts.Name = thunk->getName();
+ ts.Parent = 0;
+ ts.End = 0;
+ ts.Next = 0;
+ ts.Thunk = ThunkOrdinal::Standard;
+ ts.Length = thunkChunk->getSize();
+ ts.Segment = thunkOS->sectionIndex;
+ ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA();
+
+ mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ons, alloc, CodeViewContainer::Pdb));
+ mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ cs, alloc, CodeViewContainer::Pdb));
+
+ SmallVector<SymbolScope, 4> scopes;
+ CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol(
+ ts, alloc, CodeViewContainer::Pdb);
+ scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym);
+
+ mod->addSymbol(newSym);
+
+ newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc,
+ CodeViewContainer::Pdb);
+ scopeStackClose(scopes, mod->getNextSymbolOffset(), file);
+
+ mod->addSymbol(newSym);
+
+ pdb::SectionContrib sc =
+ createSectionContrib(thunk->getChunk(), mod->getModuleIndex());
+ mod->setFirstSectionContrib(sc);
+ }
}
// Creates a PDB file.
-void coff::createPDB(SymbolTable *Symtab,
- ArrayRef<OutputSection *> OutputSections,
- ArrayRef<uint8_t> SectionTable,
- llvm::codeview::DebugInfo *BuildId) {
- ScopedTimer T1(TotalPdbLinkTimer);
- PDBLinker PDB(Symtab);
-
- PDB.initialize(BuildId);
- PDB.addObjectsToPDB();
- PDB.addSections(OutputSections, SectionTable);
- PDB.addNatvisFiles();
-
- ScopedTimer T2(DiskCommitTimer);
- codeview::GUID Guid;
- PDB.commit(&Guid);
- memcpy(&BuildId->PDB70.Signature, &Guid, 16);
+void coff::createPDB(SymbolTable *symtab,
+ ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId) {
+ ScopedTimer t1(totalPdbLinkTimer);
+ PDBLinker pdb(symtab);
+
+ pdb.initialize(buildId);
+ pdb.addObjectsToPDB();
+ pdb.addImportFilesToPDB(outputSections);
+ pdb.addSections(outputSections, sectionTable);
+ pdb.addNatvisFiles();
+
+ ScopedTimer t2(diskCommitTimer);
+ codeview::GUID guid;
+ pdb.commit(&guid);
+ memcpy(&buildId->PDB70.Signature, &guid, 16);
+
+ t2.stop();
+ t1.stop();
+ pdb.printStats();
}
-void PDBLinker::initialize(llvm::codeview::DebugInfo *BuildId) {
- ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
+void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
+ exitOnErr(builder.initialize(4096)); // 4096 is blocksize
- BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+ buildId->Signature.CVSignature = OMF::Signature::PDB70;
// Signature is set to a hash of the PDB contents when the PDB is done.
- memset(BuildId->PDB70.Signature, 0, 16);
- BuildId->PDB70.Age = 1;
+ memset(buildId->PDB70.Signature, 0, 16);
+ buildId->PDB70.Age = 1;
// Create streams in MSF for predefined streams, namely
// PDB, TPI, DBI and IPI.
- for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I)
- ExitOnErr(Builder.getMsfBuilder().addStream(0));
+ for (int i = 0; i < (int)pdb::kSpecialStreamCount; ++i)
+ exitOnErr(builder.getMsfBuilder().addStream(0));
// Add an Info stream.
- auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
- InfoBuilder.setHashPDBContentsToGUID(true);
+ auto &infoBuilder = builder.getInfoBuilder();
+ infoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
+ infoBuilder.setHashPDBContentsToGUID(true);
// Add an empty DBI stream.
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- DbiBuilder.setAge(BuildId->PDB70.Age);
- DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
- DbiBuilder.setMachineType(Config->Machine);
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ dbiBuilder.setAge(buildId->PDB70.Age);
+ dbiBuilder.setVersionHeader(pdb::PdbDbiV70);
+ dbiBuilder.setMachineType(config->machine);
// Technically we are not link.exe 14.11, but there are known cases where
// debugging tools on Windows expect Microsoft-specific version numbers or
// they fail to work at all. Since we know we produce PDBs that are
// compatible with LINK 14.11, we set that version number here.
- DbiBuilder.setBuildNumber(14, 11);
+ dbiBuilder.setBuildNumber(14, 11);
}
-void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
- ArrayRef<uint8_t> SectionTable) {
+void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable) {
// It's not entirely clear what this is, but the * Linker * module uses it.
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- NativePath = Config->PDBPath;
- pdbMakeAbsolute(NativePath);
- uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
- auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
- LinkerModule.setPdbFilePathNI(PdbFilePathNI);
- addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ nativePath = config->pdbPath;
+ pdbMakeAbsolute(nativePath);
+ uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
+ auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
+ linkerModule.setPdbFilePathNI(pdbFilePathNI);
+ addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc);
// Add section contributions. They must be ordered by ascending RVA.
- for (OutputSection *OS : OutputSections) {
- addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
- for (Chunk *C : OS->Chunks) {
- pdb::SectionContrib SC =
- createSectionContrib(C, LinkerModule.getModuleIndex());
- Builder.getDbiBuilder().addSectionContrib(SC);
+ for (OutputSection *os : outputSections) {
+ addLinkerModuleSectionSymbol(linkerModule, *os, alloc);
+ for (Chunk *c : os->chunks) {
+ pdb::SectionContrib sc =
+ createSectionContrib(c, linkerModule.getModuleIndex());
+ builder.getDbiBuilder().addSectionContrib(sc);
}
}
+ // The * Linker * first section contrib is only used along with /INCREMENTAL,
+ // to provide trampolines thunks for incremental function patching. Set this
+ // as "unused" because LLD doesn't support /INCREMENTAL link.
+ pdb::SectionContrib sc =
+ createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
+ linkerModule.setFirstSectionContrib(sc);
+
// Add Section Map stream.
- 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);
+ 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);
// Add COFF section header stream.
- ExitOnErr(
- DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
+ exitOnErr(
+ dbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, sectionTable));
}
-void PDBLinker::commit(codeview::GUID *Guid) {
+void PDBLinker::commit(codeview::GUID *guid) {
// Write to a file.
- ExitOnErr(Builder.commit(Config->PDBPath, Guid));
-}
-
-static Expected<StringRef>
-getFileName(const DebugStringTableSubsectionRef &Strings,
- const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) {
- auto Iter = Checksums.getArray().at(FileID);
- if (Iter == Checksums.getArray().end())
- return make_error<CodeViewError>(cv_error_code::no_records);
- uint32_t Offset = Iter->FileNameOffset;
- return Strings.getString(Offset);
+ exitOnErr(builder.commit(config->pdbPath, guid));
}
static uint32_t getSecrelReloc() {
- switch (Config->Machine) {
+ switch (config->machine) {
case AMD64:
return COFF::IMAGE_REL_AMD64_SECREL;
case I386:
@@ -1646,78 +1717,78 @@ static uint32_t getSecrelReloc() {
// that are used to interpret the line table, and the offset of Addr in the line
// 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,
- DebugChecksumsSubsectionRef &Checksums,
- DebugLinesSubsectionRef &Lines,
- uint32_t &OffsetInLinetable) {
- ExitOnError ExitOnErr;
- uint32_t SecrelReloc = getSecrelReloc();
-
- for (SectionChunk *DbgC : C->File->getDebugChunks()) {
- if (DbgC->getSectionName() != ".debug$S")
+static bool findLineTable(const SectionChunk *c, uint32_t addr,
+ DebugStringTableSubsectionRef &cVStrTab,
+ DebugChecksumsSubsectionRef &checksums,
+ DebugLinesSubsectionRef &lines,
+ uint32_t &offsetInLinetable) {
+ ExitOnError exitOnErr;
+ uint32_t secrelReloc = getSecrelReloc();
+
+ for (SectionChunk *dbgC : c->file->getDebugChunks()) {
+ if (dbgC->getSectionName() != ".debug$S")
continue;
- // Build a mapping of SECREL relocations in DbgC that refer to C.
- DenseMap<uint32_t, uint32_t> Secrels;
- for (const coff_relocation &R : DbgC->Relocs) {
- if (R.Type != SecrelReloc)
+ // Build a mapping of SECREL relocations in dbgC that refer to `c`.
+ DenseMap<uint32_t, uint32_t> secrels;
+ for (const coff_relocation &r : dbgC->getRelocs()) {
+ if (r.Type != secrelReloc)
continue;
- if (auto *S = dyn_cast_or_null<DefinedRegular>(
- C->File->getSymbols()[R.SymbolTableIndex]))
- if (S->getChunk() == C)
- Secrels[R.VirtualAddress] = S->getValue();
+ if (auto *s = dyn_cast_or_null<DefinedRegular>(
+ c->file->getSymbols()[r.SymbolTableIndex]))
+ if (s->getChunk() == c)
+ secrels[r.VirtualAddress] = s->getValue();
}
- ArrayRef<uint8_t> Contents =
- consumeDebugMagic(DbgC->getContents(), ".debug$S");
- DebugSubsectionArray Subsections;
- BinaryStreamReader Reader(Contents, support::little);
- ExitOnErr(Reader.readArray(Subsections, Contents.size()));
+ ArrayRef<uint8_t> contents =
+ SectionChunk::consumeDebugMagic(dbgC->getContents(), ".debug$S");
+ DebugSubsectionArray subsections;
+ BinaryStreamReader reader(contents, support::little);
+ exitOnErr(reader.readArray(subsections, contents.size()));
- for (const DebugSubsectionRecord &SS : Subsections) {
- switch (SS.kind()) {
+ 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:
- assert(!Checksums.valid() &&
+ assert(!checksums.valid() &&
"Encountered multiple checksum subsections!");
- ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ exitOnErr(checksums.initialize(ss.getRecordData()));
break;
case DebugSubsectionKind::Lines: {
- ArrayRef<uint8_t> Bytes;
- auto Ref = SS.getRecordData();
- ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes));
- size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data();
+ ArrayRef<uint8_t> bytes;
+ auto ref = ss.getRecordData();
+ exitOnErr(ref.readLongestContiguousChunk(0, bytes));
+ size_t offsetInDbgC = bytes.data() - dbgC->getContents().data();
// Check whether this line table refers to C.
- auto I = Secrels.find(OffsetInDbgC);
- if (I == Secrels.end())
+ auto i = secrels.find(offsetInDbgC);
+ if (i == secrels.end())
break;
// Check whether this line table covers Addr in C.
- DebugLinesSubsectionRef LinesTmp;
- ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref)));
- uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset;
- if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize)
+ DebugLinesSubsectionRef linesTmp;
+ exitOnErr(linesTmp.initialize(BinaryStreamReader(ref)));
+ uint32_t offsetInC = i->second + linesTmp.header()->RelocOffset;
+ if (addr < offsetInC || addr >= offsetInC + linesTmp.header()->CodeSize)
break;
- assert(!Lines.header() &&
+ assert(!lines.header() &&
"Encountered multiple line tables for function!");
- ExitOnErr(Lines.initialize(BinaryStreamReader(Ref)));
- OffsetInLinetable = Addr - OffsetInC;
+ exitOnErr(lines.initialize(BinaryStreamReader(ref)));
+ offsetInLinetable = addr - offsetInC;
break;
}
default:
break;
}
- if (CVStrTab.valid() && Checksums.valid() && Lines.header())
+ if (cVStrTab.valid() && checksums.valid() && lines.header())
return true;
}
}
@@ -1728,38 +1799,38 @@ static bool findLineTable(const SectionChunk *C, uint32_t Addr,
// Use CodeView line tables to resolve a file and line number for the given
// offset into the given chunk and return them, or {"", 0} if a line table was
// not found.
-std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C,
- uint32_t Addr) {
- ExitOnError ExitOnErr;
+std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
+ uint32_t addr) {
+ ExitOnError exitOnErr;
- DebugStringTableSubsectionRef CVStrTab;
- DebugChecksumsSubsectionRef Checksums;
- DebugLinesSubsectionRef Lines;
- uint32_t OffsetInLinetable;
+ 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 {"", 0};
- Optional<uint32_t> NameIndex;
- Optional<uint32_t> LineNumber;
- for (LineColumnEntry &Entry : Lines) {
- for (const LineNumberEntry &LN : Entry.LineNumbers) {
- LineInfo LI(LN.Flags);
- if (LN.Offset > OffsetInLinetable) {
- if (!NameIndex) {
- NameIndex = Entry.NameIndex;
- LineNumber = LI.getStartLine();
+ Optional<uint32_t> nameIndex;
+ Optional<uint32_t> lineNumber;
+ for (LineColumnEntry &entry : lines) {
+ for (const LineNumberEntry &ln : entry.LineNumbers) {
+ LineInfo li(ln.Flags);
+ if (ln.Offset > offsetInLinetable) {
+ if (!nameIndex) {
+ nameIndex = entry.NameIndex;
+ lineNumber = li.getStartLine();
}
- StringRef Filename =
- ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex));
- return {Filename, *LineNumber};
+ StringRef filename =
+ exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ return {filename, *lineNumber};
}
- NameIndex = Entry.NameIndex;
- LineNumber = LI.getStartLine();
+ nameIndex = entry.NameIndex;
+ lineNumber = li.getStartLine();
}
}
- if (!NameIndex)
+ if (!nameIndex)
return {"", 0};
- StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex));
- return {Filename, *LineNumber};
+ StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ return {filename, *lineNumber};
}
diff --git a/COFF/PDB.h b/COFF/PDB.h
index ea7a9996f415..3ac1adc85c5d 100644
--- a/COFF/PDB.h
+++ b/COFF/PDB.h
@@ -1,9 +1,8 @@
//===- PDB.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -25,13 +24,13 @@ class OutputSection;
class SectionChunk;
class SymbolTable;
-void createPDB(SymbolTable *Symtab,
- llvm::ArrayRef<OutputSection *> OutputSections,
- llvm::ArrayRef<uint8_t> SectionTable,
- llvm::codeview::DebugInfo *BuildId);
+void createPDB(SymbolTable *symtab,
+ llvm::ArrayRef<OutputSection *> outputSections,
+ llvm::ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId);
-std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C,
- uint32_t Addr);
+std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c,
+ uint32_t addr);
}
}
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 1a9e0455dc1d..0aff164ee567 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -1,9 +1,8 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -17,6 +16,7 @@
#include "lld/Common/Memory.h"
#include "lld/Common/Timer.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
@@ -26,521 +26,576 @@ using namespace llvm;
namespace lld {
namespace coff {
-static Timer LTOTimer("LTO", Timer::root());
+static Timer ltoTimer("LTO", Timer::root());
-SymbolTable *Symtab;
+SymbolTable *symtab;
-void SymbolTable::addFile(InputFile *File) {
- log("Reading " + toString(File));
- File->parse();
+void SymbolTable::addFile(InputFile *file) {
+ log("Reading " + toString(file));
+ file->parse();
- MachineTypes MT = File->getMachineType();
- if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- Config->Machine = MT;
- } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) {
- error(toString(File) + ": machine type " + machineToStr(MT) +
- " conflicts with " + machineToStr(Config->Machine));
+ MachineTypes mt = file->getMachineType();
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ config->machine = mt;
+ } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
+ error(toString(file) + ": machine type " + machineToStr(mt) +
+ " conflicts with " + machineToStr(config->machine));
return;
}
- if (auto *F = dyn_cast<ObjFile>(File)) {
- ObjFile::Instances.push_back(F);
- } else if (auto *F = dyn_cast<BitcodeFile>(File)) {
- BitcodeFile::Instances.push_back(F);
- } else if (auto *F = dyn_cast<ImportFile>(File)) {
- ImportFile::Instances.push_back(F);
+ if (auto *f = dyn_cast<ObjFile>(file)) {
+ ObjFile::instances.push_back(f);
+ } else if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ BitcodeFile::instances.push_back(f);
+ } else if (auto *f = dyn_cast<ImportFile>(file)) {
+ ImportFile::instances.push_back(f);
}
- StringRef S = File->getDirectives();
- if (S.empty())
- return;
-
- log("Directives: " + toString(File) + ": " + S);
- Driver->parseDirectives(S);
+ driver->parseDirectives(file);
}
-static void errorOrWarn(const Twine &S) {
- if (Config->ForceUnresolved)
- warn(S);
+static void errorOrWarn(const Twine &s) {
+ if (config->forceUnresolved)
+ warn(s);
else
- error(S);
+ error(s);
}
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
// This is generally the global variable or function whose definition contains
// Addr.
-static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) {
- DefinedRegular *Candidate = nullptr;
+static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
+ DefinedRegular *candidate = nullptr;
- for (Symbol *S : SC->File->getSymbols()) {
- auto *D = dyn_cast_or_null<DefinedRegular>(S);
- if (!D || D->getChunk() != SC || D->getValue() > Addr ||
- (Candidate && D->getValue() < Candidate->getValue()))
+ for (Symbol *s : sc->file->getSymbols()) {
+ auto *d = dyn_cast_or_null<DefinedRegular>(s);
+ if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr ||
+ (candidate && d->getValue() < candidate->getValue()))
continue;
- Candidate = D;
+ candidate = d;
}
- return Candidate;
+ return candidate;
}
-std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
+// Given a file and the index of a symbol in that file, returns a description
+// 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) {
struct Location {
- Symbol *Sym;
- std::pair<StringRef, uint32_t> FileLine;
+ Symbol *sym;
+ std::pair<StringRef, uint32_t> fileLine;
};
- std::vector<Location> Locations;
+ std::vector<Location> locations;
- for (Chunk *C : File->getChunks()) {
- auto *SC = dyn_cast<SectionChunk>(C);
- if (!SC)
+ for (Chunk *c : file->getChunks()) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
continue;
- for (const coff_relocation &R : SC->Relocs) {
- if (R.SymbolTableIndex != SymIndex)
+ for (const coff_relocation &r : sc->getRelocs()) {
+ if (r.SymbolTableIndex != symIndex)
continue;
- std::pair<StringRef, uint32_t> FileLine =
- getFileLine(SC, R.VirtualAddress);
- Symbol *Sym = getSymbol(SC, R.VirtualAddress);
- if (!FileLine.first.empty() || Sym)
- Locations.push_back({Sym, FileLine});
+ std::pair<StringRef, uint32_t> fileLine =
+ getFileLine(sc, r.VirtualAddress);
+ Symbol *sym = getSymbol(sc, r.VirtualAddress);
+ if (!fileLine.first.empty() || sym)
+ locations.push_back({sym, fileLine});
}
}
- if (Locations.empty())
- return "\n>>> referenced by " + toString(File);
+ if (locations.empty())
+ return std::vector<std::string>({"\n>>> referenced by " + toString(file)});
- std::string Out;
- llvm::raw_string_ostream OS(Out);
- for (Location Loc : Locations) {
- OS << "\n>>> referenced by ";
- if (!Loc.FileLine.first.empty())
- OS << Loc.FileLine.first << ":" << Loc.FileLine.second
+ std::vector<std::string> symbolLocations(locations.size());
+ size_t i = 0;
+ for (Location loc : locations) {
+ llvm::raw_string_ostream os(symbolLocations[i++]);
+ os << "\n>>> referenced by ";
+ if (!loc.fileLine.first.empty())
+ os << loc.fileLine.first << ":" << loc.fileLine.second
<< "\n>>> ";
- OS << toString(File);
- if (Loc.Sym)
- OS << ":(" << toString(*Loc.Sym) << ')';
+ os << toString(file);
+ if (loc.sym)
+ os << ":(" << toString(*loc.sym) << ')';
+ }
+ return symbolLocations;
+}
+
+// For an undefined symbol, stores all files referencing it and the index of
+// the undefined symbol in each file.
+struct UndefinedDiag {
+ Symbol *sym;
+ struct File {
+ ObjFile *oFile;
+ uint64_t symIndex;
+ };
+ std::vector<File> files;
+};
+
+static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
+ std::string out;
+ llvm::raw_string_ostream os(out);
+ os << "undefined symbol: " << toString(*undefDiag.sym);
+
+ const size_t maxUndefReferences = 10;
+ size_t i = 0, numRefs = 0;
+ for (const UndefinedDiag::File &ref : undefDiag.files) {
+ std::vector<std::string> symbolLocations =
+ getSymbolLocations(ref.oFile, ref.symIndex);
+ numRefs += symbolLocations.size();
+ for (const std::string &s : symbolLocations) {
+ if (i >= maxUndefReferences)
+ break;
+ os << s;
+ i++;
+ }
}
- return OS.str();
+ if (i < numRefs)
+ os << "\n>>> referenced " << numRefs - i << " more times";
+ errorOrWarn(os.str());
}
void SymbolTable::loadMinGWAutomaticImports() {
- for (auto &I : SymMap) {
- Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym);
- if (!Undef)
+ for (auto &i : symMap) {
+ Symbol *sym = i.second;
+ auto *undef = dyn_cast<Undefined>(sym);
+ if (!undef)
continue;
- if (!Sym->IsUsedInRegularObj)
+ if (!sym->isUsedInRegularObj)
continue;
- StringRef Name = Undef->getName();
+ StringRef name = undef->getName();
- if (Name.startswith("__imp_"))
+ if (name.startswith("__imp_"))
continue;
// If we have an undefined symbol, but we have a Lazy representing a
// symbol we could load from file, make sure to load that.
- Lazy *L = dyn_cast_or_null<Lazy>(find(("__imp_" + Name).str()));
- if (!L || L->PendingArchiveLoad)
+ Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
+ if (!l || l->pendingArchiveLoad)
continue;
- log("Loading lazy " + L->getName() + " from " + L->File->getName() +
+ log("Loading lazy " + l->getName() + " from " + l->file->getName() +
" for automatic import");
- L->PendingArchiveLoad = true;
- L->File->addMember(&L->Sym);
+ l->pendingArchiveLoad = true;
+ l->file->addMember(&l->sym);
}
}
-bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) {
- if (Name.startswith("__imp_"))
+bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
+ if (name.startswith("__imp_"))
return false;
- Defined *Imp = dyn_cast_or_null<Defined>(find(("__imp_" + Name).str()));
- if (!Imp)
+ Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
+ if (!imp)
return false;
// Replace the reference directly to a variable with a reference
// to the import address table instead. This obviously isn't right,
- // but we mark the symbol as IsRuntimePseudoReloc, and a later pass
+ // but we mark the symbol as isRuntimePseudoReloc, and a later pass
// will add runtime pseudo relocations for every relocation against
// this Symbol. The runtime pseudo relocation framework expects the
// reference itself to point at the IAT entry.
- size_t ImpSize = 0;
- if (isa<DefinedImportData>(Imp)) {
- log("Automatically importing " + Name + " from " +
- cast<DefinedImportData>(Imp)->getDLLName());
- ImpSize = sizeof(DefinedImportData);
- } else if (isa<DefinedRegular>(Imp)) {
- log("Automatically importing " + Name + " from " +
- toString(cast<DefinedRegular>(Imp)->File));
- ImpSize = sizeof(DefinedRegular);
+ size_t impSize = 0;
+ if (isa<DefinedImportData>(imp)) {
+ log("Automatically importing " + name + " from " +
+ cast<DefinedImportData>(imp)->getDLLName());
+ impSize = sizeof(DefinedImportData);
+ } else if (isa<DefinedRegular>(imp)) {
+ log("Automatically importing " + name + " from " +
+ toString(cast<DefinedRegular>(imp)->file));
+ impSize = sizeof(DefinedRegular);
} else {
- warn("unable to automatically import " + Name + " from " + Imp->getName() +
- " from " + toString(cast<DefinedRegular>(Imp)->File) +
+ warn("unable to automatically import " + name + " from " + imp->getName() +
+ " from " + toString(cast<DefinedRegular>(imp)->file) +
"; unexpected symbol type");
return false;
}
- Sym->replaceKeepingName(Imp, ImpSize);
- Sym->IsRuntimePseudoReloc = true;
+ sym->replaceKeepingName(imp, impSize);
+ sym->isRuntimePseudoReloc = true;
// There may exist symbols named .refptr.<name> which only consist
// of a single pointer to <name>. If it turns out <name> is
// automatically imported, we don't need to keep the .refptr.<name>
// pointer at all, but redirect all accesses to it to the IAT entry
// for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
- DefinedRegular *Refptr =
- dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str()));
- if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) {
- SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk());
- if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) {
- log("Replacing .refptr." + Name + " with " + Imp->getName());
- Refptr->getChunk()->Live = false;
- Refptr->replaceKeepingName(Imp, ImpSize);
+ DefinedRegular *refptr =
+ dyn_cast_or_null<DefinedRegular>(find((".refptr." + name).str()));
+ if (refptr && refptr->getChunk()->getSize() == config->wordsize) {
+ SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk());
+ if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) {
+ log("Replacing .refptr." + name + " with " + imp->getName());
+ refptr->getChunk()->live = false;
+ refptr->replaceKeepingName(imp, impSize);
}
}
return true;
}
void SymbolTable::reportRemainingUndefines() {
- SmallPtrSet<Symbol *, 8> Undefs;
- DenseMap<Symbol *, Symbol *> LocalImports;
+ SmallPtrSet<Symbol *, 8> undefs;
+ DenseMap<Symbol *, Symbol *> localImports;
- for (auto &I : SymMap) {
- Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym);
- if (!Undef)
+ for (auto &i : symMap) {
+ Symbol *sym = i.second;
+ auto *undef = dyn_cast<Undefined>(sym);
+ if (!undef)
continue;
- if (!Sym->IsUsedInRegularObj)
+ if (!sym->isUsedInRegularObj)
continue;
- StringRef Name = Undef->getName();
+ StringRef name = undef->getName();
// A weak alias may have been resolved, so check for that.
- if (Defined *D = Undef->getWeakAlias()) {
+ if (Defined *d = undef->getWeakAlias()) {
// We want to replace Sym with D. However, we can't just blindly
// copy sizeof(SymbolUnion) bytes from D to Sym because D may be an
// internal symbol, and internal symbols are stored as "unparented"
// Symbols. For that reason we need to check which type of symbol we
// are dealing with and copy the correct number of bytes.
- if (isa<DefinedRegular>(D))
- memcpy(Sym, D, sizeof(DefinedRegular));
- else if (isa<DefinedAbsolute>(D))
- memcpy(Sym, D, sizeof(DefinedAbsolute));
+ if (isa<DefinedRegular>(d))
+ memcpy(sym, d, sizeof(DefinedRegular));
+ else if (isa<DefinedAbsolute>(d))
+ memcpy(sym, d, sizeof(DefinedAbsolute));
else
- memcpy(Sym, D, sizeof(SymbolUnion));
+ memcpy(sym, d, sizeof(SymbolUnion));
continue;
}
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
- if (Name.startswith("__imp_")) {
- Symbol *Imp = find(Name.substr(strlen("__imp_")));
- if (Imp && isa<Defined>(Imp)) {
- auto *D = cast<Defined>(Imp);
- replaceSymbol<DefinedLocalImport>(Sym, Name, D);
- LocalImportChunks.push_back(cast<DefinedLocalImport>(Sym)->getChunk());
- LocalImports[Sym] = D;
+ if (name.startswith("__imp_")) {
+ Symbol *imp = find(name.substr(strlen("__imp_")));
+ if (imp && isa<Defined>(imp)) {
+ auto *d = cast<Defined>(imp);
+ replaceSymbol<DefinedLocalImport>(sym, name, d);
+ localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk());
+ localImports[sym] = d;
continue;
}
}
// We don't want to report missing Microsoft precompiled headers symbols.
// A proper message will be emitted instead in PDBLinker::aquirePrecompObj
- if (Name.contains("_PchSym_"))
+ if (name.contains("_PchSym_"))
continue;
- if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name))
+ if (config->mingw && handleMinGWAutomaticImport(sym, name))
continue;
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
- if (Config->ForceUnresolved)
- replaceSymbol<DefinedAbsolute>(Sym, Name, 0);
- Undefs.insert(Sym);
+ if (config->forceUnresolved)
+ replaceSymbol<DefinedAbsolute>(sym, name, 0);
+ undefs.insert(sym);
}
- if (Undefs.empty() && LocalImports.empty())
+ if (undefs.empty() && localImports.empty())
return;
- for (Symbol *B : Config->GCRoot) {
- if (Undefs.count(B))
- errorOrWarn("<root>: undefined symbol: " + toString(*B));
- if (Config->WarnLocallyDefinedImported)
- if (Symbol *Imp = LocalImports.lookup(B))
- warn("<root>: locally defined symbol imported: " + toString(*Imp) +
- " (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
+ for (Symbol *b : config->gcroot) {
+ if (undefs.count(b))
+ errorOrWarn("<root>: undefined symbol: " + toString(*b));
+ if (config->warnLocallyDefinedImported)
+ if (Symbol *imp = localImports.lookup(b))
+ warn("<root>: locally defined symbol imported: " + toString(*imp) +
+ " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
}
- for (ObjFile *File : ObjFile::Instances) {
- size_t SymIndex = (size_t)-1;
- for (Symbol *Sym : File->getSymbols()) {
- ++SymIndex;
- if (!Sym)
+ std::vector<UndefinedDiag> undefDiags;
+ DenseMap<Symbol *, int> firstDiag;
+
+ for (ObjFile *file : ObjFile::instances) {
+ size_t symIndex = (size_t)-1;
+ for (Symbol *sym : file->getSymbols()) {
+ ++symIndex;
+ if (!sym)
continue;
- if (Undefs.count(Sym))
- errorOrWarn("undefined symbol: " + toString(*Sym) +
- getSymbolLocations(File, SymIndex));
- if (Config->WarnLocallyDefinedImported)
- if (Symbol *Imp = LocalImports.lookup(Sym))
- warn(toString(File) +
- ": locally defined symbol imported: " + toString(*Imp) +
- " (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
+ if (undefs.count(sym)) {
+ auto it = firstDiag.find(sym);
+ if (it == firstDiag.end()) {
+ firstDiag[sym] = undefDiags.size();
+ undefDiags.push_back({sym, {{file, symIndex}}});
+ } else {
+ undefDiags[it->second].files.push_back({file, symIndex});
+ }
+ }
+ if (config->warnLocallyDefinedImported)
+ if (Symbol *imp = localImports.lookup(sym))
+ warn(toString(file) +
+ ": locally defined symbol imported: " + toString(*imp) +
+ " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
}
}
+
+ for (const UndefinedDiag& undefDiag : undefDiags)
+ reportUndefinedSymbol(undefDiag);
}
-std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
- bool Inserted = false;
- Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
- if (!Sym) {
- Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
- Sym->IsUsedInRegularObj = false;
- Sym->PendingArchiveLoad = false;
- Inserted = true;
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
+ bool inserted = false;
+ Symbol *&sym = symMap[CachedHashStringRef(name)];
+ if (!sym) {
+ sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ sym->isUsedInRegularObj = false;
+ sym->pendingArchiveLoad = false;
+ inserted = true;
}
- return {Sym, Inserted};
+ return {sym, inserted};
}
-std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, InputFile *File) {
- std::pair<Symbol *, bool> Result = insert(Name);
- if (!File || !isa<BitcodeFile>(File))
- Result.first->IsUsedInRegularObj = true;
- return Result;
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
+ std::pair<Symbol *, bool> result = insert(name);
+ if (!file || !isa<BitcodeFile>(file))
+ result.first->isUsedInRegularObj = true;
+ return result;
}
-Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
- bool IsWeakAlias) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, F);
- if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) {
- replaceSymbol<Undefined>(S, Name);
- return S;
+Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
+ bool isWeakAlias) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name, f);
+ if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
+ replaceSymbol<Undefined>(s, name);
+ return s;
}
- if (auto *L = dyn_cast<Lazy>(S)) {
- if (!S->PendingArchiveLoad) {
- S->PendingArchiveLoad = true;
- L->File->addMember(&L->Sym);
+ if (auto *l = dyn_cast<Lazy>(s)) {
+ if (!s->pendingArchiveLoad) {
+ s->pendingArchiveLoad = true;
+ l->file->addMember(&l->sym);
}
}
- return S;
+ return s;
}
-void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
- StringRef Name = Sym.getName();
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
- if (WasInserted) {
- replaceSymbol<Lazy>(S, F, Sym);
+void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) {
+ StringRef name = sym.getName();
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+ if (wasInserted) {
+ replaceSymbol<Lazy>(s, f, sym);
return;
}
- auto *U = dyn_cast<Undefined>(S);
- if (!U || U->WeakAlias || S->PendingArchiveLoad)
+ auto *u = dyn_cast<Undefined>(s);
+ if (!u || u->weakAlias || s->pendingArchiveLoad)
return;
- S->PendingArchiveLoad = true;
- F->addMember(&Sym);
+ s->pendingArchiveLoad = true;
+ f->addMember(&sym);
}
-void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
- std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " +
- toString(Existing->getFile()) + " and in " +
- toString(NewFile);
+void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
+ std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
+ toString(existing->getFile()) + " and in " +
+ toString(newFile);
- if (Config->ForceMultiple)
- warn(Msg);
+ if (config->forceMultiple)
+ warn(msg);
else
- error(Msg);
+ error(msg);
}
-Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
- replaceSymbol<DefinedAbsolute>(S, N, Sym);
- else if (!isa<DefinedCOFF>(S))
- reportDuplicate(S, nullptr);
- return S;
+Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ replaceSymbol<DefinedAbsolute>(s, n, sym);
+ else if (!isa<DefinedCOFF>(s))
+ reportDuplicate(s, nullptr);
+ return s;
}
-Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
- replaceSymbol<DefinedAbsolute>(S, N, VA);
- else if (!isa<DefinedCOFF>(S))
- reportDuplicate(S, nullptr);
- return S;
+Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ replaceSymbol<DefinedAbsolute>(s, n, va);
+ else if (!isa<DefinedCOFF>(s))
+ reportDuplicate(s, nullptr);
+ return s;
}
-Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
- replaceSymbol<DefinedSynthetic>(S, N, C);
- else if (!isa<DefinedCOFF>(S))
- reportDuplicate(S, nullptr);
- return S;
+Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ replaceSymbol<DefinedSynthetic>(s, n, c);
+ else if (!isa<DefinedCOFF>(s))
+ reportDuplicate(s, nullptr);
+ return s;
}
-Symbol *SymbolTable::addRegular(InputFile *F, StringRef N,
- const coff_symbol_generic *Sym,
- SectionChunk *C) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, F);
- if (WasInserted || !isa<DefinedRegular>(S))
- replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false,
- /*IsExternal*/ true, Sym, C);
+Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
+ const coff_symbol_generic *sym,
+ SectionChunk *c) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted || !isa<DefinedRegular>(s))
+ replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
+ /*IsExternal*/ true, sym, c);
else
- reportDuplicate(S, F);
- return S;
+ reportDuplicate(s, f);
+ return s;
}
-std::pair<Symbol *, bool>
-SymbolTable::addComdat(InputFile *F, StringRef N,
- const coff_symbol_generic *Sym) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, F);
- if (WasInserted || !isa<DefinedRegular>(S)) {
- replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
- /*IsExternal*/ true, Sym, nullptr);
- return {S, true};
+std::pair<DefinedRegular *, bool>
+SymbolTable::addComdat(InputFile *f, StringRef n,
+ const coff_symbol_generic *sym) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted || !isa<DefinedRegular>(s)) {
+ replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ true,
+ /*IsExternal*/ true, sym, nullptr);
+ return {cast<DefinedRegular>(s), true};
}
- if (!cast<DefinedRegular>(S)->isCOMDAT())
- reportDuplicate(S, F);
- return {S, false};
+ auto *existingSymbol = cast<DefinedRegular>(s);
+ if (!existingSymbol->isCOMDAT)
+ reportDuplicate(s, f);
+ return {existingSymbol, false};
}
-Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
- const coff_symbol_generic *Sym, CommonChunk *C) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, F);
- if (WasInserted || !isa<DefinedCOFF>(S))
- replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
- else if (auto *DC = dyn_cast<DefinedCommon>(S))
- if (Size > DC->getSize())
- replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
- return S;
+Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size,
+ const coff_symbol_generic *sym, CommonChunk *c) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted || !isa<DefinedCOFF>(s))
+ replaceSymbol<DefinedCommon>(s, f, n, size, sym, c);
+ else if (auto *dc = dyn_cast<DefinedCommon>(s))
+ if (size > dc->getSize())
+ replaceSymbol<DefinedCommon>(s, f, n, size, sym, c);
+ return s;
}
-Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
- replaceSymbol<DefinedImportData>(S, N, F);
- return S;
+Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
+ replaceSymbol<DefinedImportData>(s, n, f);
+ return s;
}
- reportDuplicate(S, F);
+ reportDuplicate(s, f);
return nullptr;
}
-Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
- uint16_t Machine) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
- replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
- return S;
+Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
+ uint16_t machine) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
+ replaceSymbol<DefinedImportThunk>(s, name, id, machine);
+ return s;
}
- reportDuplicate(S, ID->File);
+ reportDuplicate(s, id->file);
return nullptr;
}
std::vector<Chunk *> SymbolTable::getChunks() {
- std::vector<Chunk *> Res;
- for (ObjFile *File : ObjFile::Instances) {
- ArrayRef<Chunk *> V = File->getChunks();
- Res.insert(Res.end(), V.begin(), V.end());
+ std::vector<Chunk *> res;
+ for (ObjFile *file : ObjFile::instances) {
+ ArrayRef<Chunk *> v = file->getChunks();
+ res.insert(res.end(), v.begin(), v.end());
}
- return Res;
+ return res;
}
-Symbol *SymbolTable::find(StringRef Name) {
- return SymMap.lookup(CachedHashStringRef(Name));
+Symbol *SymbolTable::find(StringRef name) {
+ return symMap.lookup(CachedHashStringRef(name));
}
-Symbol *SymbolTable::findUnderscore(StringRef Name) {
- if (Config->Machine == I386)
- return find(("_" + Name).str());
- return find(Name);
+Symbol *SymbolTable::findUnderscore(StringRef name) {
+ if (config->machine == I386)
+ return find(("_" + name).str());
+ return find(name);
}
-StringRef SymbolTable::findByPrefix(StringRef Prefix) {
- for (auto Pair : SymMap) {
- StringRef Name = Pair.first.val();
- if (Name.startswith(Prefix))
- return Name;
+// Return all symbols that start with Prefix, possibly ignoring the first
+// character of Prefix or the first character symbol.
+std::vector<Symbol *> SymbolTable::getSymsWithPrefix(StringRef prefix) {
+ std::vector<Symbol *> syms;
+ for (auto pair : symMap) {
+ StringRef name = pair.first.val();
+ if (name.startswith(prefix) || name.startswith(prefix.drop_front()) ||
+ name.drop_front().startswith(prefix) ||
+ name.drop_front().startswith(prefix.drop_front())) {
+ syms.push_back(pair.second);
+ }
}
- return "";
+ return syms;
}
-StringRef SymbolTable::findMangle(StringRef Name) {
- if (Symbol *Sym = find(Name))
- if (!isa<Undefined>(Sym))
- return Name;
- if (Config->Machine != I386)
- return findByPrefix(("?" + Name + "@@Y").str());
- if (!Name.startswith("_"))
- return "";
+Symbol *SymbolTable::findMangle(StringRef name) {
+ if (Symbol *sym = find(name))
+ if (!isa<Undefined>(sym))
+ return sym;
+
+ // Efficient fuzzy string lookup is impossible with a hash table, so iterate
+ // the symbol table once and collect all possibly matching symbols into this
+ // vector. Then compare each possibly matching symbol with each possible
+ // mangling.
+ std::vector<Symbol *> syms = getSymsWithPrefix(name);
+ auto findByPrefix = [&syms](const Twine &t) -> Symbol * {
+ std::string prefix = t.str();
+ for (auto *s : syms)
+ if (s->getName().startswith(prefix))
+ return s;
+ return nullptr;
+ };
+
+ // For non-x86, just look for C++ functions.
+ if (config->machine != I386)
+ return findByPrefix("?" + name + "@@Y");
+
+ if (!name.startswith("_"))
+ return nullptr;
// Search for x86 stdcall function.
- StringRef S = findByPrefix((Name + "@").str());
- if (!S.empty())
- return S;
+ if (Symbol *s = findByPrefix(name + "@"))
+ return s;
// Search for x86 fastcall function.
- S = findByPrefix(("@" + Name.substr(1) + "@").str());
- if (!S.empty())
- return S;
+ if (Symbol *s = findByPrefix("@" + name.substr(1) + "@"))
+ return s;
// Search for x86 vectorcall function.
- S = findByPrefix((Name.substr(1) + "@@").str());
- if (!S.empty())
- return S;
+ if (Symbol *s = findByPrefix(name.substr(1) + "@@"))
+ return s;
// Search for x86 C++ non-member function.
- return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
-}
-
-void SymbolTable::mangleMaybe(Symbol *B) {
- auto *U = dyn_cast<Undefined>(B);
- if (!U || U->WeakAlias)
- return;
- StringRef Alias = findMangle(U->getName());
- if (!Alias.empty()) {
- log(U->getName() + " aliased to " + Alias);
- U->WeakAlias = addUndefined(Alias);
- }
+ return findByPrefix("?" + name.substr(1) + "@@Y");
}
-Symbol *SymbolTable::addUndefined(StringRef Name) {
- return addUndefined(Name, nullptr, false);
+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();
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *f : BitcodeFile::instances)
+ lto->add(*f);
+ return lto->compile();
}
void SymbolTable::addCombinedLTOObjects() {
- if (BitcodeFile::Instances.empty())
+ if (BitcodeFile::instances.empty())
return;
- ScopedTimer T(LTOTimer);
- for (StringRef Object : compileBitcodeFiles()) {
- auto *Obj = make<ObjFile>(MemoryBufferRef(Object, "lto.tmp"));
- Obj->parse();
- ObjFile::Instances.push_back(Obj);
+ ScopedTimer t(ltoTimer);
+ for (StringRef object : compileBitcodeFiles()) {
+ auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp"));
+ obj->parse();
+ ObjFile::instances.push_back(obj);
}
}
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 00e55dbb7a02..88f47cbe9e78 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -1,9 +1,8 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -28,6 +27,7 @@ class Chunk;
class CommonChunk;
class Defined;
class DefinedAbsolute;
+class DefinedRegular;
class DefinedRelative;
class Lazy;
class SectionChunk;
@@ -47,7 +47,7 @@ class Symbol;
// There is one add* function per symbol type.
class SymbolTable {
public:
- void addFile(InputFile *File);
+ void addFile(InputFile *file);
// Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined
@@ -55,21 +55,20 @@ public:
void reportRemainingUndefines();
void loadMinGWAutomaticImports();
- bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name);
+ bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks();
// Returns a symbol for a given name. Returns a nullptr if not found.
- Symbol *find(StringRef Name);
- Symbol *findUnderscore(StringRef Name);
+ Symbol *find(StringRef name);
+ Symbol *findUnderscore(StringRef name);
// Occasionally we have to resolve an undefined symbol to its
// mangled symbol. This function tries to find a mangled name
// for U from the symbol table, and if found, set the symbol as
// a weak alias for U.
- void mangleMaybe(Symbol *B);
- StringRef findMangle(StringRef Name);
+ Symbol *findMangle(StringRef name);
// Build a set of COFF objects representing the combined contents of
// BitcodeFiles and add them to the symbol table. Called after all files are
@@ -78,52 +77,53 @@ public:
std::vector<StringRef> compileBitcodeFiles();
// Creates an Undefined symbol for a given name.
- Symbol *addUndefined(StringRef Name);
-
- Symbol *addSynthetic(StringRef N, Chunk *C);
- Symbol *addAbsolute(StringRef N, uint64_t VA);
-
- Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
- void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
- Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
- Symbol *addRegular(InputFile *F, StringRef N,
- const llvm::object::coff_symbol_generic *S = nullptr,
- SectionChunk *C = nullptr);
- std::pair<Symbol *, bool>
- addComdat(InputFile *F, StringRef N,
- const llvm::object::coff_symbol_generic *S = nullptr);
- Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
- const llvm::object::coff_symbol_generic *S = nullptr,
- CommonChunk *C = nullptr);
- Symbol *addImportData(StringRef N, ImportFile *F);
- Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
- uint16_t Machine);
-
- void reportDuplicate(Symbol *Existing, InputFile *NewFile);
+ Symbol *addUndefined(StringRef name);
+
+ Symbol *addSynthetic(StringRef n, Chunk *c);
+ Symbol *addAbsolute(StringRef n, uint64_t va);
+
+ Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
+ void addLazy(ArchiveFile *f, const Archive::Symbol sym);
+ Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
+ Symbol *addRegular(InputFile *f, StringRef n,
+ const llvm::object::coff_symbol_generic *s = nullptr,
+ SectionChunk *c = nullptr);
+ std::pair<DefinedRegular *, bool>
+ addComdat(InputFile *f, StringRef n,
+ const llvm::object::coff_symbol_generic *s = nullptr);
+ Symbol *addCommon(InputFile *f, StringRef n, uint64_t size,
+ const llvm::object::coff_symbol_generic *s = nullptr,
+ CommonChunk *c = nullptr);
+ Symbol *addImportData(StringRef n, ImportFile *f);
+ Symbol *addImportThunk(StringRef name, DefinedImportData *s,
+ uint16_t machine);
+
+ void reportDuplicate(Symbol *existing, InputFile *newFile);
// A list of chunks which to be added to .rdata.
- std::vector<Chunk *> LocalImportChunks;
+ std::vector<Chunk *> localImportChunks;
// Iterates symbols in non-determinstic hash table order.
- template <typename T> void forEachSymbol(T Callback) {
- for (auto &Pair : SymMap)
- Callback(Pair.second);
+ template <typename T> void forEachSymbol(T callback) {
+ for (auto &pair : symMap)
+ callback(pair.second);
}
private:
/// Inserts symbol if not already present.
- std::pair<Symbol *, bool> insert(StringRef Name);
- /// Same as insert(Name), but also sets IsUsedInRegularObj.
- std::pair<Symbol *, bool> insert(StringRef Name, InputFile *F);
- StringRef findByPrefix(StringRef Prefix);
+ std::pair<Symbol *, bool> insert(StringRef name);
+ /// Same as insert(Name), but also sets isUsedInRegularObj.
+ std::pair<Symbol *, bool> insert(StringRef name, InputFile *f);
+
+ std::vector<Symbol *> getSymsWithPrefix(StringRef prefix);
- llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
- std::unique_ptr<BitcodeCompiler> LTO;
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
+ std::unique_ptr<BitcodeCompiler> lto;
};
-extern SymbolTable *Symtab;
+extern SymbolTable *symtab;
-std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex);
+std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index ccaf86417f10..3583d4cb28c1 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -1,9 +1,8 @@
//===- Symbols.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -19,11 +18,17 @@
using namespace llvm;
using namespace llvm::object;
+using namespace lld::coff;
+
+static_assert(sizeof(SymbolUnion) <= 48,
+ "symbols should be optimized for memory usage");
+
// Returns a symbol name for an error message.
-std::string lld::toString(coff::Symbol &B) {
- if (Optional<std::string> S = lld::demangleMSVC(B.getName()))
- return ("\"" + *S + "\" (" + B.getName() + ")").str();
- return B.getName();
+std::string lld::toString(coff::Symbol &b) {
+ if (config->demangle)
+ if (Optional<std::string> s = lld::demangleMSVC(b.getName()))
+ return *s;
+ return b.getName();
}
namespace lld {
@@ -37,70 +42,75 @@ StringRef Symbol::getName() {
// 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 (Name.empty()) {
- auto *D = cast<DefinedCOFF>(this);
- cast<ObjFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
+ 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 Name;
+ return StringRef(nameData, nameSize);
}
InputFile *Symbol::getFile() {
- if (auto *Sym = dyn_cast<DefinedCOFF>(this))
- return Sym->File;
- if (auto *Sym = dyn_cast<Lazy>(this))
- return Sym->File;
+ if (auto *sym = dyn_cast<DefinedCOFF>(this))
+ return sym->file;
+ if (auto *sym = dyn_cast<Lazy>(this))
+ return sym->file;
return nullptr;
}
bool Symbol::isLive() const {
- if (auto *R = dyn_cast<DefinedRegular>(this))
- return R->getChunk()->Live;
- if (auto *Imp = dyn_cast<DefinedImportData>(this))
- return Imp->File->Live;
- if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
- return Imp->WrappedSym->File->ThunkLive;
+ if (auto *r = dyn_cast<DefinedRegular>(this))
+ return r->getChunk()->live;
+ if (auto *imp = dyn_cast<DefinedImportData>(this))
+ return imp->file->live;
+ if (auto *imp = dyn_cast<DefinedImportThunk>(this))
+ return imp->wrappedSym->file->thunkLive;
// Assume any other kind of symbol is live.
return true;
}
// MinGW specific.
-void Symbol::replaceKeepingName(Symbol *Other, size_t Size) {
- StringRef OrigName = Name;
- memcpy(this, Other, Size);
- Name = OrigName;
+void Symbol::replaceKeepingName(Symbol *other, size_t size) {
+ StringRef origName = getName();
+ memcpy(this, other, size);
+ nameData = origName.data();
+ nameSize = origName.size();
}
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
- size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
- if (SymSize == sizeof(coff_symbol16))
- return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
- assert(SymSize == sizeof(coff_symbol32));
- return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym));
+ size_t symSize = cast<ObjFile>(file)->getCOFFObj()->getSymbolTableEntrySize();
+ if (symSize == sizeof(coff_symbol16))
+ return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(sym));
+ assert(symSize == sizeof(coff_symbol32));
+ return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym));
}
-uint16_t DefinedAbsolute::NumOutputSections;
+uint16_t DefinedAbsolute::numOutputSections;
-static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) {
- if (Machine == AMD64)
- return make<ImportThunkChunkX64>(S);
- if (Machine == I386)
- return make<ImportThunkChunkX86>(S);
- if (Machine == ARM64)
- return make<ImportThunkChunkARM64>(S);
- assert(Machine == ARMNT);
- return make<ImportThunkChunkARM>(S);
+static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) {
+ if (machine == AMD64)
+ return make<ImportThunkChunkX64>(s);
+ if (machine == I386)
+ return make<ImportThunkChunkX86>(s);
+ if (machine == ARM64)
+ return make<ImportThunkChunkARM64>(s);
+ assert(machine == ARMNT);
+ return make<ImportThunkChunkARM>(s);
}
-DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
- uint16_t Machine)
- : Defined(DefinedImportThunkKind, Name), WrappedSym(S),
- Data(makeImportThunk(S, Machine)) {}
+DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s,
+ uint16_t machine)
+ : Defined(DefinedImportThunkKind, name), wrappedSym(s),
+ data(makeImportThunk(s, machine)) {}
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
- for (Symbol *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
- if (auto *D = dyn_cast<Defined>(A))
- return D;
+ for (Symbol *a = weakAlias; a; a = cast<Undefined>(a)->weakAlias)
+ if (auto *d = dyn_cast<Defined>(a))
+ return d;
return nullptr;
}
} // namespace coff
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index 4a8693e22e3c..86cd4f585e50 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -1,9 +1,8 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -58,15 +57,12 @@ public:
LastDefinedKind = DefinedSyntheticKind,
};
- Kind kind() const { return static_cast<Kind>(SymbolKind); }
-
- // Returns true if this is an external symbol.
- bool isExternal() { return IsExternal; }
+ Kind kind() const { return static_cast<Kind>(symbolKind); }
// Returns the symbol name.
StringRef getName();
- void replaceKeepingName(Symbol *Other, size_t Size);
+ void replaceKeepingName(Symbol *other, size_t size);
// Returns the file from which this symbol was created.
InputFile *getFile();
@@ -77,46 +73,50 @@ public:
protected:
friend SymbolTable;
- explicit Symbol(Kind K, StringRef N = "")
- : SymbolKind(K), IsExternal(true), IsCOMDAT(false),
- WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false),
- IsRuntimePseudoReloc(false), Name(N) {}
+ explicit Symbol(Kind k, StringRef n = "")
+ : symbolKind(k), isExternal(true), isCOMDAT(false),
+ writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false),
+ isRuntimePseudoReloc(false), nameSize(n.size()),
+ nameData(n.empty() ? nullptr : n.data()) {}
- const unsigned SymbolKind : 8;
- unsigned IsExternal : 1;
+ const unsigned symbolKind : 8;
+ unsigned isExternal : 1;
+public:
// This bit is used by the \c DefinedRegular subclass.
- unsigned IsCOMDAT : 1;
+ unsigned isCOMDAT : 1;
-public:
// This bit is used by Writer::createSymbolAndStringTable() to prevent
// symbols from being written to the symbol table more than once.
- unsigned WrittenToSymtab : 1;
+ unsigned writtenToSymtab : 1;
// True if this symbol was referenced by a regular (non-bitcode) object.
- unsigned IsUsedInRegularObj : 1;
+ unsigned isUsedInRegularObj : 1;
// True if we've seen both a lazy and an undefined symbol with this symbol
// name, which means that we have enqueued an archive member load and should
// not load any more archive members to resolve the same symbol.
- unsigned PendingArchiveLoad : 1;
+ unsigned pendingArchiveLoad : 1;
/// True if we've already added this symbol to the list of GC roots.
- unsigned IsGCRoot : 1;
+ unsigned isGCRoot : 1;
- unsigned IsRuntimePseudoReloc : 1;
+ unsigned isRuntimePseudoReloc : 1;
protected:
- StringRef Name;
+ // Symbol name length. Assume symbol lengths fit in a 32-bit integer.
+ uint32_t nameSize;
+
+ const char *nameData;
};
// The base class for any defined symbols, including absolute symbols,
// etc.
class Defined : public Symbol {
public:
- Defined(Kind K, StringRef N) : Symbol(K, N) {}
+ Defined(Kind k, StringRef n) : Symbol(k, n) {}
- static bool classof(const Symbol *S) { return S->kind() <= LastDefinedKind; }
+ static bool classof(const Symbol *s) { return s->kind() <= LastDefinedKind; }
// Returns the RVA (relative virtual address) of this symbol. The
// writer sets and uses RVAs.
@@ -130,120 +130,119 @@ public:
// Symbols defined via a COFF object file or bitcode file. For COFF files, this
// stores a coff_symbol_generic*, and names of internal symbols are lazily
// loaded through that. For bitcode files, Sym is nullptr and the name is stored
-// as a StringRef.
+// as a decomposed StringRef.
class DefinedCOFF : public Defined {
friend Symbol;
public:
- DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
- : Defined(K, N), File(F), Sym(S) {}
+ DefinedCOFF(Kind k, InputFile *f, StringRef n, const coff_symbol_generic *s)
+ : Defined(k, n), file(f), sym(s) {}
- static bool classof(const Symbol *S) {
- return S->kind() <= LastDefinedCOFFKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() <= LastDefinedCOFFKind;
}
- InputFile *getFile() { return File; }
+ InputFile *getFile() { return file; }
COFFSymbolRef getCOFFSymbol();
- InputFile *File;
+ InputFile *file;
protected:
- const coff_symbol_generic *Sym;
+ const coff_symbol_generic *sym;
};
// Regular defined symbols read from object file symbol tables.
class DefinedRegular : public DefinedCOFF {
public:
- DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT,
- bool IsExternal = false,
- const coff_symbol_generic *S = nullptr,
- SectionChunk *C = nullptr)
- : DefinedCOFF(DefinedRegularKind, F, N, S), Data(C ? &C->Repl : nullptr) {
- this->IsExternal = IsExternal;
- this->IsCOMDAT = IsCOMDAT;
+ DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT,
+ bool isExternal = false,
+ const coff_symbol_generic *s = nullptr,
+ SectionChunk *c = nullptr)
+ : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) {
+ this->isExternal = isExternal;
+ this->isCOMDAT = isCOMDAT;
}
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedRegularKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedRegularKind;
}
- uint64_t getRVA() const { return (*Data)->getRVA() + Sym->Value; }
- bool isCOMDAT() const { return IsCOMDAT; }
- SectionChunk *getChunk() const { return *Data; }
- uint32_t getValue() const { return Sym->Value; }
+ uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; }
+ SectionChunk *getChunk() const { return *data; }
+ uint32_t getValue() const { return sym->Value; }
- SectionChunk **Data;
+ SectionChunk **data;
};
class DefinedCommon : public DefinedCOFF {
public:
- DefinedCommon(InputFile *F, StringRef N, uint64_t Size,
- const coff_symbol_generic *S = nullptr,
- CommonChunk *C = nullptr)
- : DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) {
- this->IsExternal = true;
+ DefinedCommon(InputFile *f, StringRef n, uint64_t size,
+ const coff_symbol_generic *s = nullptr,
+ CommonChunk *c = nullptr)
+ : DefinedCOFF(DefinedCommonKind, f, n, s), data(c), size(size) {
+ this->isExternal = true;
}
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedCommonKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedCommonKind;
}
- uint64_t getRVA() { return Data->getRVA(); }
- CommonChunk *getChunk() { return Data; }
+ uint64_t getRVA() { return data->getRVA(); }
+ CommonChunk *getChunk() { return data; }
private:
friend SymbolTable;
- uint64_t getSize() const { return Size; }
- CommonChunk *Data;
- uint64_t Size;
+ uint64_t getSize() const { return size; }
+ CommonChunk *data;
+ uint64_t size;
};
// Absolute symbols.
class DefinedAbsolute : public Defined {
public:
- DefinedAbsolute(StringRef N, COFFSymbolRef S)
- : Defined(DefinedAbsoluteKind, N), VA(S.getValue()) {
- IsExternal = S.isExternal();
+ DefinedAbsolute(StringRef n, COFFSymbolRef s)
+ : Defined(DefinedAbsoluteKind, n), va(s.getValue()) {
+ isExternal = s.isExternal();
}
- DefinedAbsolute(StringRef N, uint64_t V)
- : Defined(DefinedAbsoluteKind, N), VA(V) {}
+ DefinedAbsolute(StringRef n, uint64_t v)
+ : Defined(DefinedAbsoluteKind, n), va(v) {}
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedAbsoluteKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedAbsoluteKind;
}
- uint64_t getRVA() { return VA - Config->ImageBase; }
- void setVA(uint64_t V) { VA = V; }
+ uint64_t getRVA() { return va - config->imageBase; }
+ void setVA(uint64_t v) { va = v; }
// Section index relocations against absolute symbols resolve to
// this 16 bit number, and it is the largest valid section index
// plus one. This variable keeps it.
- static uint16_t NumOutputSections;
+ static uint16_t numOutputSections;
private:
- uint64_t VA;
+ uint64_t va;
};
// This symbol is used for linker-synthesized symbols like __ImageBase and
// __safe_se_handler_table.
class DefinedSynthetic : public Defined {
public:
- explicit DefinedSynthetic(StringRef Name, Chunk *C)
- : Defined(DefinedSyntheticKind, Name), C(C) {}
+ explicit DefinedSynthetic(StringRef name, Chunk *c)
+ : Defined(DefinedSyntheticKind, name), c(c) {}
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedSyntheticKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedSyntheticKind;
}
// A null chunk indicates that this is __ImageBase. Otherwise, this is some
// other synthesized chunk, like SEHTableChunk.
- uint32_t getRVA() { return C ? C->getRVA() : 0; }
- Chunk *getChunk() { return C; }
+ uint32_t getRVA() { return c ? c->getRVA() : 0; }
+ Chunk *getChunk() { return c; }
private:
- Chunk *C;
+ Chunk *c;
};
// This class represents a symbol defined in an archive file. It is
@@ -253,32 +252,32 @@ private:
// the same name, it will ask the Lazy to load a file.
class Lazy : public Symbol {
public:
- Lazy(ArchiveFile *F, const Archive::Symbol S)
- : Symbol(LazyKind, S.getName()), File(F), Sym(S) {}
+ Lazy(ArchiveFile *f, const Archive::Symbol s)
+ : Symbol(LazyKind, s.getName()), file(f), sym(s) {}
- static bool classof(const Symbol *S) { return S->kind() == LazyKind; }
+ static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
- ArchiveFile *File;
+ ArchiveFile *file;
private:
friend SymbolTable;
private:
- const Archive::Symbol Sym;
+ const Archive::Symbol sym;
};
// Undefined symbols.
class Undefined : public Symbol {
public:
- explicit Undefined(StringRef N) : Symbol(UndefinedKind, N) {}
+ explicit Undefined(StringRef n) : Symbol(UndefinedKind, n) {}
- static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
+ static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
// An undefined symbol can have a fallback symbol which gives an
// undefined symbol a second chance if it would remain undefined.
// If it remains undefined, it'll be replaced with whatever the
// Alias pointer points to.
- Symbol *WeakAlias = nullptr;
+ Symbol *weakAlias = nullptr;
// If this symbol is external weak, try to resolve it to a defined
// symbol by searching the chain of fallback symbols. Returns the symbol if
@@ -294,23 +293,23 @@ public:
// table in an output. The former has "__imp_" prefix.
class DefinedImportData : public Defined {
public:
- DefinedImportData(StringRef N, ImportFile *F)
- : Defined(DefinedImportDataKind, N), File(F) {
+ DefinedImportData(StringRef n, ImportFile *f)
+ : Defined(DefinedImportDataKind, n), file(f) {
}
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedImportDataKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedImportDataKind;
}
- uint64_t getRVA() { return File->Location->getRVA(); }
- Chunk *getChunk() { return File->Location; }
- void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
+ uint64_t getRVA() { return file->location->getRVA(); }
+ Chunk *getChunk() { return file->location; }
+ void setLocation(Chunk *addressTable) { file->location = addressTable; }
- StringRef getDLLName() { return File->DLLName; }
- StringRef getExternalName() { return File->ExternalName; }
- uint16_t getOrdinal() { return File->Hdr->OrdinalHint; }
+ StringRef getDLLName() { return file->dllName; }
+ StringRef getExternalName() { return file->externalName; }
+ uint16_t getOrdinal() { return file->hdr->OrdinalHint; }
- ImportFile *File;
+ ImportFile *file;
};
// This class represents a symbol for a jump table entry which jumps
@@ -320,19 +319,19 @@ public:
// a regular name. A function pointer is given as a DefinedImportData.
class DefinedImportThunk : public Defined {
public:
- DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine);
+ DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine);
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedImportThunkKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedImportThunkKind;
}
- uint64_t getRVA() { return Data->getRVA(); }
- Chunk *getChunk() { return Data; }
+ uint64_t getRVA() { return data->getRVA(); }
+ Chunk *getChunk() { return data; }
- DefinedImportData *WrappedSym;
+ DefinedImportData *wrappedSym;
private:
- Chunk *Data;
+ Chunk *data;
};
// If you have a symbol "foo" in your object file, a symbol name
@@ -342,18 +341,18 @@ private:
// This is here just for compatibility with MSVC.
class DefinedLocalImport : public Defined {
public:
- DefinedLocalImport(StringRef N, Defined *S)
- : Defined(DefinedLocalImportKind, N), Data(make<LocalImportChunk>(S)) {}
+ DefinedLocalImport(StringRef n, Defined *s)
+ : Defined(DefinedLocalImportKind, n), data(make<LocalImportChunk>(s)) {}
- static bool classof(const Symbol *S) {
- return S->kind() == DefinedLocalImportKind;
+ static bool classof(const Symbol *s) {
+ return s->kind() == DefinedLocalImportKind;
}
- uint64_t getRVA() { return Data->getRVA(); }
- Chunk *getChunk() { return Data; }
+ uint64_t getRVA() { return data->getRVA(); }
+ Chunk *getChunk() { return data; }
private:
- LocalImportChunk *Data;
+ LocalImportChunk *data;
};
inline uint64_t Defined::getRVA() {
@@ -406,19 +405,19 @@ inline Chunk *Defined::getChunk() {
// object. We allocate memory using this class and instantiate a symbol
// using the placement new.
union SymbolUnion {
- alignas(DefinedRegular) char A[sizeof(DefinedRegular)];
- alignas(DefinedCommon) char B[sizeof(DefinedCommon)];
- alignas(DefinedAbsolute) char C[sizeof(DefinedAbsolute)];
- alignas(DefinedSynthetic) char D[sizeof(DefinedSynthetic)];
- alignas(Lazy) char E[sizeof(Lazy)];
- alignas(Undefined) char F[sizeof(Undefined)];
- alignas(DefinedImportData) char G[sizeof(DefinedImportData)];
- alignas(DefinedImportThunk) char H[sizeof(DefinedImportThunk)];
- alignas(DefinedLocalImport) char I[sizeof(DefinedLocalImport)];
+ alignas(DefinedRegular) char a[sizeof(DefinedRegular)];
+ alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
+ alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
+ alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
+ alignas(Lazy) char e[sizeof(Lazy)];
+ alignas(Undefined) char f[sizeof(Undefined)];
+ alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
+ alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
+ alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
};
template <typename T, typename... ArgT>
-void replaceSymbol(Symbol *S, ArgT &&... Arg) {
+void replaceSymbol(Symbol *s, ArgT &&... arg) {
static_assert(std::is_trivially_destructible<T>(),
"Symbol types must be trivially destructible");
static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small");
@@ -426,11 +425,11 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) {
"SymbolUnion not aligned enough");
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
"Not a Symbol");
- new (S) T(std::forward<ArgT>(Arg)...);
+ new (s) T(std::forward<ArgT>(arg)...);
}
} // namespace coff
-std::string toString(coff::Symbol &B);
+std::string toString(coff::Symbol &b);
} // namespace lld
#endif
diff --git a/COFF/TypeMerger.h b/COFF/TypeMerger.h
new file mode 100644
index 000000000000..e2cfe668cfa5
--- /dev/null
+++ b/COFF/TypeMerger.h
@@ -0,0 +1,65 @@
+//===- TypeMerger.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_TYPEMERGER_H
+#define LLD_COFF_TYPEMERGER_H
+
+#include "Config.h"
+#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
+#include "llvm/Support/Allocator.h"
+
+namespace lld {
+namespace coff {
+
+class TypeMerger {
+public:
+ TypeMerger(llvm::BumpPtrAllocator &alloc)
+ : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc),
+ globalIDTable(alloc) {}
+
+ /// Get the type table or the global type table if /DEBUG:GHASH is enabled.
+ inline llvm::codeview::TypeCollection &getTypeTable() {
+ if (config->debugGHashes)
+ return globalTypeTable;
+ return typeTable;
+ }
+
+ /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
+ inline llvm::codeview::TypeCollection &getIDTable() {
+ if (config->debugGHashes)
+ return globalIDTable;
+ 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;
+
+ /// 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;
+};
+
+/// Map from type index and item index in a type server PDB to the
+/// corresponding index in the destination PDB.
+struct CVIndexMap {
+ llvm::SmallVector<llvm::codeview::TypeIndex, 0> tpiMap;
+ llvm::SmallVector<llvm::codeview::TypeIndex, 0> ipiMap;
+ bool isTypeServerMap = false;
+ bool isPrecompiledTypeMap = false;
+};
+
+} // namespace coff
+} // namespace lld
+
+#endif \ No newline at end of file
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index 258796ea6057..36ef87de4263 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -1,9 +1,8 @@
//===- Writer.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -17,6 +16,7 @@
#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"
@@ -63,104 +63,129 @@ align 8, db 0
$ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin
$ xxd -i /tmp/DOSProgram.bin
*/
-static unsigned char DOSProgram[] = {
+static unsigned char dosProgram[] = {
0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c,
0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65,
0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00
};
-static_assert(sizeof(DOSProgram) % 8 == 0,
+static_assert(sizeof(dosProgram) % 8 == 0,
"DOSProgram size must be multiple of 8");
-static const int SectorSize = 512;
-static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram);
-static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8");
+static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram);
+static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8");
+
+static const int numberOfDataDirectory = 16;
+
+// Global vector of all output sections. After output sections are finalized,
+// this can be indexed by Chunk::getOutputSection.
+static std::vector<OutputSection *> outputSections;
-static const int NumberOfDataDirectory = 16;
+OutputSection *Chunk::getOutputSection() const {
+ return osidx == 0 ? nullptr : outputSections[osidx - 1];
+}
namespace {
-class DebugDirectoryChunk : public Chunk {
+class DebugDirectoryChunk : public NonSectionChunk {
public:
- DebugDirectoryChunk(const std::vector<Chunk *> &R, bool WriteRepro)
- : Records(R), WriteRepro(WriteRepro) {}
+ DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
+ : records(r), writeRepro(writeRepro) {}
size_t getSize() const override {
- return (Records.size() + int(WriteRepro)) * sizeof(debug_directory);
+ return (records.size() + int(writeRepro)) * sizeof(debug_directory);
}
- void writeTo(uint8_t *B) const override {
- auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
+ 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);
- ++D;
+ 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);
+ ++d;
}
- if (WriteRepro) {
+ if (writeRepro) {
// FIXME: The COFF spec allows either a 0-sized entry to just say
// "the timestamp field is really a hash", or a 4-byte size field
// followed by that many bytes containing a longer hash (with the
// lowest 4 bytes usually being the timestamp in little-endian order).
// Consider storing the full 8 bytes computed by xxHash64 here.
- fillEntry(D, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0);
+ fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0);
}
}
- void setTimeDateStamp(uint32_t TimeDateStamp) {
- for (support::ulittle32_t *TDS : TimeDateStamps)
- *TDS = TimeDateStamp;
+ void setTimeDateStamp(uint32_t timeDateStamp) {
+ for (support::ulittle32_t *tds : timeDateStamps)
+ *tds = timeDateStamp;
}
private:
- void fillEntry(debug_directory *D, COFF::DebugType DebugType, size_t Size,
- uint64_t RVA, uint64_t Offs) const {
- D->Characteristics = 0;
- D->TimeDateStamp = 0;
- D->MajorVersion = 0;
- D->MinorVersion = 0;
- D->Type = DebugType;
- D->SizeOfData = Size;
- D->AddressOfRawData = RVA;
- D->PointerToRawData = Offs;
-
- TimeDateStamps.push_back(&D->TimeDateStamp);
- }
-
- mutable std::vector<support::ulittle32_t *> TimeDateStamps;
- const std::vector<Chunk *> &Records;
- bool WriteRepro;
+ void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size,
+ uint64_t rva, uint64_t offs) const {
+ d->Characteristics = 0;
+ d->TimeDateStamp = 0;
+ d->MajorVersion = 0;
+ d->MinorVersion = 0;
+ d->Type = debugType;
+ d->SizeOfData = size;
+ d->AddressOfRawData = rva;
+ d->PointerToRawData = offs;
+
+ timeDateStamps.push_back(&d->TimeDateStamp);
+ }
+
+ mutable std::vector<support::ulittle32_t *> timeDateStamps;
+ const std::vector<Chunk *> &records;
+ bool writeRepro;
};
-class CVDebugRecordChunk : public Chunk {
+class CVDebugRecordChunk : public NonSectionChunk {
public:
size_t getSize() const override {
- return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1;
+ return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1;
}
- void writeTo(uint8_t *B) const override {
+ void writeTo(uint8_t *b) const override {
// Save off the DebugInfo entry to backfill the file signature (build id)
// in Writer::writeBuildId
- BuildId = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
+ buildId = reinterpret_cast<codeview::DebugInfo *>(b);
// variable sized field (PDB Path)
- char *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*BuildId));
- if (!Config->PDBAltPath.empty())
- memcpy(P, Config->PDBAltPath.data(), Config->PDBAltPath.size());
- P[Config->PDBAltPath.size()] = '\0';
+ char *p = reinterpret_cast<char *>(b + sizeof(*buildId));
+ if (!config->pdbAltPath.empty())
+ memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size());
+ p[config->pdbAltPath.size()] = '\0';
}
- mutable codeview::DebugInfo *BuildId = nullptr;
+ mutable codeview::DebugInfo *buildId = nullptr;
+};
+
+// PartialSection represents a group of chunks that contribute to an
+// OutputSection. Collating a collection of PartialSections of same name and
+// characteristics constitutes the OutputSection.
+class PartialSectionKey {
+public:
+ StringRef name;
+ unsigned characteristics;
+
+ bool operator<(const PartialSectionKey &other) const {
+ int c = name.compare(other.name);
+ if (c == 1)
+ return false;
+ if (c == 0)
+ return characteristics < other.characteristics;
+ return true;
+ }
};
// The writer writes a SymbolTable result to a file.
class Writer {
public:
- Writer() : Buffer(errorHandler().OutputBuffer) {}
+ Writer() : buffer(errorHandler().outputBuffer) {}
void run();
private:
@@ -168,78 +193,81 @@ private:
void createMiscChunks();
void createImportTables();
void appendImportThunks();
- void locateImportTables(
- std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map);
+ void locateImportTables();
void createExportTable();
void mergeSections();
- void readRelocTargets();
void removeUnusedSections();
void assignAddresses();
void finalizeAddresses();
void removeEmptySections();
+ void assignOutputSectionIndices();
void createSymbolAndStringTable();
- void openFile(StringRef OutputPath);
+ void openFile(StringRef outputPath);
template <typename PEHeaderTy> void writeHeader();
void createSEHTable();
void createRuntimePseudoRelocs();
void insertCtorDtorSymbols();
void createGuardCFTables();
- void markSymbolsForRVATable(ObjFile *File,
- ArrayRef<SectionChunk *> SymIdxChunks,
- SymbolRVASet &TableSymbols);
- void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
- StringRef CountSym);
+ void markSymbolsForRVATable(ObjFile *file,
+ ArrayRef<SectionChunk *> symIdxChunks,
+ SymbolRVASet &tableSymbols);
+ void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
+ StringRef countSym);
void setSectionPermissions();
void writeSections();
void writeBuildId();
void sortExceptionTable();
- void sortCRTSectionChunks(std::vector<Chunk *> &Chunks);
+ void sortCRTSectionChunks(std::vector<Chunk *> &chunks);
+ void addSyntheticIdata();
+ void fixPartialSectionChars(StringRef name, uint32_t chars);
+ bool fixGnuImportChunks();
+ PartialSection *createPartialSection(StringRef name, uint32_t outChars);
+ PartialSection *findPartialSection(StringRef name, uint32_t outChars);
- llvm::Optional<coff_symbol16> createSymbol(Defined *D);
- size_t addEntryToStringTable(StringRef Str);
+ llvm::Optional<coff_symbol16> createSymbol(Defined *d);
+ size_t addEntryToStringTable(StringRef str);
- OutputSection *findSection(StringRef Name);
+ OutputSection *findSection(StringRef name);
void addBaserels();
- void addBaserelBlocks(std::vector<Baserel> &V);
+ void addBaserelBlocks(std::vector<Baserel> &v);
uint32_t getSizeOfInitializedData();
- std::map<StringRef, std::vector<DefinedImportData *>> binImports();
-
- std::unique_ptr<FileOutputBuffer> &Buffer;
- std::vector<OutputSection *> OutputSections;
- std::vector<char> Strtab;
- std::vector<llvm::object::coff_symbol16> OutputSymtab;
- IdataContents Idata;
- Chunk *ImportTableStart = nullptr;
- uint64_t ImportTableSize = 0;
- Chunk *IATStart = nullptr;
- uint64_t IATSize = 0;
- DelayLoadContents DelayIdata;
- EdataContents Edata;
- bool SetNoSEHCharacteristic = false;
-
- DebugDirectoryChunk *DebugDirectory = nullptr;
- std::vector<Chunk *> DebugRecords;
- CVDebugRecordChunk *BuildId = nullptr;
- ArrayRef<uint8_t> SectionTable;
-
- uint64_t FileSize;
- uint32_t PointerToSymbolTable = 0;
- uint64_t SizeOfImage;
- uint64_t SizeOfHeaders;
-
- OutputSection *TextSec;
- OutputSection *RdataSec;
- OutputSection *BuildidSec;
- OutputSection *DataSec;
- OutputSection *PdataSec;
- OutputSection *IdataSec;
- OutputSection *EdataSec;
- OutputSection *DidatSec;
- OutputSection *RsrcSec;
- OutputSection *RelocSec;
- OutputSection *CtorsSec;
- OutputSection *DtorsSec;
+
+ std::unique_ptr<FileOutputBuffer> &buffer;
+ std::map<PartialSectionKey, PartialSection *> partialSections;
+ std::vector<char> strtab;
+ std::vector<llvm::object::coff_symbol16> outputSymtab;
+ IdataContents idata;
+ Chunk *importTableStart = nullptr;
+ uint64_t importTableSize = 0;
+ Chunk *iatStart = nullptr;
+ uint64_t iatSize = 0;
+ DelayLoadContents delayIdata;
+ EdataContents edata;
+ bool setNoSEHCharacteristic = false;
+
+ DebugDirectoryChunk *debugDirectory = nullptr;
+ std::vector<Chunk *> debugRecords;
+ CVDebugRecordChunk *buildId = nullptr;
+ ArrayRef<uint8_t> sectionTable;
+
+ uint64_t fileSize;
+ uint32_t pointerToSymbolTable = 0;
+ uint64_t sizeOfImage;
+ uint64_t sizeOfHeaders;
+
+ OutputSection *textSec;
+ OutputSection *rdataSec;
+ OutputSection *buildidSec;
+ OutputSection *dataSec;
+ OutputSection *pdataSec;
+ OutputSection *idataSec;
+ OutputSection *edataSec;
+ OutputSection *didatSec;
+ OutputSection *rsrcSec;
+ OutputSection *relocSec;
+ OutputSection *ctorsSec;
+ OutputSection *dtorsSec;
// The first and last .pdata sections in the output file.
//
@@ -250,87 +278,115 @@ private:
// are entirely linker-generated we can keep track of their locations using
// the chunks that the linker creates. All .pdata chunks come from input
// files, so we need to keep track of them separately.
- Chunk *FirstPdata = nullptr;
- Chunk *LastPdata;
+ Chunk *firstPdata = nullptr;
+ Chunk *lastPdata;
};
} // anonymous namespace
namespace lld {
namespace coff {
-static Timer CodeLayoutTimer("Code Layout", Timer::root());
-static Timer DiskCommitTimer("Commit Output File", Timer::root());
+static Timer codeLayoutTimer("Code Layout", Timer::root());
+static Timer diskCommitTimer("Commit Output File", Timer::root());
void writeResult() { Writer().run(); }
-void OutputSection::addChunk(Chunk *C) {
- Chunks.push_back(C);
- C->setOutputSection(this);
+void OutputSection::addChunk(Chunk *c) {
+ chunks.push_back(c);
}
-void OutputSection::insertChunkAtStart(Chunk *C) {
- Chunks.insert(Chunks.begin(), C);
- C->setOutputSection(this);
+void OutputSection::insertChunkAtStart(Chunk *c) {
+ chunks.insert(chunks.begin(), c);
}
-void OutputSection::setPermissions(uint32_t C) {
- Header.Characteristics &= ~PermMask;
- Header.Characteristics |= C;
+void OutputSection::setPermissions(uint32_t c) {
+ header.Characteristics &= ~permMask;
+ header.Characteristics |= c;
}
-void OutputSection::merge(OutputSection *Other) {
- for (Chunk *C : Other->Chunks)
- C->setOutputSection(this);
- Chunks.insert(Chunks.end(), Other->Chunks.begin(), Other->Chunks.end());
- Other->Chunks.clear();
+void OutputSection::merge(OutputSection *other) {
+ chunks.insert(chunks.end(), other->chunks.begin(), other->chunks.end());
+ other->chunks.clear();
+ contribSections.insert(contribSections.end(), other->contribSections.begin(),
+ other->contribSections.end());
+ other->contribSections.clear();
}
// Write the section header to a given buffer.
-void OutputSection::writeHeaderTo(uint8_t *Buf) {
- auto *Hdr = reinterpret_cast<coff_section *>(Buf);
- *Hdr = Header;
- if (StringTableOff) {
+void OutputSection::writeHeaderTo(uint8_t *buf) {
+ auto *hdr = reinterpret_cast<coff_section *>(buf);
+ *hdr = header;
+ if (stringTableOff) {
// If name is too long, write offset into the string table as a name.
- sprintf(Hdr->Name, "/%d", StringTableOff);
+ sprintf(hdr->Name, "/%d", stringTableOff);
} else {
- assert(!Config->Debug || Name.size() <= COFF::NameSize ||
- (Hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
- strncpy(Hdr->Name, Name.data(),
- std::min(Name.size(), (size_t)COFF::NameSize));
+ assert(!config->debug || name.size() <= COFF::NameSize ||
+ (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
+ strncpy(hdr->Name, name.data(),
+ std::min(name.size(), (size_t)COFF::NameSize));
}
}
+void OutputSection::addContributingPartialSection(PartialSection *sec) {
+ contribSections.push_back(sec);
+}
+
} // namespace coff
} // namespace lld
// Check whether the target address S is in range from a relocation
-// of type RelType at address P.
-static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
- assert(Config->Machine == ARMNT);
- int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
- switch (RelType) {
- case IMAGE_REL_ARM_BRANCH20T:
- return isInt<21>(Diff);
- case IMAGE_REL_ARM_BRANCH24T:
- case IMAGE_REL_ARM_BLX23T:
- return isInt<25>(Diff);
- default:
- return true;
+// of type relType at address P.
+static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
+ if (config->machine == ARMNT) {
+ int64_t diff = AbsoluteDifference(s, p + 4) + margin;
+ switch (relType) {
+ case IMAGE_REL_ARM_BRANCH20T:
+ return isInt<21>(diff);
+ case IMAGE_REL_ARM_BRANCH24T:
+ case IMAGE_REL_ARM_BLX23T:
+ return isInt<25>(diff);
+ default:
+ return true;
+ }
+ } else if (config->machine == ARM64) {
+ int64_t diff = AbsoluteDifference(s, p) + margin;
+ switch (relType) {
+ case IMAGE_REL_ARM64_BRANCH26:
+ return isInt<28>(diff);
+ case IMAGE_REL_ARM64_BRANCH19:
+ return isInt<21>(diff);
+ case IMAGE_REL_ARM64_BRANCH14:
+ return isInt<16>(diff);
+ default:
+ return true;
+ }
+ } else {
+ llvm_unreachable("Unexpected architecture");
}
}
// Return the last thunk for the given target if it is in range,
// or create a new one.
static std::pair<Defined *, bool>
-getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
- uint16_t Type, int Margin) {
- Defined *&LastThunk = LastThunks[Target->getRVA()];
- if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
- return {LastThunk, false};
- RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
- Defined *D = make<DefinedSynthetic>("", C);
- LastThunk = D;
- return {D, true};
+getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target, uint64_t p,
+ uint16_t type, int margin) {
+ Defined *&lastThunk = lastThunks[target->getRVA()];
+ if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin))
+ return {lastThunk, false};
+ Chunk *c;
+ switch (config->machine) {
+ case ARMNT:
+ c = make<RangeExtensionThunkARM>(target);
+ break;
+ case ARM64:
+ c = make<RangeExtensionThunkARM64>(target);
+ break;
+ default:
+ llvm_unreachable("Unexpected architecture");
+ }
+ Defined *d = make<DefinedSynthetic>("", c);
+ lastThunk = d;
+ return {d, true};
}
// This checks all relocations, and for any relocation which isn't in range
@@ -344,81 +400,124 @@ getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
// After adding thunks, we verify that all relocations are in range (with
// no extra margin requirements). If this failed, we restart (throwing away
// the previously created thunks) and retry with a wider margin.
-static bool createThunks(std::vector<Chunk *> &Chunks, int Margin) {
- bool AddressesChanged = false;
- DenseMap<uint64_t, Defined *> LastThunks;
- size_t ThunksSize = 0;
+static bool createThunks(OutputSection *os, int margin) {
+ bool addressesChanged = false;
+ DenseMap<uint64_t, Defined *> lastThunks;
+ DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices;
+ size_t thunksSize = 0;
// Recheck Chunks.size() each iteration, since we can insert more
// elements into it.
- for (size_t I = 0; I != Chunks.size(); ++I) {
- SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Chunks[I]);
- if (!SC)
+ for (size_t i = 0; i != os->chunks.size(); ++i) {
+ SectionChunk *sc = dyn_cast_or_null<SectionChunk>(os->chunks[i]);
+ if (!sc)
continue;
- size_t ThunkInsertionSpot = I + 1;
+ size_t thunkInsertionSpot = i + 1;
// Try to get a good enough estimate of where new thunks will be placed.
// Offset this by the size of the new thunks added so far, to make the
// estimate slightly better.
- size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize;
- for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
- const coff_relocation &Rel = SC->Relocs[J];
- Symbol *&RelocTarget = SC->RelocTargets[J];
+ size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize;
+ ObjFile *file = sc->file;
+ std::vector<std::pair<uint32_t, uint32_t>> relocReplacements;
+ ArrayRef<coff_relocation> originalRelocs =
+ file->getCOFFObj()->getRelocations(sc->header);
+ for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) {
+ const coff_relocation &rel = originalRelocs[j];
+ Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex);
// The estimate of the source address P should be pretty accurate,
// but we don't know whether the target Symbol address should be
- // offset by ThunkSize or not (or by some of ThunksSize but not all of
+ // offset by thunksSize or not (or by some of thunksSize but not all of
// it), giving us some uncertainty once we have added one thunk.
- uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize;
+ uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize;
- Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
- if (!Sym)
+ Defined *sym = dyn_cast_or_null<Defined>(relocTarget);
+ if (!sym)
continue;
- uint64_t S = Sym->getRVA();
+ uint64_t s = sym->getRVA();
- if (isInRange(Rel.Type, S, P, Margin))
+ if (isInRange(rel.Type, s, p, margin))
continue;
// If the target isn't in range, hook it up to an existing or new
// thunk.
- Defined *Thunk;
- bool WasNew;
- std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin);
- if (WasNew) {
- Chunk *ThunkChunk = Thunk->getChunk();
- ThunkChunk->setRVA(
- ThunkInsertionRVA); // Estimate of where it will be located.
- Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk);
- ThunkInsertionSpot++;
- ThunksSize += ThunkChunk->getSize();
- ThunkInsertionRVA += ThunkChunk->getSize();
- AddressesChanged = true;
+ Defined *thunk;
+ bool wasNew;
+ std::tie(thunk, wasNew) = getThunk(lastThunks, sym, p, rel.Type, margin);
+ if (wasNew) {
+ Chunk *thunkChunk = thunk->getChunk();
+ thunkChunk->setRVA(
+ thunkInsertionRVA); // Estimate of where it will be located.
+ os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk);
+ thunkInsertionSpot++;
+ thunksSize += thunkChunk->getSize();
+ thunkInsertionRVA += thunkChunk->getSize();
+ addressesChanged = true;
}
- RelocTarget = Thunk;
+
+ // To redirect the relocation, add a symbol to the parent object file's
+ // symbol table, and replace the relocation symbol table index with the
+ // new index.
+ auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U});
+ uint32_t &thunkSymbolIndex = insertion.first->second;
+ if (insertion.second)
+ thunkSymbolIndex = file->addRangeThunkSymbol(thunk);
+ relocReplacements.push_back({j, thunkSymbolIndex});
+ }
+
+ // Get a writable copy of this section's relocations so they can be
+ // modified. If the relocations point into the object file, allocate new
+ // memory. Otherwise, this must be previously allocated memory that can be
+ // modified in place.
+ ArrayRef<coff_relocation> curRelocs = sc->getRelocs();
+ MutableArrayRef<coff_relocation> newRelocs;
+ if (originalRelocs.data() == curRelocs.data()) {
+ newRelocs = makeMutableArrayRef(
+ bAlloc.Allocate<coff_relocation>(originalRelocs.size()),
+ originalRelocs.size());
+ } else {
+ newRelocs = makeMutableArrayRef(
+ const_cast<coff_relocation *>(curRelocs.data()), curRelocs.size());
}
+
+ // Copy each relocation, but replace the symbol table indices which need
+ // thunks.
+ auto nextReplacement = relocReplacements.begin();
+ auto endReplacement = relocReplacements.end();
+ for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) {
+ newRelocs[i] = originalRelocs[i];
+ if (nextReplacement != endReplacement && nextReplacement->first == i) {
+ newRelocs[i].SymbolTableIndex = nextReplacement->second;
+ ++nextReplacement;
+ }
+ }
+
+ sc->setRelocs(newRelocs);
}
- return AddressesChanged;
+ return addressesChanged;
}
// Verify that all relocations are in range, with no extra margin requirements.
-static bool verifyRanges(const std::vector<Chunk *> Chunks) {
- for (Chunk *C : Chunks) {
- SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C);
- if (!SC)
+static bool verifyRanges(const std::vector<Chunk *> chunks) {
+ for (Chunk *c : chunks) {
+ SectionChunk *sc = dyn_cast_or_null<SectionChunk>(c);
+ if (!sc)
continue;
- for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
- const coff_relocation &Rel = SC->Relocs[J];
- Symbol *RelocTarget = SC->RelocTargets[J];
+ ArrayRef<coff_relocation> relocs = sc->getRelocs();
+ for (size_t j = 0, e = relocs.size(); j < e; ++j) {
+ const coff_relocation &rel = relocs[j];
+ Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex);
- Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
- if (!Sym)
+ Defined *sym = dyn_cast_or_null<Defined>(relocTarget);
+ if (!sym)
continue;
- uint64_t P = SC->getRVA() + Rel.VirtualAddress;
- uint64_t S = Sym->getRVA();
+ uint64_t p = sc->getRVA() + rel.VirtualAddress;
+ uint64_t s = sym->getRVA();
- if (!isInRange(Rel.Type, S, P, 0))
+ if (!isInRange(rel.Type, s, p, 0))
return false;
}
}
@@ -428,71 +527,68 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) {
// Assign addresses and add thunks if necessary.
void Writer::finalizeAddresses() {
assignAddresses();
- if (Config->Machine != ARMNT)
+ if (config->machine != ARMNT && config->machine != ARM64)
return;
- size_t OrigNumChunks = 0;
- for (OutputSection *Sec : OutputSections) {
- Sec->OrigChunks = Sec->Chunks;
- OrigNumChunks += Sec->Chunks.size();
+ size_t origNumChunks = 0;
+ for (OutputSection *sec : outputSections) {
+ sec->origChunks = sec->chunks;
+ origNumChunks += sec->chunks.size();
}
- int Pass = 0;
- int Margin = 1024 * 100;
+ int pass = 0;
+ int margin = 1024 * 100;
while (true) {
// First check whether we need thunks at all, or if the previous pass of
// adding them turned out ok.
- bool RangesOk = true;
- size_t NumChunks = 0;
- for (OutputSection *Sec : OutputSections) {
- if (!verifyRanges(Sec->Chunks)) {
- RangesOk = false;
+ bool rangesOk = true;
+ size_t numChunks = 0;
+ for (OutputSection *sec : outputSections) {
+ if (!verifyRanges(sec->chunks)) {
+ rangesOk = false;
break;
}
- NumChunks += Sec->Chunks.size();
+ numChunks += sec->chunks.size();
}
- if (RangesOk) {
- if (Pass > 0)
- log("Added " + Twine(NumChunks - OrigNumChunks) + " thunks with " +
- "margin " + Twine(Margin) + " in " + Twine(Pass) + " passes");
+ if (rangesOk) {
+ if (pass > 0)
+ log("Added " + Twine(numChunks - origNumChunks) + " thunks with " +
+ "margin " + Twine(margin) + " in " + Twine(pass) + " passes");
return;
}
- if (Pass >= 10)
- fatal("adding thunks hasn't converged after " + Twine(Pass) + " passes");
+ if (pass >= 10)
+ fatal("adding thunks hasn't converged after " + Twine(pass) + " passes");
- if (Pass > 0) {
+ if (pass > 0) {
// If the previous pass didn't work out, reset everything back to the
// original conditions before retrying with a wider margin. This should
// ideally never happen under real circumstances.
- for (OutputSection *Sec : OutputSections) {
- Sec->Chunks = Sec->OrigChunks;
- for (Chunk *C : Sec->Chunks)
- C->resetRelocTargets();
- }
- Margin *= 2;
+ for (OutputSection *sec : outputSections)
+ sec->chunks = sec->origChunks;
+ margin *= 2;
}
// Try adding thunks everywhere where it is needed, with a margin
// to avoid things going out of range due to the added thunks.
- bool AddressesChanged = false;
- for (OutputSection *Sec : OutputSections)
- AddressesChanged |= createThunks(Sec->Chunks, Margin);
+ bool addressesChanged = false;
+ for (OutputSection *sec : outputSections)
+ addressesChanged |= createThunks(sec, margin);
// If the verification above thought we needed thunks, we should have
// added some.
- assert(AddressesChanged);
+ assert(addressesChanged);
// Recalculate the layout for the whole image (and verify the ranges at
// the start of the next round).
assignAddresses();
- Pass++;
+ pass++;
}
}
// The main function of the writer.
void Writer::run() {
- ScopedTimer T1(CodeLayoutTimer);
+ ScopedTimer t1(codeLayoutTimer);
createImportTables();
createSections();
@@ -500,19 +596,19 @@ void Writer::run() {
appendImportThunks();
createExportTable();
mergeSections();
- readRelocTargets();
removeUnusedSections();
finalizeAddresses();
removeEmptySections();
+ assignOutputSectionIndices();
setSectionPermissions();
createSymbolAndStringTable();
- if (FileSize > UINT32_MAX)
- fatal("image size (" + Twine(FileSize) + ") " +
+ if (fileSize > UINT32_MAX)
+ fatal("image size (" + Twine(fileSize) + ") " +
"exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")");
- openFile(Config->OutputFile);
- if (Config->is64()) {
+ openFile(config->outputFile);
+ if (config->is64()) {
writeHeader<pe32plus_header>();
} else {
writeHeader<pe32_header>();
@@ -520,42 +616,59 @@ void Writer::run() {
writeSections();
sortExceptionTable();
- T1.stop();
+ t1.stop();
- if (!Config->PDBPath.empty() && Config->Debug) {
- assert(BuildId);
- createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId);
+ if (!config->pdbPath.empty() && config->debug) {
+ assert(buildId);
+ createPDB(symtab, outputSections, sectionTable, buildId->buildId);
}
writeBuildId();
- writeMapFile(OutputSections);
+ writeMapFile(outputSections);
- ScopedTimer T2(DiskCommitTimer);
- if (auto E = Buffer->commit())
- fatal("failed to write the output file: " + toString(std::move(E)));
+ ScopedTimer t2(diskCommitTimer);
+ if (auto e = buffer->commit())
+ fatal("failed to write the output file: " + toString(std::move(e)));
}
-static StringRef getOutputSectionName(StringRef Name) {
- StringRef S = Name.split('$').first;
+static StringRef getOutputSectionName(StringRef name) {
+ StringRef s = name.split('$').first;
// Treat a later period as a separator for MinGW, for sections like
// ".ctors.01234".
- return S.substr(0, S.find('.', 1));
+ return s.substr(0, s.find('.', 1));
}
// For /order.
-static void sortBySectionOrder(std::vector<Chunk *> &Chunks) {
- auto GetPriority = [](const Chunk *C) {
- if (auto *Sec = dyn_cast<SectionChunk>(C))
- if (Sec->Sym)
- return Config->Order.lookup(Sec->Sym->getName());
+static void sortBySectionOrder(std::vector<Chunk *> &chunks) {
+ auto getPriority = [](const Chunk *c) {
+ if (auto *sec = dyn_cast<SectionChunk>(c))
+ if (sec->sym)
+ return config->order.lookup(sec->sym->getName());
return 0;
};
- std::stable_sort(Chunks.begin(), Chunks.end(),
- [=](const Chunk *A, const Chunk *B) {
- return GetPriority(A) < GetPriority(B);
- });
+ llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) {
+ return getPriority(a) < getPriority(b);
+ });
+}
+
+// Change the characteristics of existing PartialSections that belong to the
+// section Name to Chars.
+void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) {
+ for (auto it : partialSections) {
+ PartialSection *pSec = it.second;
+ StringRef curName = pSec->name;
+ if (!curName.consume_front(name) ||
+ (!curName.empty() && !curName.startswith("$")))
+ continue;
+ if (pSec->characteristics == chars)
+ continue;
+ PartialSection *destSec = createPartialSection(pSec->name, chars);
+ destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(),
+ pSec->chunks.end());
+ pSec->chunks.clear();
+ }
}
// Sort concrete section chunks from GNU import libraries.
@@ -568,249 +681,250 @@ static void sortBySectionOrder(std::vector<Chunk *> &Chunks) {
// be formed correctly, the section chunks within each .idata$* section need
// to be grouped by library, and sorted alphabetically within each library
// (which makes sure the header comes first and the trailer last).
-static bool fixGnuImportChunks(
- std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
- uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+bool Writer::fixGnuImportChunks() {
+ uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
// Make sure all .idata$* section chunks are mapped as RDATA in order to
// be sorted into the same sections as our own synthesized .idata chunks.
- for (auto &Pair : Map) {
- StringRef SectionName = Pair.first.first;
- uint32_t OutChars = Pair.first.second;
- if (!SectionName.startswith(".idata"))
- continue;
- if (OutChars == RDATA)
- continue;
- std::vector<Chunk *> &SrcVect = Pair.second;
- std::vector<Chunk *> &DestVect = Map[{SectionName, RDATA}];
- DestVect.insert(DestVect.end(), SrcVect.begin(), SrcVect.end());
- SrcVect.clear();
- }
+ fixPartialSectionChars(".idata", rdata);
- bool HasIdata = false;
+ bool hasIdata = false;
// Sort all .idata$* chunks, grouping chunks from the same library,
// with alphabetical ordering of the object fils within a library.
- for (auto &Pair : Map) {
- StringRef SectionName = Pair.first.first;
- if (!SectionName.startswith(".idata"))
+ for (auto it : partialSections) {
+ PartialSection *pSec = it.second;
+ if (!pSec->name.startswith(".idata"))
continue;
- std::vector<Chunk *> &Chunks = Pair.second;
- if (!Chunks.empty())
- HasIdata = true;
- std::stable_sort(Chunks.begin(), Chunks.end(), [&](Chunk *S, Chunk *T) {
- SectionChunk *SC1 = dyn_cast_or_null<SectionChunk>(S);
- SectionChunk *SC2 = dyn_cast_or_null<SectionChunk>(T);
- if (!SC1 || !SC2) {
+ if (!pSec->chunks.empty())
+ hasIdata = true;
+ llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) {
+ SectionChunk *sc1 = dyn_cast_or_null<SectionChunk>(s);
+ SectionChunk *sc2 = dyn_cast_or_null<SectionChunk>(t);
+ if (!sc1 || !sc2) {
// if SC1, order them ascending. If SC2 or both null,
// S is not less than T.
- return SC1 != nullptr;
+ return sc1 != nullptr;
}
// Make a string with "libraryname/objectfile" for sorting, achieving
// both grouping by library and sorting of objects within a library,
// at once.
- std::string Key1 =
- (SC1->File->ParentName + "/" + SC1->File->getName()).str();
- std::string Key2 =
- (SC2->File->ParentName + "/" + SC2->File->getName()).str();
- return Key1 < Key2;
+ std::string key1 =
+ (sc1->file->parentName + "/" + sc1->file->getName()).str();
+ std::string key2 =
+ (sc2->file->parentName + "/" + sc2->file->getName()).str();
+ return key1 < key2;
});
}
- return HasIdata;
+ return hasIdata;
}
// Add generated idata chunks, for imported symbols and DLLs, and a
// terminator in .idata$2.
-static void addSyntheticIdata(
- IdataContents &Idata,
- std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
- uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
- Idata.create();
+void Writer::addSyntheticIdata() {
+ uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+ idata.create();
// Add the .idata content in the right section groups, to allow
// chunks from other linked in object files to be grouped together.
// See Microsoft PE/COFF spec 5.4 for details.
- auto Add = [&](StringRef N, std::vector<Chunk *> &V) {
- std::vector<Chunk *> &DestVect = Map[{N, RDATA}];
- DestVect.insert(DestVect.end(), V.begin(), V.end());
+ auto add = [&](StringRef n, std::vector<Chunk *> &v) {
+ PartialSection *pSec = createPartialSection(n, rdata);
+ pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end());
};
// The loader assumes a specific order of data.
// Add each type in the correct order.
- Add(".idata$2", Idata.Dirs);
- Add(".idata$4", Idata.Lookups);
- Add(".idata$5", Idata.Addresses);
- Add(".idata$6", Idata.Hints);
- Add(".idata$7", Idata.DLLNames);
+ add(".idata$2", idata.dirs);
+ add(".idata$4", idata.lookups);
+ add(".idata$5", idata.addresses);
+ add(".idata$6", idata.hints);
+ add(".idata$7", idata.dllNames);
}
// Locate the first Chunk and size of the import directory list and the
// IAT.
-void Writer::locateImportTables(
- std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
- uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
- std::vector<Chunk *> &ImportTables = Map[{".idata$2", RDATA}];
- if (!ImportTables.empty())
- ImportTableStart = ImportTables.front();
- for (Chunk *C : ImportTables)
- ImportTableSize += C->getSize();
-
- std::vector<Chunk *> &IAT = Map[{".idata$5", RDATA}];
- if (!IAT.empty())
- IATStart = IAT.front();
- for (Chunk *C : IAT)
- IATSize += C->getSize();
+void Writer::locateImportTables() {
+ uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+
+ if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) {
+ if (!importDirs->chunks.empty())
+ importTableStart = importDirs->chunks.front();
+ for (Chunk *c : importDirs->chunks)
+ importTableSize += c->getSize();
+ }
+
+ if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) {
+ if (!importAddresses->chunks.empty())
+ iatStart = importAddresses->chunks.front();
+ for (Chunk *c : importAddresses->chunks)
+ iatSize += c->getSize();
+ }
}
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, create the builtin sections.
- const uint32_t DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
- const uint32_t BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
- const uint32_t CODE = IMAGE_SCN_CNT_CODE;
- const uint32_t DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE;
- const uint32_t R = IMAGE_SCN_MEM_READ;
- const uint32_t W = IMAGE_SCN_MEM_WRITE;
- const uint32_t X = IMAGE_SCN_MEM_EXECUTE;
-
- SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> Sections;
- auto CreateSection = [&](StringRef Name, uint32_t OutChars) {
- OutputSection *&Sec = Sections[{Name, OutChars}];
- if (!Sec) {
- Sec = make<OutputSection>(Name, OutChars);
- OutputSections.push_back(Sec);
+ const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA;
+ const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
+ const uint32_t code = IMAGE_SCN_CNT_CODE;
+ const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE;
+ const uint32_t r = IMAGE_SCN_MEM_READ;
+ const uint32_t w = IMAGE_SCN_MEM_WRITE;
+ const uint32_t x = IMAGE_SCN_MEM_EXECUTE;
+
+ SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> sections;
+ auto createSection = [&](StringRef name, uint32_t outChars) {
+ OutputSection *&sec = sections[{name, outChars}];
+ if (!sec) {
+ sec = make<OutputSection>(name, outChars);
+ outputSections.push_back(sec);
}
- return Sec;
+ return sec;
};
// Try to match the section order used by link.exe.
- TextSec = CreateSection(".text", CODE | R | X);
- CreateSection(".bss", BSS | R | W);
- RdataSec = CreateSection(".rdata", DATA | R);
- BuildidSec = CreateSection(".buildid", DATA | R);
- DataSec = CreateSection(".data", DATA | R | W);
- PdataSec = CreateSection(".pdata", DATA | R);
- IdataSec = CreateSection(".idata", DATA | R);
- EdataSec = CreateSection(".edata", DATA | R);
- DidatSec = CreateSection(".didat", DATA | R);
- RsrcSec = CreateSection(".rsrc", DATA | R);
- RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R);
- CtorsSec = CreateSection(".ctors", DATA | R | W);
- DtorsSec = CreateSection(".dtors", DATA | R | W);
+ textSec = createSection(".text", code | r | x);
+ createSection(".bss", bss | r | w);
+ rdataSec = createSection(".rdata", data | r);
+ buildidSec = createSection(".buildid", data | r);
+ dataSec = createSection(".data", data | r | w);
+ pdataSec = createSection(".pdata", data | r);
+ idataSec = createSection(".idata", data | r);
+ edataSec = createSection(".edata", data | r);
+ didatSec = createSection(".didat", data | r);
+ rsrcSec = createSection(".rsrc", data | r);
+ relocSec = createSection(".reloc", data | discardable | r);
+ ctorsSec = createSection(".ctors", data | r | w);
+ dtorsSec = createSection(".dtors", data | r | w);
// Then bin chunks by name and output characteristics.
- std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map;
- for (Chunk *C : Symtab->getChunks()) {
- auto *SC = dyn_cast<SectionChunk>(C);
- if (SC && !SC->Live) {
- if (Config->Verbose)
- SC->printDiscardedMessage();
+ for (Chunk *c : symtab->getChunks()) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (sc && !sc->live) {
+ if (config->verbose)
+ sc->printDiscardedMessage();
continue;
}
- Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C);
+ StringRef name = c->getSectionName();
+ // On MinGW, comdat groups are formed by putting the comdat group name
+ // after the '$' in the section name. Such a section name suffix shouldn't
+ // imply separate alphabetical sorting of those section chunks though.
+ if (config->mingw && sc && sc->isCOMDAT())
+ name = name.split('$').first;
+ PartialSection *pSec = createPartialSection(name,
+ c->getOutputCharacteristics());
+ pSec->chunks.push_back(c);
}
+ fixPartialSectionChars(".rsrc", data | r);
// Even in non MinGW cases, we might need to link against GNU import
// libraries.
- bool HasIdata = fixGnuImportChunks(Map);
- if (!Idata.empty())
- HasIdata = true;
+ bool hasIdata = fixGnuImportChunks();
+ if (!idata.empty())
+ hasIdata = true;
- if (HasIdata)
- addSyntheticIdata(Idata, Map);
+ if (hasIdata)
+ addSyntheticIdata();
// Process an /order option.
- if (!Config->Order.empty())
- for (auto &Pair : Map)
- sortBySectionOrder(Pair.second);
+ if (!config->order.empty())
+ for (auto it : partialSections)
+ sortBySectionOrder(it.second->chunks);
- if (HasIdata)
- locateImportTables(Map);
+ if (hasIdata)
+ locateImportTables();
// Then create an OutputSection for each section.
// '$' and all following characters in input section names are
// discarded when determining output section. So, .text$foo
// contributes to .text, for example. See PE/COFF spec 3.2.
- for (auto &Pair : Map) {
- StringRef Name = getOutputSectionName(Pair.first.first);
- uint32_t OutChars = Pair.first.second;
+ for (auto it : partialSections) {
+ PartialSection *pSec = it.second;
+ StringRef name = getOutputSectionName(pSec->name);
+ uint32_t outChars = pSec->characteristics;
- if (Name == ".CRT") {
+ if (name == ".CRT") {
// In link.exe, there is a special case for the I386 target where .CRT
// sections are treated as if they have output characteristics DATA | R if
// their characteristics are DATA | R | W. This implements the same
// special case for all architectures.
- OutChars = DATA | R;
+ outChars = data | r;
- log("Processing section " + Pair.first.first + " -> " + Name);
+ log("Processing section " + pSec->name + " -> " + name);
- sortCRTSectionChunks(Pair.second);
+ sortCRTSectionChunks(pSec->chunks);
}
- OutputSection *Sec = CreateSection(Name, OutChars);
- std::vector<Chunk *> &Chunks = Pair.second;
- for (Chunk *C : Chunks)
- Sec->addChunk(C);
+ OutputSection *sec = createSection(name, outChars);
+ for (Chunk *c : pSec->chunks)
+ sec->addChunk(c);
+
+ sec->addContributingPartialSection(pSec);
}
// Finally, move some output sections to the end.
- auto SectionOrder = [&](OutputSection *S) {
- // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because
- // the loader cannot handle holes. Stripping can remove other discardable ones
- // than .reloc, which is first of them (created early).
- if (S->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ auto sectionOrder = [&](const OutputSection *s) {
+ // Move DISCARDABLE (or non-memory-mapped) sections to the end of file
+ // because the loader cannot handle holes. Stripping can remove other
+ // discardable ones than .reloc, which is first of them (created early).
+ if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
return 2;
// .rsrc should come at the end of the non-discardable sections because its
// size may change by the Win32 UpdateResources() function, causing
// subsequent sections to move (see https://crbug.com/827082).
- if (S == RsrcSec)
+ if (s == rsrcSec)
return 1;
return 0;
};
- std::stable_sort(OutputSections.begin(), OutputSections.end(),
- [&](OutputSection *S, OutputSection *T) {
- return SectionOrder(S) < SectionOrder(T);
- });
+ llvm::stable_sort(outputSections,
+ [&](const OutputSection *s, const OutputSection *t) {
+ return sectionOrder(s) < sectionOrder(t);
+ });
}
void Writer::createMiscChunks() {
- for (auto &P : MergeChunk::Instances)
- RdataSec->addChunk(P.second);
+ for (MergeChunk *p : MergeChunk::instances) {
+ if (p) {
+ p->finalizeContents();
+ rdataSec->addChunk(p);
+ }
+ }
// Create thunks for locally-dllimported symbols.
- if (!Symtab->LocalImportChunks.empty()) {
- for (Chunk *C : Symtab->LocalImportChunks)
- RdataSec->addChunk(C);
+ if (!symtab->localImportChunks.empty()) {
+ for (Chunk *c : symtab->localImportChunks)
+ rdataSec->addChunk(c);
}
// Create Debug Information Chunks
- OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec;
- if (Config->Debug || Config->Repro) {
- DebugDirectory = make<DebugDirectoryChunk>(DebugRecords, Config->Repro);
- DebugInfoSec->addChunk(DebugDirectory);
+ OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
+ if (config->debug || config->repro) {
+ debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
+ debugInfoSec->addChunk(debugDirectory);
}
- if (Config->Debug) {
+ if (config->debug) {
// Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We
// output a PDB no matter what, and this chunk provides the only means of
// 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);
+ buildId = make<CVDebugRecordChunk>();
+ debugRecords.push_back(buildId);
- for (Chunk *C : DebugRecords)
- DebugInfoSec->addChunk(C);
+ for (Chunk *c : debugRecords)
+ debugInfoSec->addChunk(c);
}
// Create SEH table. x86-only.
- if (Config->Machine == I386)
+ if (config->safeSEH)
createSEHTable();
// Create /guard:cf tables if requested.
- if (Config->GuardCF != GuardCFLevel::Off)
+ if (config->guardCF != GuardCFLevel::Off)
createGuardCFTables();
- if (Config->MinGW) {
+ if (config->mingw) {
createRuntimePseudoRelocs();
insertCtorDtorSymbols();
@@ -825,106 +939,123 @@ void Writer::createImportTables() {
// Initialize DLLOrder so that import entries are ordered in
// the same order as in the command line. (That affects DLL
// initialization order, and this ordering is MSVC-compatible.)
- for (ImportFile *File : ImportFile::Instances) {
- if (!File->Live)
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
continue;
- std::string DLL = StringRef(File->DLLName).lower();
- if (Config->DLLOrder.count(DLL) == 0)
- Config->DLLOrder[DLL] = Config->DLLOrder.size();
-
- if (File->ImpSym && !isa<DefinedImportData>(File->ImpSym))
- fatal(toString(*File->ImpSym) + " was replaced");
- DefinedImportData *ImpSym = cast_or_null<DefinedImportData>(File->ImpSym);
- if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) {
- if (!File->ThunkSym)
- fatal("cannot delay-load " + toString(File) +
- " due to import of data: " + toString(*ImpSym));
- DelayIdata.add(ImpSym);
+ std::string dll = StringRef(file->dllName).lower();
+ if (config->dllOrder.count(dll) == 0)
+ config->dllOrder[dll] = config->dllOrder.size();
+
+ if (file->impSym && !isa<DefinedImportData>(file->impSym))
+ fatal(toString(*file->impSym) + " was replaced");
+ DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym);
+ if (config->delayLoads.count(StringRef(file->dllName).lower())) {
+ if (!file->thunkSym)
+ fatal("cannot delay-load " + toString(file) +
+ " due to import of data: " + toString(*impSym));
+ delayIdata.add(impSym);
} else {
- Idata.add(ImpSym);
+ idata.add(impSym);
}
}
}
void Writer::appendImportThunks() {
- if (ImportFile::Instances.empty())
+ if (ImportFile::instances.empty())
return;
- for (ImportFile *File : ImportFile::Instances) {
- if (!File->Live)
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
continue;
- if (!File->ThunkSym)
+ if (!file->thunkSym)
continue;
- if (!isa<DefinedImportThunk>(File->ThunkSym))
- fatal(toString(*File->ThunkSym) + " was replaced");
- DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym);
- if (File->ThunkLive)
- TextSec->addChunk(Thunk->getChunk());
+ if (!isa<DefinedImportThunk>(file->thunkSym))
+ fatal(toString(*file->thunkSym) + " was replaced");
+ DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
+ if (file->thunkLive)
+ textSec->addChunk(thunk->getChunk());
}
- if (!DelayIdata.empty()) {
- Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
- DelayIdata.create(Helper);
- for (Chunk *C : DelayIdata.getChunks())
- DidatSec->addChunk(C);
- for (Chunk *C : DelayIdata.getDataChunks())
- DataSec->addChunk(C);
- for (Chunk *C : DelayIdata.getCodeChunks())
- TextSec->addChunk(C);
+ if (!delayIdata.empty()) {
+ Defined *helper = cast<Defined>(config->delayLoadHelper);
+ delayIdata.create(helper);
+ for (Chunk *c : delayIdata.getChunks())
+ didatSec->addChunk(c);
+ for (Chunk *c : delayIdata.getDataChunks())
+ dataSec->addChunk(c);
+ for (Chunk *c : delayIdata.getCodeChunks())
+ textSec->addChunk(c);
}
}
void Writer::createExportTable() {
- if (Config->Exports.empty())
+ if (config->exports.empty())
return;
- for (Chunk *C : Edata.Chunks)
- EdataSec->addChunk(C);
+ for (Chunk *c : edata.chunks)
+ edataSec->addChunk(c);
}
void Writer::removeUnusedSections() {
// Remove sections that we can be sure won't get content, to avoid
// allocating space for their section headers.
- auto IsUnused = [this](OutputSection *S) {
- if (S == RelocSec)
+ auto isUnused = [this](OutputSection *s) {
+ if (s == relocSec)
return false; // This section is populated later.
// MergeChunks have zero size at this point, as their size is finalized
// later. Only remove sections that have no Chunks at all.
- return S->Chunks.empty();
+ return s->chunks.empty();
};
- OutputSections.erase(
- std::remove_if(OutputSections.begin(), OutputSections.end(), IsUnused),
- OutputSections.end());
+ outputSections.erase(
+ std::remove_if(outputSections.begin(), outputSections.end(), isUnused),
+ outputSections.end());
}
// The Windows loader doesn't seem to like empty sections,
// so we remove them if any.
void Writer::removeEmptySections() {
- auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; };
- OutputSections.erase(
- std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty),
- OutputSections.end());
- uint32_t Idx = 1;
- for (OutputSection *Sec : OutputSections)
- Sec->SectionIndex = Idx++;
+ auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; };
+ outputSections.erase(
+ std::remove_if(outputSections.begin(), outputSections.end(), isEmpty),
+ outputSections.end());
}
-size_t Writer::addEntryToStringTable(StringRef Str) {
- assert(Str.size() > COFF::NameSize);
- size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field
- Strtab.insert(Strtab.end(), Str.begin(), Str.end());
- Strtab.push_back('\0');
- return OffsetOfEntry;
+void Writer::assignOutputSectionIndices() {
+ // Assign final output section indices, and assign each chunk to its output
+ // section.
+ uint32_t idx = 1;
+ for (OutputSection *os : outputSections) {
+ os->sectionIndex = idx;
+ for (Chunk *c : os->chunks)
+ c->setOutputSectionIdx(idx);
+ ++idx;
+ }
+
+ // Merge chunks are containers of chunks, so assign those an output section
+ // too.
+ for (MergeChunk *mc : MergeChunk::instances)
+ if (mc)
+ for (SectionChunk *sc : mc->sections)
+ if (sc && sc->live)
+ sc->setOutputSectionIdx(mc->getOutputSectionIdx());
}
-Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
- coff_symbol16 Sym;
- switch (Def->kind()) {
+size_t Writer::addEntryToStringTable(StringRef str) {
+ assert(str.size() > COFF::NameSize);
+ size_t offsetOfEntry = strtab.size() + 4; // +4 for the size field
+ strtab.insert(strtab.end(), str.begin(), str.end());
+ strtab.push_back('\0');
+ return offsetOfEntry;
+}
+
+Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
+ coff_symbol16 sym;
+ switch (def->kind()) {
case Symbol::DefinedAbsoluteKind:
- Sym.Value = Def->getRVA();
- Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
+ sym.Value = def->getRVA();
+ sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
break;
case Symbol::DefinedSyntheticKind:
// Relative symbols are unrepresentable in a COFF symbol table.
@@ -932,38 +1063,38 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
default: {
// Don't write symbols that won't be written to the output to the symbol
// table.
- Chunk *C = Def->getChunk();
- if (!C)
+ Chunk *c = def->getChunk();
+ if (!c)
return None;
- OutputSection *OS = C->getOutputSection();
- if (!OS)
+ OutputSection *os = c->getOutputSection();
+ if (!os)
return None;
- Sym.Value = Def->getRVA() - OS->getRVA();
- Sym.SectionNumber = OS->SectionIndex;
+ sym.Value = def->getRVA() - os->getRVA();
+ sym.SectionNumber = os->sectionIndex;
break;
}
}
- StringRef Name = Def->getName();
- if (Name.size() > COFF::NameSize) {
- Sym.Name.Offset.Zeroes = 0;
- Sym.Name.Offset.Offset = addEntryToStringTable(Name);
+ StringRef name = def->getName();
+ if (name.size() > COFF::NameSize) {
+ sym.Name.Offset.Zeroes = 0;
+ sym.Name.Offset.Offset = addEntryToStringTable(name);
} else {
- memset(Sym.Name.ShortName, 0, COFF::NameSize);
- memcpy(Sym.Name.ShortName, Name.data(), Name.size());
+ memset(sym.Name.ShortName, 0, COFF::NameSize);
+ memcpy(sym.Name.ShortName, name.data(), name.size());
}
- if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
- COFFSymbolRef Ref = D->getCOFFSymbol();
- Sym.Type = Ref.getType();
- Sym.StorageClass = Ref.getStorageClass();
+ if (auto *d = dyn_cast<DefinedCOFF>(def)) {
+ COFFSymbolRef ref = d->getCOFFSymbol();
+ sym.Type = ref.getType();
+ sym.StorageClass = ref.getStorageClass();
} else {
- Sym.Type = IMAGE_SYM_TYPE_NULL;
- Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
+ sym.Type = IMAGE_SYM_TYPE_NULL;
+ sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
}
- Sym.NumberOfAuxSymbols = 0;
- return Sym;
+ sym.NumberOfAuxSymbols = 0;
+ return sym;
}
void Writer::createSymbolAndStringTable() {
@@ -975,113 +1106,120 @@ void Writer::createSymbolAndStringTable() {
// solution where discardable sections have long names preserved and
// non-discardable sections have their names truncated, to ensure that any
// section which is mapped at runtime also has its name mapped at runtime.
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Name.size() <= COFF::NameSize)
+ for (OutputSection *sec : outputSections) {
+ if (sec->name.size() <= COFF::NameSize)
continue;
- if ((Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
+ if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
continue;
- Sec->setStringTableOff(addEntryToStringTable(Sec->Name));
+ sec->setStringTableOff(addEntryToStringTable(sec->name));
}
- if (Config->DebugDwarf || Config->DebugSymtab) {
- for (ObjFile *File : ObjFile::Instances) {
- for (Symbol *B : File->getSymbols()) {
- auto *D = dyn_cast_or_null<Defined>(B);
- if (!D || D->WrittenToSymtab)
+ if (config->debugDwarf || config->debugSymtab) {
+ for (ObjFile *file : ObjFile::instances) {
+ for (Symbol *b : file->getSymbols()) {
+ auto *d = dyn_cast_or_null<Defined>(b);
+ if (!d || d->writtenToSymtab)
continue;
- D->WrittenToSymtab = true;
+ d->writtenToSymtab = true;
- if (Optional<coff_symbol16> Sym = createSymbol(D))
- OutputSymtab.push_back(*Sym);
+ if (Optional<coff_symbol16> sym = createSymbol(d))
+ outputSymtab.push_back(*sym);
}
}
}
- if (OutputSymtab.empty() && Strtab.empty())
+ if (outputSymtab.empty() && strtab.empty())
return;
// We position the symbol table to be adjacent to the end of the last section.
- uint64_t FileOff = FileSize;
- PointerToSymbolTable = FileOff;
- FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
- FileOff += 4 + Strtab.size();
- FileSize = alignTo(FileOff, SectorSize);
+ uint64_t fileOff = fileSize;
+ pointerToSymbolTable = fileOff;
+ fileOff += outputSymtab.size() * sizeof(coff_symbol16);
+ fileOff += 4 + strtab.size();
+ fileSize = alignTo(fileOff, config->fileAlign);
}
void Writer::mergeSections() {
- if (!PdataSec->Chunks.empty()) {
- FirstPdata = PdataSec->Chunks.front();
- LastPdata = PdataSec->Chunks.back();
+ if (!pdataSec->chunks.empty()) {
+ firstPdata = pdataSec->chunks.front();
+ lastPdata = pdataSec->chunks.back();
}
- for (auto &P : Config->Merge) {
- StringRef ToName = P.second;
- if (P.first == ToName)
+ for (auto &p : config->merge) {
+ StringRef toName = p.second;
+ if (p.first == toName)
continue;
- StringSet<> Names;
+ StringSet<> names;
while (1) {
- if (!Names.insert(ToName).second)
- fatal("/merge: cycle found for section '" + P.first + "'");
- auto I = Config->Merge.find(ToName);
- if (I == Config->Merge.end())
+ if (!names.insert(toName).second)
+ fatal("/merge: cycle found for section '" + p.first + "'");
+ auto i = config->merge.find(toName);
+ if (i == config->merge.end())
break;
- ToName = I->second;
+ toName = i->second;
}
- OutputSection *From = findSection(P.first);
- OutputSection *To = findSection(ToName);
- if (!From)
+ OutputSection *from = findSection(p.first);
+ OutputSection *to = findSection(toName);
+ if (!from)
continue;
- if (!To) {
- From->Name = ToName;
+ if (!to) {
+ from->name = toName;
continue;
}
- To->merge(From);
+ to->merge(from);
}
}
-// Visits all sections to initialize their relocation targets.
-void Writer::readRelocTargets() {
- for (OutputSection *Sec : OutputSections)
- for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
- [&](Chunk *C) { C->readRelocTargets(); });
-}
-
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
- SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
- sizeof(data_directory) * NumberOfDataDirectory +
- sizeof(coff_section) * OutputSections.size();
- SizeOfHeaders +=
- Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
- SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize);
- uint64_t RVA = PageSize; // The first page is kept unmapped.
- FileSize = SizeOfHeaders;
-
- for (OutputSection *Sec : OutputSections) {
- if (Sec == RelocSec)
+ sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
+ sizeof(data_directory) * numberOfDataDirectory +
+ sizeof(coff_section) * outputSections.size();
+ sizeOfHeaders +=
+ config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
+ sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);
+ uint64_t rva = pageSize; // The first page is kept unmapped.
+ fileSize = sizeOfHeaders;
+
+ for (OutputSection *sec : outputSections) {
+ if (sec == relocSec)
addBaserels();
- uint64_t RawSize = 0, VirtualSize = 0;
- Sec->Header.VirtualAddress = RVA;
- for (Chunk *C : Sec->Chunks) {
- VirtualSize = alignTo(VirtualSize, C->Alignment);
- C->setRVA(RVA + VirtualSize);
- C->OutputSectionOff = VirtualSize;
- C->finalizeContents();
- VirtualSize += C->getSize();
- if (C->hasData())
- RawSize = alignTo(VirtualSize, SectorSize);
+ uint64_t rawSize = 0, virtualSize = 0;
+ sec->header.VirtualAddress = rva;
+
+ // If /FUNCTIONPADMIN is used, functions are padded in order to create a
+ // hotpatchable image.
+ const bool isCodeSection =
+ (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) &&
+ (sec->header.Characteristics & IMAGE_SCN_MEM_READ) &&
+ (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE);
+ uint32_t padding = isCodeSection ? config->functionPadMin : 0;
+
+ for (Chunk *c : sec->chunks) {
+ if (padding && c->isHotPatchable())
+ virtualSize += padding;
+ virtualSize = alignTo(virtualSize, c->getAlignment());
+ c->setRVA(rva + virtualSize);
+ virtualSize += c->getSize();
+ if (c->hasData)
+ rawSize = alignTo(virtualSize, config->fileAlign);
}
- if (VirtualSize > UINT32_MAX)
- error("section larger than 4 GiB: " + Sec->Name);
- Sec->Header.VirtualSize = VirtualSize;
- Sec->Header.SizeOfRawData = RawSize;
- if (RawSize != 0)
- Sec->Header.PointerToRawData = FileSize;
- RVA += alignTo(VirtualSize, PageSize);
- FileSize += alignTo(RawSize, SectorSize);
- }
- SizeOfImage = alignTo(RVA, PageSize);
+ if (virtualSize > UINT32_MAX)
+ error("section larger than 4 GiB: " + sec->name);
+ sec->header.VirtualSize = virtualSize;
+ sec->header.SizeOfRawData = rawSize;
+ if (rawSize != 0)
+ sec->header.PointerToRawData = fileSize;
+ rva += alignTo(virtualSize, pageSize);
+ fileSize += alignTo(rawSize, config->fileAlign);
+ }
+ sizeOfImage = alignTo(rva, pageSize);
+
+ // Assign addresses to sections in MergeChunks.
+ for (MergeChunk *mc : MergeChunk::instances)
+ if (mc)
+ mc->assignSubsectionRVAs();
}
template <typename PEHeaderTy> void Writer::writeHeader() {
@@ -1090,275 +1228,299 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
// under DOS, that program gets run (usually to just print an error message).
// When run under Windows, the loader looks at AddressOfNewExeHeader and uses
// the PE header instead.
- uint8_t *Buf = Buffer->getBufferStart();
- auto *DOS = reinterpret_cast<dos_header *>(Buf);
- Buf += sizeof(dos_header);
- DOS->Magic[0] = 'M';
- DOS->Magic[1] = 'Z';
- DOS->UsedBytesInTheLastPage = DOSStubSize % 512;
- DOS->FileSizeInPages = divideCeil(DOSStubSize, 512);
- DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16;
-
- DOS->AddressOfRelocationTable = sizeof(dos_header);
- DOS->AddressOfNewExeHeader = DOSStubSize;
+ uint8_t *buf = buffer->getBufferStart();
+ auto *dos = reinterpret_cast<dos_header *>(buf);
+ buf += sizeof(dos_header);
+ dos->Magic[0] = 'M';
+ dos->Magic[1] = 'Z';
+ dos->UsedBytesInTheLastPage = dosStubSize % 512;
+ dos->FileSizeInPages = divideCeil(dosStubSize, 512);
+ dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16;
+
+ dos->AddressOfRelocationTable = sizeof(dos_header);
+ dos->AddressOfNewExeHeader = dosStubSize;
// Write DOS program.
- memcpy(Buf, DOSProgram, sizeof(DOSProgram));
- Buf += sizeof(DOSProgram);
+ memcpy(buf, dosProgram, sizeof(dosProgram));
+ buf += sizeof(dosProgram);
// Write PE magic
- memcpy(Buf, PEMagic, sizeof(PEMagic));
- Buf += sizeof(PEMagic);
+ memcpy(buf, PEMagic, sizeof(PEMagic));
+ buf += sizeof(PEMagic);
// Write COFF header
- auto *COFF = reinterpret_cast<coff_file_header *>(Buf);
- Buf += sizeof(*COFF);
- COFF->Machine = Config->Machine;
- COFF->NumberOfSections = OutputSections.size();
- COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
- if (Config->LargeAddressAware)
- COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
- if (!Config->is64())
- COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE;
- if (Config->DLL)
- COFF->Characteristics |= IMAGE_FILE_DLL;
- if (!Config->Relocatable)
- COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
- COFF->SizeOfOptionalHeader =
- sizeof(PEHeaderTy) + sizeof(data_directory) * NumberOfDataDirectory;
+ auto *coff = reinterpret_cast<coff_file_header *>(buf);
+ buf += sizeof(*coff);
+ coff->Machine = config->machine;
+ coff->NumberOfSections = outputSections.size();
+ coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
+ if (config->largeAddressAware)
+ coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
+ if (!config->is64())
+ coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE;
+ if (config->dll)
+ coff->Characteristics |= IMAGE_FILE_DLL;
+ if (!config->relocatable)
+ coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
+ if (config->swaprunCD)
+ coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
+ if (config->swaprunNet)
+ coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP;
+ coff->SizeOfOptionalHeader =
+ sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory;
// Write PE header
- auto *PE = reinterpret_cast<PEHeaderTy *>(Buf);
- Buf += sizeof(*PE);
- PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;
+ auto *pe = reinterpret_cast<PEHeaderTy *>(buf);
+ buf += sizeof(*pe);
+ pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;
// If {Major,Minor}LinkerVersion is left at 0.0, then for some
// reason signing the resulting PE file with Authenticode produces a
// signature that fails to validate on Windows 7 (but is OK on 10).
// Set it to 14.0, which is what VS2015 outputs, and which avoids
// that problem.
- PE->MajorLinkerVersion = 14;
- PE->MinorLinkerVersion = 0;
-
- PE->ImageBase = Config->ImageBase;
- PE->SectionAlignment = PageSize;
- PE->FileAlignment = SectorSize;
- PE->MajorImageVersion = Config->MajorImageVersion;
- PE->MinorImageVersion = Config->MinorImageVersion;
- PE->MajorOperatingSystemVersion = Config->MajorOSVersion;
- PE->MinorOperatingSystemVersion = Config->MinorOSVersion;
- PE->MajorSubsystemVersion = Config->MajorOSVersion;
- PE->MinorSubsystemVersion = Config->MinorOSVersion;
- PE->Subsystem = Config->Subsystem;
- PE->SizeOfImage = SizeOfImage;
- PE->SizeOfHeaders = SizeOfHeaders;
- if (!Config->NoEntry) {
- Defined *Entry = cast<Defined>(Config->Entry);
- PE->AddressOfEntryPoint = Entry->getRVA();
+ pe->MajorLinkerVersion = 14;
+ pe->MinorLinkerVersion = 0;
+
+ pe->ImageBase = config->imageBase;
+ pe->SectionAlignment = pageSize;
+ pe->FileAlignment = config->fileAlign;
+ pe->MajorImageVersion = config->majorImageVersion;
+ pe->MinorImageVersion = config->minorImageVersion;
+ pe->MajorOperatingSystemVersion = config->majorOSVersion;
+ pe->MinorOperatingSystemVersion = config->minorOSVersion;
+ pe->MajorSubsystemVersion = config->majorOSVersion;
+ pe->MinorSubsystemVersion = config->minorOSVersion;
+ pe->Subsystem = config->subsystem;
+ pe->SizeOfImage = sizeOfImage;
+ pe->SizeOfHeaders = sizeOfHeaders;
+ if (!config->noEntry) {
+ Defined *entry = cast<Defined>(config->entry);
+ pe->AddressOfEntryPoint = entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
- if (Config->Machine == ARMNT)
- PE->AddressOfEntryPoint |= 1;
- }
- PE->SizeOfStackReserve = Config->StackReserve;
- PE->SizeOfStackCommit = Config->StackCommit;
- PE->SizeOfHeapReserve = Config->HeapReserve;
- PE->SizeOfHeapCommit = Config->HeapCommit;
- if (Config->AppContainer)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
- if (Config->DynamicBase)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
- if (Config->HighEntropyVA)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
- if (!Config->AllowBind)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND;
- if (Config->NxCompat)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
- if (!Config->AllowIsolation)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
- if (Config->GuardCF != GuardCFLevel::Off)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;
- if (Config->IntegrityCheck)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;
- if (SetNoSEHCharacteristic)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
- if (Config->TerminalServerAware)
- PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
- PE->NumberOfRvaAndSize = NumberOfDataDirectory;
- if (TextSec->getVirtualSize()) {
- PE->BaseOfCode = TextSec->getRVA();
- PE->SizeOfCode = TextSec->getRawSize();
- }
- PE->SizeOfInitializedData = getSizeOfInitializedData();
+ if (config->machine == ARMNT)
+ pe->AddressOfEntryPoint |= 1;
+ }
+ pe->SizeOfStackReserve = config->stackReserve;
+ pe->SizeOfStackCommit = config->stackCommit;
+ pe->SizeOfHeapReserve = config->heapReserve;
+ pe->SizeOfHeapCommit = config->heapCommit;
+ if (config->appContainer)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
+ if (config->dynamicBase)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
+ if (config->highEntropyVA)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
+ if (!config->allowBind)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND;
+ if (config->nxCompat)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
+ if (!config->allowIsolation)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
+ if (config->guardCF != GuardCFLevel::Off)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;
+ if (config->integrityCheck)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;
+ if (setNoSEHCharacteristic)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
+ if (config->terminalServerAware)
+ pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
+ pe->NumberOfRvaAndSize = numberOfDataDirectory;
+ if (textSec->getVirtualSize()) {
+ pe->BaseOfCode = textSec->getRVA();
+ pe->SizeOfCode = textSec->getRawSize();
+ }
+ pe->SizeOfInitializedData = getSizeOfInitializedData();
// Write data directory
- auto *Dir = reinterpret_cast<data_directory *>(Buf);
- Buf += sizeof(*Dir) * NumberOfDataDirectory;
- if (!Config->Exports.empty()) {
- Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA();
- Dir[EXPORT_TABLE].Size = Edata.getSize();
- }
- if (ImportTableStart) {
- Dir[IMPORT_TABLE].RelativeVirtualAddress = ImportTableStart->getRVA();
- Dir[IMPORT_TABLE].Size = ImportTableSize;
- }
- if (IATStart) {
- Dir[IAT].RelativeVirtualAddress = IATStart->getRVA();
- Dir[IAT].Size = IATSize;
- }
- if (RsrcSec->getVirtualSize()) {
- Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA();
- Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize();
- }
- if (FirstPdata) {
- Dir[EXCEPTION_TABLE].RelativeVirtualAddress = FirstPdata->getRVA();
- Dir[EXCEPTION_TABLE].Size =
- LastPdata->getRVA() + LastPdata->getSize() - FirstPdata->getRVA();
- }
- if (RelocSec->getVirtualSize()) {
- Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA();
- Dir[BASE_RELOCATION_TABLE].Size = RelocSec->getVirtualSize();
- }
- if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
- if (Defined *B = dyn_cast<Defined>(Sym)) {
- Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
- Dir[TLS_TABLE].Size = Config->is64()
+ auto *dir = reinterpret_cast<data_directory *>(buf);
+ buf += sizeof(*dir) * numberOfDataDirectory;
+ if (!config->exports.empty()) {
+ dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA();
+ dir[EXPORT_TABLE].Size = edata.getSize();
+ }
+ if (importTableStart) {
+ dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
+ dir[IMPORT_TABLE].Size = importTableSize;
+ }
+ if (iatStart) {
+ dir[IAT].RelativeVirtualAddress = iatStart->getRVA();
+ dir[IAT].Size = iatSize;
+ }
+ if (rsrcSec->getVirtualSize()) {
+ dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA();
+ dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize();
+ }
+ if (firstPdata) {
+ dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA();
+ dir[EXCEPTION_TABLE].Size =
+ lastPdata->getRVA() + lastPdata->getSize() - firstPdata->getRVA();
+ }
+ if (relocSec->getVirtualSize()) {
+ dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
+ dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize();
+ }
+ if (Symbol *sym = symtab->findUnderscore("_tls_used")) {
+ if (Defined *b = dyn_cast<Defined>(sym)) {
+ dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
+ dir[TLS_TABLE].Size = config->is64()
? sizeof(object::coff_tls_directory64)
: sizeof(object::coff_tls_directory32);
}
}
- if (DebugDirectory) {
- Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
- Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
+ if (debugDirectory) {
+ dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();
+ dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();
}
- if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
- if (auto *B = dyn_cast<DefinedRegular>(Sym)) {
- SectionChunk *SC = B->getChunk();
- assert(B->getRVA() >= SC->getRVA());
- uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
- if (!SC->hasData() || OffsetInChunk + 4 > SC->getSize())
+ if (Symbol *sym = symtab->findUnderscore("_load_config_used")) {
+ if (auto *b = dyn_cast<DefinedRegular>(sym)) {
+ SectionChunk *sc = b->getChunk();
+ assert(b->getRVA() >= sc->getRVA());
+ uint64_t offsetInChunk = b->getRVA() - sc->getRVA();
+ if (!sc->hasData || offsetInChunk + 4 > sc->getSize())
fatal("_load_config_used is malformed");
- ArrayRef<uint8_t> SecContents = SC->getContents();
- uint32_t LoadConfigSize =
- *reinterpret_cast<const ulittle32_t *>(&SecContents[OffsetInChunk]);
- if (OffsetInChunk + LoadConfigSize > SC->getSize())
+ ArrayRef<uint8_t> secContents = sc->getContents();
+ uint32_t loadConfigSize =
+ *reinterpret_cast<const ulittle32_t *>(&secContents[offsetInChunk]);
+ if (offsetInChunk + loadConfigSize > sc->getSize())
fatal("_load_config_used is too large");
- Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA();
- Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize;
+ dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA();
+ dir[LOAD_CONFIG_TABLE].Size = loadConfigSize;
}
}
- if (!DelayIdata.empty()) {
- Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
- DelayIdata.getDirRVA();
- Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
+ if (!delayIdata.empty()) {
+ dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
+ delayIdata.getDirRVA();
+ dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize();
}
// Write section table
- for (OutputSection *Sec : OutputSections) {
- Sec->writeHeaderTo(Buf);
- Buf += sizeof(coff_section);
+ for (OutputSection *sec : outputSections) {
+ sec->writeHeaderTo(buf);
+ buf += sizeof(coff_section);
}
- SectionTable = ArrayRef<uint8_t>(
- Buf - OutputSections.size() * sizeof(coff_section), Buf);
+ sectionTable = ArrayRef<uint8_t>(
+ buf - outputSections.size() * sizeof(coff_section), buf);
- if (OutputSymtab.empty() && Strtab.empty())
+ if (outputSymtab.empty() && strtab.empty())
return;
- COFF->PointerToSymbolTable = PointerToSymbolTable;
- uint32_t NumberOfSymbols = OutputSymtab.size();
- COFF->NumberOfSymbols = NumberOfSymbols;
- auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(
- Buffer->getBufferStart() + COFF->PointerToSymbolTable);
- for (size_t I = 0; I != NumberOfSymbols; ++I)
- SymbolTable[I] = OutputSymtab[I];
+ coff->PointerToSymbolTable = pointerToSymbolTable;
+ uint32_t numberOfSymbols = outputSymtab.size();
+ coff->NumberOfSymbols = numberOfSymbols;
+ auto *symbolTable = reinterpret_cast<coff_symbol16 *>(
+ buffer->getBufferStart() + coff->PointerToSymbolTable);
+ for (size_t i = 0; i != numberOfSymbols; ++i)
+ symbolTable[i] = outputSymtab[i];
// Create the string table, it follows immediately after the symbol table.
// The first 4 bytes is length including itself.
- Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]);
- write32le(Buf, Strtab.size() + 4);
- if (!Strtab.empty())
- memcpy(Buf + 4, Strtab.data(), Strtab.size());
+ buf = reinterpret_cast<uint8_t *>(&symbolTable[numberOfSymbols]);
+ write32le(buf, strtab.size() + 4);
+ if (!strtab.empty())
+ memcpy(buf + 4, strtab.data(), strtab.size());
}
-void Writer::openFile(StringRef Path) {
- Buffer = CHECK(
- FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable),
- "failed to open " + Path);
+void Writer::openFile(StringRef path) {
+ buffer = CHECK(
+ FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable),
+ "failed to open " + path);
}
void Writer::createSEHTable() {
- // Set the no SEH characteristic on x86 binaries unless we find exception
- // handlers.
- SetNoSEHCharacteristic = true;
-
- SymbolRVASet Handlers;
- for (ObjFile *File : ObjFile::Instances) {
- // FIXME: We should error here instead of earlier unless /safeseh:no was
- // passed.
- if (!File->hasSafeSEH())
- return;
-
- markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers);
+ SymbolRVASet handlers;
+ for (ObjFile *file : ObjFile::instances) {
+ if (!file->hasSafeSEH())
+ error("/safeseh: " + file->getName() + " is not compatible with SEH");
+ markSymbolsForRVATable(file, file->getSXDataChunks(), handlers);
}
- // Remove the "no SEH" characteristic if all object files were built with
- // safeseh, we found some exception handlers, and there is a load config in
- // the object.
- SetNoSEHCharacteristic =
- Handlers.empty() || !Symtab->findUnderscore("_load_config_used");
+ // Set the "no SEH" characteristic if there really were no handlers, or if
+ // there is no load config object to point to the table of handlers.
+ setNoSEHCharacteristic =
+ handlers.empty() || !symtab->findUnderscore("_load_config_used");
- maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table",
+ maybeAddRVATable(std::move(handlers), "__safe_se_handler_table",
"__safe_se_handler_count");
}
// Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set
// cannot contain duplicates. Therefore, the set is uniqued by Chunk and the
// symbol's offset into that Chunk.
-static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) {
- Chunk *C = S->getChunk();
- if (auto *SC = dyn_cast<SectionChunk>(C))
- C = SC->Repl; // Look through ICF replacement.
- uint32_t Off = S->getRVA() - (C ? C->getRVA() : 0);
- RVASet.insert({C, Off});
+static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) {
+ Chunk *c = s->getChunk();
+ if (auto *sc = dyn_cast<SectionChunk>(c))
+ c = sc->repl; // Look through ICF replacement.
+ uint32_t off = s->getRVA() - (c ? c->getRVA() : 0);
+ rvaSet.insert({c, off});
}
// Given a symbol, add it to the GFIDs table if it is a live, defined, function
// symbol in an executable section.
-static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms,
- Symbol *S) {
- auto *D = dyn_cast_or_null<DefinedCOFF>(S);
-
- // Ignore undefined symbols and references to non-functions (e.g. globals and
- // labels).
- if (!D ||
- D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION)
+static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
+ Symbol *s) {
+ if (!s)
return;
- // Mark the symbol as address taken if it's in an executable section.
- Chunk *RefChunk = D->getChunk();
- OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
- if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
- addSymbolToRVASet(AddressTakenSyms, D);
+ switch (s->kind()) {
+ case Symbol::DefinedLocalImportKind:
+ case Symbol::DefinedImportDataKind:
+ // Defines an __imp_ pointer, so it is data, so it is ignored.
+ break;
+ case Symbol::DefinedCommonKind:
+ // Common is always data, so it is ignored.
+ break;
+ case Symbol::DefinedAbsoluteKind:
+ case Symbol::DefinedSyntheticKind:
+ // Absolute is never code, synthetic generally isn't and usually isn't
+ // determinable.
+ break;
+ case Symbol::LazyKind:
+ case Symbol::UndefinedKind:
+ // Undefined symbols resolve to zero, so they don't have an RVA. Lazy
+ // symbols shouldn't have relocations.
+ break;
+
+ case Symbol::DefinedImportThunkKind:
+ // Thunks are always code, include them.
+ addSymbolToRVASet(addressTakenSyms, cast<Defined>(s));
+ break;
+
+ case Symbol::DefinedRegularKind: {
+ // This is a regular, defined, symbol from a COFF file. Mark the symbol as
+ // address taken if the symbol type is function and it's in an executable
+ // section.
+ auto *d = cast<DefinedRegular>(s);
+ if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) {
+ SectionChunk *sc = dyn_cast<SectionChunk>(d->getChunk());
+ if (sc && sc->live &&
+ sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)
+ addSymbolToRVASet(addressTakenSyms, d);
+ }
+ break;
+ }
+ }
}
// Visit all relocations from all section contributions of this object file and
// mark the relocation target as address-taken.
-static void markSymbolsWithRelocations(ObjFile *File,
- SymbolRVASet &UsedSymbols) {
- for (Chunk *C : File->getChunks()) {
+static void markSymbolsWithRelocations(ObjFile *file,
+ SymbolRVASet &usedSymbols) {
+ for (Chunk *c : file->getChunks()) {
// We only care about live section chunks. Common chunks and other chunks
// don't generally contain relocations.
- SectionChunk *SC = dyn_cast<SectionChunk>(C);
- if (!SC || !SC->Live)
+ SectionChunk *sc = dyn_cast<SectionChunk>(c);
+ if (!sc || !sc->live)
continue;
- for (const coff_relocation &Reloc : SC->Relocs) {
- if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32)
+ for (const coff_relocation &reloc : sc->getRelocs()) {
+ if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32)
// Ignore relative relocations on x86. On x86_64 they can't be ignored
// since they're also used to compute absolute addresses.
continue;
- Symbol *Ref = SC->File->getSymbol(Reloc.SymbolTableIndex);
- maybeAddAddressTakenFunction(UsedSymbols, Ref);
+ Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex);
+ maybeAddAddressTakenFunction(usedSymbols, ref);
}
}
}
@@ -1367,109 +1529,109 @@ static void markSymbolsWithRelocations(ObjFile *File,
// address-taken functions. It is sorted and uniqued, just like the safe SEH
// table.
void Writer::createGuardCFTables() {
- SymbolRVASet AddressTakenSyms;
- SymbolRVASet LongJmpTargets;
- for (ObjFile *File : ObjFile::Instances) {
+ SymbolRVASet addressTakenSyms;
+ SymbolRVASet longJmpTargets;
+ for (ObjFile *file : ObjFile::instances) {
// If the object was compiled with /guard:cf, the address taken symbols
// are in .gfids$y sections, and the longjmp targets are in .gljmp$y
// sections. If the object was not compiled with /guard:cf, we assume there
// were no setjmp targets, and that all code symbols with relocations are
// possibly address-taken.
- if (File->hasGuardCF()) {
- markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms);
- markSymbolsForRVATable(File, File->getGuardLJmpChunks(), LongJmpTargets);
+ if (file->hasGuardCF()) {
+ markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms);
+ markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets);
} else {
- markSymbolsWithRelocations(File, AddressTakenSyms);
+ markSymbolsWithRelocations(file, addressTakenSyms);
}
}
// Mark the image entry as address-taken.
- if (Config->Entry)
- maybeAddAddressTakenFunction(AddressTakenSyms, Config->Entry);
+ if (config->entry)
+ maybeAddAddressTakenFunction(addressTakenSyms, config->entry);
// Mark exported symbols in executable sections as address-taken.
- for (Export &E : Config->Exports)
- maybeAddAddressTakenFunction(AddressTakenSyms, E.Sym);
+ for (Export &e : config->exports)
+ maybeAddAddressTakenFunction(addressTakenSyms, e.sym);
// Ensure sections referenced in the gfid table are 16-byte aligned.
- for (const ChunkAndOffset &C : AddressTakenSyms)
- if (C.InputChunk->Alignment < 16)
- C.InputChunk->Alignment = 16;
+ for (const ChunkAndOffset &c : addressTakenSyms)
+ if (c.inputChunk->getAlignment() < 16)
+ c.inputChunk->setAlignment(16);
- maybeAddRVATable(std::move(AddressTakenSyms), "__guard_fids_table",
+ maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table",
"__guard_fids_count");
// Add the longjmp target table unless the user told us not to.
- if (Config->GuardCF == GuardCFLevel::Full)
- maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table",
+ if (config->guardCF == GuardCFLevel::Full)
+ maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table",
"__guard_longjmp_count");
// Set __guard_flags, which will be used in the load config to indicate that
// /guard:cf was enabled.
- uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) |
+ uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) |
uint32_t(coff_guard_flags::HasFidTable);
- if (Config->GuardCF == GuardCFLevel::Full)
- GuardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable);
- Symbol *FlagSym = Symtab->findUnderscore("__guard_flags");
- cast<DefinedAbsolute>(FlagSym)->setVA(GuardFlags);
+ if (config->guardCF == GuardCFLevel::Full)
+ guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable);
+ Symbol *flagSym = symtab->findUnderscore("__guard_flags");
+ cast<DefinedAbsolute>(flagSym)->setVA(guardFlags);
}
// Take a list of input sections containing symbol table indices and add those
// symbols to an RVA table. The challenge is that symbol RVAs are not known and
// depend on the table size, so we can't directly build a set of integers.
-void Writer::markSymbolsForRVATable(ObjFile *File,
- ArrayRef<SectionChunk *> SymIdxChunks,
- SymbolRVASet &TableSymbols) {
- for (SectionChunk *C : SymIdxChunks) {
+void Writer::markSymbolsForRVATable(ObjFile *file,
+ ArrayRef<SectionChunk *> symIdxChunks,
+ SymbolRVASet &tableSymbols) {
+ for (SectionChunk *c : symIdxChunks) {
// Skip sections discarded by linker GC. This comes up when a .gfids section
// is associated with something like a vtable and the vtable is discarded.
// In this case, the associated gfids section is discarded, and we don't
// mark the virtual member functions as address-taken by the vtable.
- if (!C->Live)
+ if (!c->live)
continue;
// Validate that the contents look like symbol table indices.
- ArrayRef<uint8_t> Data = C->getContents();
- if (Data.size() % 4 != 0) {
- warn("ignoring " + C->getSectionName() +
- " symbol table index section in object " + toString(File));
+ ArrayRef<uint8_t> data = c->getContents();
+ if (data.size() % 4 != 0) {
+ warn("ignoring " + c->getSectionName() +
+ " symbol table index section in object " + toString(file));
continue;
}
// Read each symbol table index and check if that symbol was included in the
// final link. If so, add it to the table symbol set.
- ArrayRef<ulittle32_t> SymIndices(
- reinterpret_cast<const ulittle32_t *>(Data.data()), Data.size() / 4);
- ArrayRef<Symbol *> ObjSymbols = File->getSymbols();
- for (uint32_t SymIndex : SymIndices) {
- if (SymIndex >= ObjSymbols.size()) {
+ ArrayRef<ulittle32_t> symIndices(
+ reinterpret_cast<const ulittle32_t *>(data.data()), data.size() / 4);
+ ArrayRef<Symbol *> objSymbols = file->getSymbols();
+ for (uint32_t symIndex : symIndices) {
+ if (symIndex >= objSymbols.size()) {
warn("ignoring invalid symbol table index in section " +
- C->getSectionName() + " in object " + toString(File));
+ c->getSectionName() + " in object " + toString(file));
continue;
}
- if (Symbol *S = ObjSymbols[SymIndex]) {
- if (S->isLive())
- addSymbolToRVASet(TableSymbols, cast<Defined>(S));
+ if (Symbol *s = objSymbols[symIndex]) {
+ if (s->isLive())
+ addSymbolToRVASet(tableSymbols, cast<Defined>(s));
}
}
}
}
// Replace the absolute table symbol with a synthetic symbol pointing to
-// TableChunk so that we can emit base relocations for it and resolve section
+// tableChunk so that we can emit base relocations for it and resolve section
// relative relocations.
-void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
- StringRef CountSym) {
- if (TableSymbols.empty())
+void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
+ StringRef countSym) {
+ if (tableSymbols.empty())
return;
- RVATableChunk *TableChunk = make<RVATableChunk>(std::move(TableSymbols));
- RdataSec->addChunk(TableChunk);
+ RVATableChunk *tableChunk = make<RVATableChunk>(std::move(tableSymbols));
+ rdataSec->addChunk(tableChunk);
- Symbol *T = Symtab->findUnderscore(TableSym);
- Symbol *C = Symtab->findUnderscore(CountSym);
- replaceSymbol<DefinedSynthetic>(T, T->getName(), TableChunk);
- cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4);
+ Symbol *t = symtab->findUnderscore(tableSym);
+ Symbol *c = symtab->findUnderscore(countSym);
+ replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk);
+ cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / 4);
}
// MinGW specific. Gather all relocations that are imported from a DLL even
@@ -1477,26 +1639,26 @@ void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
// uses for fixing them up, and provide the synthetic symbols that the
// runtime uses for finding the table.
void Writer::createRuntimePseudoRelocs() {
- std::vector<RuntimePseudoReloc> Rels;
+ std::vector<RuntimePseudoReloc> rels;
- for (Chunk *C : Symtab->getChunks()) {
- auto *SC = dyn_cast<SectionChunk>(C);
- if (!SC || !SC->Live)
+ for (Chunk *c : symtab->getChunks()) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc || !sc->live)
continue;
- SC->getRuntimePseudoRelocs(Rels);
+ sc->getRuntimePseudoRelocs(rels);
}
- if (!Rels.empty())
- log("Writing " + Twine(Rels.size()) + " runtime pseudo relocations");
- PseudoRelocTableChunk *Table = make<PseudoRelocTableChunk>(Rels);
- RdataSec->addChunk(Table);
- EmptyChunk *EndOfList = make<EmptyChunk>();
- RdataSec->addChunk(EndOfList);
-
- Symbol *HeadSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
- Symbol *EndSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
- replaceSymbol<DefinedSynthetic>(HeadSym, HeadSym->getName(), Table);
- replaceSymbol<DefinedSynthetic>(EndSym, EndSym->getName(), EndOfList);
+ if (!rels.empty())
+ log("Writing " + Twine(rels.size()) + " runtime pseudo relocations");
+ PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(rels);
+ rdataSec->addChunk(table);
+ EmptyChunk *endOfList = make<EmptyChunk>();
+ rdataSec->addChunk(endOfList);
+
+ Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
+ Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
+ replaceSymbol<DefinedSynthetic>(headSym, headSym->getName(), table);
+ replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList);
}
// MinGW specific.
@@ -1505,32 +1667,32 @@ void Writer::createRuntimePseudoRelocs() {
// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__
// and __DTOR_LIST__ respectively.
void Writer::insertCtorDtorSymbols() {
- AbsolutePointerChunk *CtorListHead = make<AbsolutePointerChunk>(-1);
- AbsolutePointerChunk *CtorListEnd = make<AbsolutePointerChunk>(0);
- AbsolutePointerChunk *DtorListHead = make<AbsolutePointerChunk>(-1);
- AbsolutePointerChunk *DtorListEnd = make<AbsolutePointerChunk>(0);
- CtorsSec->insertChunkAtStart(CtorListHead);
- CtorsSec->addChunk(CtorListEnd);
- DtorsSec->insertChunkAtStart(DtorListHead);
- DtorsSec->addChunk(DtorListEnd);
-
- Symbol *CtorListSym = Symtab->findUnderscore("__CTOR_LIST__");
- Symbol *DtorListSym = Symtab->findUnderscore("__DTOR_LIST__");
- replaceSymbol<DefinedSynthetic>(CtorListSym, CtorListSym->getName(),
- CtorListHead);
- replaceSymbol<DefinedSynthetic>(DtorListSym, DtorListSym->getName(),
- DtorListHead);
+ AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(0);
+ AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(0);
+ ctorsSec->insertChunkAtStart(ctorListHead);
+ ctorsSec->addChunk(ctorListEnd);
+ dtorsSec->insertChunkAtStart(dtorListHead);
+ dtorsSec->addChunk(dtorListEnd);
+
+ Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__");
+ Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__");
+ replaceSymbol<DefinedSynthetic>(ctorListSym, ctorListSym->getName(),
+ ctorListHead);
+ replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(),
+ dtorListHead);
}
// Handles /section options to allow users to overwrite
// section attributes.
void Writer::setSectionPermissions() {
- for (auto &P : Config->Section) {
- StringRef Name = P.first;
- uint32_t Perm = P.second;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Name == Name)
- Sec->setPermissions(Perm);
+ for (auto &p : config->section) {
+ StringRef name = p.first;
+ uint32_t perm = p.second;
+ for (OutputSection *sec : outputSections)
+ if (sec->name == name)
+ sec->setPermissions(perm);
}
}
@@ -1538,18 +1700,19 @@ void Writer::setSectionPermissions() {
void Writer::writeSections() {
// Record the number of sections to apply section index relocations
// against absolute symbols. See applySecIdx in Chunks.cpp..
- DefinedAbsolute::NumOutputSections = OutputSections.size();
+ DefinedAbsolute::numOutputSections = outputSections.size();
- uint8_t *Buf = Buffer->getBufferStart();
- for (OutputSection *Sec : OutputSections) {
- uint8_t *SecBuf = Buf + Sec->getFileOff();
+ uint8_t *buf = buffer->getBufferStart();
+ for (OutputSection *sec : outputSections) {
+ uint8_t *secBuf = buf + sec->getFileOff();
// Fill gaps between functions in .text with INT3 instructions
// instead of leaving as NUL bytes (which can be interpreted as
// ADD instructions).
- if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE)
- memset(SecBuf, 0xCC, Sec->getRawSize());
- for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
- [&](Chunk *C) { C->writeTo(SecBuf); });
+ if (sec->header.Characteristics & IMAGE_SCN_CNT_CODE)
+ memset(secBuf, 0xCC, sec->getRawSize());
+ parallelForEach(sec->chunks, [&](Chunk *c) {
+ c->writeTo(secBuf + c->getRVA() - sec->getRVA());
+ });
}
}
@@ -1560,8 +1723,8 @@ void Writer::writeBuildId() {
// 2) In all cases, the PE COFF file header also contains a timestamp.
// For reproducibility, instead of a timestamp we want to use a hash of the
// PE contents.
- if (Config->Debug) {
- assert(BuildId && "BuildId is not set!");
+ if (config->debug) {
+ assert(buildId && "BuildId is not set!");
// BuildId->BuildId was filled in when the PDB was written.
}
@@ -1569,62 +1732,65 @@ void Writer::writeBuildId() {
// "timestamp" in the COFF file header, and the ones in the coff debug
// directory. Now we can hash the file and write that hash to the various
// timestamp fields in the file.
- StringRef OutputFileData(
- reinterpret_cast<const char *>(Buffer->getBufferStart()),
- Buffer->getBufferSize());
+ StringRef outputFileData(
+ reinterpret_cast<const char *>(buffer->getBufferStart()),
+ buffer->getBufferSize());
- uint32_t Timestamp = Config->Timestamp;
- uint64_t Hash = 0;
- bool GenerateSyntheticBuildId =
- Config->MinGW && Config->Debug && Config->PDBPath.empty();
+ uint32_t timestamp = config->timestamp;
+ uint64_t hash = 0;
+ bool generateSyntheticBuildId =
+ config->mingw && config->debug && config->pdbPath.empty();
- if (Config->Repro || GenerateSyntheticBuildId)
- Hash = xxHash64(OutputFileData);
+ if (config->repro || generateSyntheticBuildId)
+ hash = xxHash64(outputFileData);
- if (Config->Repro)
- Timestamp = static_cast<uint32_t>(Hash);
+ if (config->repro)
+ timestamp = static_cast<uint32_t>(hash);
- if (GenerateSyntheticBuildId) {
+ if (generateSyntheticBuildId) {
// For MinGW builds without a PDB file, we still generate a build id
// to allow associating a crash dump to the executable.
- BuildId->BuildId->PDB70.CVSignature = OMF::Signature::PDB70;
- BuildId->BuildId->PDB70.Age = 1;
- memcpy(BuildId->BuildId->PDB70.Signature, &Hash, 8);
+ buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70;
+ buildId->buildId->PDB70.Age = 1;
+ memcpy(buildId->buildId->PDB70.Signature, &hash, 8);
// xxhash only gives us 8 bytes, so put some fixed data in the other half.
- memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8);
+ memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8);
}
- if (DebugDirectory)
- DebugDirectory->setTimeDateStamp(Timestamp);
+ if (debugDirectory)
+ debugDirectory->setTimeDateStamp(timestamp);
- uint8_t *Buf = Buffer->getBufferStart();
- Buf += DOSStubSize + sizeof(PEMagic);
- object::coff_file_header *CoffHeader =
- reinterpret_cast<coff_file_header *>(Buf);
- CoffHeader->TimeDateStamp = Timestamp;
+ uint8_t *buf = buffer->getBufferStart();
+ buf += dosStubSize + sizeof(PEMagic);
+ object::coff_file_header *coffHeader =
+ reinterpret_cast<coff_file_header *>(buf);
+ coffHeader->TimeDateStamp = timestamp;
}
// Sort .pdata section contents according to PE/COFF spec 5.5.
void Writer::sortExceptionTable() {
- if (!FirstPdata)
+ if (!firstPdata)
return;
// We assume .pdata contains function table entries only.
- auto BufAddr = [&](Chunk *C) {
- return Buffer->getBufferStart() + C->getOutputSection()->getFileOff() +
- C->getRVA() - C->getOutputSection()->getRVA();
+ auto bufAddr = [&](Chunk *c) {
+ OutputSection *os = c->getOutputSection();
+ return buffer->getBufferStart() + os->getFileOff() + c->getRVA() -
+ os->getRVA();
};
- uint8_t *Begin = BufAddr(FirstPdata);
- uint8_t *End = BufAddr(LastPdata) + LastPdata->getSize();
- if (Config->Machine == AMD64) {
- struct Entry { ulittle32_t Begin, End, Unwind; };
- sort(parallel::par, (Entry *)Begin, (Entry *)End,
- [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
+ uint8_t *begin = bufAddr(firstPdata);
+ uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
+ if (config->machine == AMD64) {
+ struct Entry { ulittle32_t begin, end, unwind; };
+ parallelSort(
+ MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
+ [](const Entry &a, const Entry &b) { return a.begin < b.begin; });
return;
}
- if (Config->Machine == ARMNT || Config->Machine == ARM64) {
- struct Entry { ulittle32_t Begin, Unwind; };
- sort(parallel::par, (Entry *)Begin, (Entry *)End,
- [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
+ if (config->machine == ARMNT || config->machine == ARM64) {
+ struct Entry { ulittle32_t begin, unwind; };
+ parallelSort(
+ MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
+ [](const Entry &a, const Entry &b) { return a.begin < b.begin; });
return;
}
errs() << "warning: don't know how to handle .pdata.\n";
@@ -1644,76 +1810,92 @@ void Writer::sortExceptionTable() {
// pointers in the order that they are listed in the object file (top to
// bottom), otherwise global objects might not be initialized in the
// correct order.
-void Writer::sortCRTSectionChunks(std::vector<Chunk *> &Chunks) {
- auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) {
- auto SA = dyn_cast<SectionChunk>(A);
- auto SB = dyn_cast<SectionChunk>(B);
- assert(SA && SB && "Non-section chunks in CRT section!");
+void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {
+ auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) {
+ auto sa = dyn_cast<SectionChunk>(a);
+ auto sb = dyn_cast<SectionChunk>(b);
+ assert(sa && sb && "Non-section chunks in CRT section!");
- StringRef SAObj = SA->File->MB.getBufferIdentifier();
- StringRef SBObj = SB->File->MB.getBufferIdentifier();
+ StringRef sAObj = sa->file->mb.getBufferIdentifier();
+ StringRef sBObj = sb->file->mb.getBufferIdentifier();
- return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber();
+ return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber();
};
- std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder);
+ llvm::stable_sort(chunks, sectionChunkOrder);
- if (Config->Verbose) {
- for (auto &C : Chunks) {
- auto SC = dyn_cast<SectionChunk>(C);
- log(" " + SC->File->MB.getBufferIdentifier().str() +
- ", SectionID: " + Twine(SC->getSectionNumber()));
+ if (config->verbose) {
+ for (auto &c : chunks) {
+ auto sc = dyn_cast<SectionChunk>(c);
+ log(" " + sc->file->mb.getBufferIdentifier().str() +
+ ", SectionID: " + Twine(sc->getSectionNumber()));
}
}
}
-OutputSection *Writer::findSection(StringRef Name) {
- for (OutputSection *Sec : OutputSections)
- if (Sec->Name == Name)
- return Sec;
+OutputSection *Writer::findSection(StringRef name) {
+ for (OutputSection *sec : outputSections)
+ if (sec->name == name)
+ return sec;
return nullptr;
}
uint32_t Writer::getSizeOfInitializedData() {
- uint32_t Res = 0;
- for (OutputSection *S : OutputSections)
- if (S->Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
- Res += S->getRawSize();
- return Res;
+ uint32_t res = 0;
+ for (OutputSection *s : outputSections)
+ if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
+ res += s->getRawSize();
+ return res;
}
// Add base relocations to .reloc section.
void Writer::addBaserels() {
- if (!Config->Relocatable)
+ if (!config->relocatable)
return;
- RelocSec->Chunks.clear();
- std::vector<Baserel> V;
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ relocSec->chunks.clear();
+ std::vector<Baserel> v;
+ for (OutputSection *sec : outputSections) {
+ if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
continue;
// Collect all locations for base relocations.
- for (Chunk *C : Sec->Chunks)
- C->getBaserels(&V);
+ for (Chunk *c : sec->chunks)
+ c->getBaserels(&v);
// Add the addresses to .reloc section.
- if (!V.empty())
- addBaserelBlocks(V);
- V.clear();
+ if (!v.empty())
+ addBaserelBlocks(v);
+ v.clear();
}
}
// Add addresses to .reloc section. Note that addresses are grouped by page.
-void Writer::addBaserelBlocks(std::vector<Baserel> &V) {
- const uint32_t Mask = ~uint32_t(PageSize - 1);
- uint32_t Page = V[0].RVA & Mask;
- size_t I = 0, J = 1;
- for (size_t E = V.size(); J < E; ++J) {
- uint32_t P = V[J].RVA & Mask;
- if (P == Page)
+void Writer::addBaserelBlocks(std::vector<Baserel> &v) {
+ const uint32_t mask = ~uint32_t(pageSize - 1);
+ uint32_t page = v[0].rva & mask;
+ size_t i = 0, j = 1;
+ for (size_t e = v.size(); j < e; ++j) {
+ uint32_t p = v[j].rva & mask;
+ if (p == page)
continue;
- RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
- I = J;
- Page = P;
+ relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j));
+ i = j;
+ page = p;
}
- if (I == J)
+ if (i == j)
return;
- RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
+ relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j));
+}
+
+PartialSection *Writer::createPartialSection(StringRef name,
+ uint32_t outChars) {
+ PartialSection *&pSec = partialSections[{name, outChars}];
+ if (pSec)
+ return pSec;
+ pSec = make<PartialSection>(name, outChars);
+ return pSec;
+}
+
+PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
+ auto it = partialSections.find({name, outChars});
+ if (it != partialSections.end())
+ return it->second;
+ return nullptr;
}
diff --git a/COFF/Writer.h b/COFF/Writer.h
index 727582480c91..96389df2ac0a 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -1,9 +1,8 @@
//===- Writer.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -19,10 +18,19 @@
namespace lld {
namespace coff {
-static const int PageSize = 4096;
+static const int pageSize = 4096;
void writeResult();
+class PartialSection {
+public:
+ PartialSection(StringRef n, uint32_t chars)
+ : name(n), characteristics(chars) {}
+ StringRef name;
+ unsigned characteristics;
+ std::vector<Chunk *> chunks;
+};
+
// OutputSection represents a section in an output file. It's a
// container of chunks. OutputSection and Chunk are 1:N relationship.
// Chunks cannot belong to more than one OutputSections. The writer
@@ -30,46 +38,48 @@ void writeResult();
// non-overlapping file offsets and RVAs.
class OutputSection {
public:
- OutputSection(llvm::StringRef N, uint32_t Chars) : Name(N) {
- Header.Characteristics = Chars;
+ OutputSection(llvm::StringRef n, uint32_t chars) : name(n) {
+ header.Characteristics = chars;
}
- void addChunk(Chunk *C);
- void insertChunkAtStart(Chunk *C);
- void merge(OutputSection *Other);
- void addPermissions(uint32_t C);
- void setPermissions(uint32_t C);
- uint64_t getRVA() { return Header.VirtualAddress; }
- uint64_t getFileOff() { return Header.PointerToRawData; }
- void writeHeaderTo(uint8_t *Buf);
+ void addChunk(Chunk *c);
+ void insertChunkAtStart(Chunk *c);
+ void merge(OutputSection *other);
+ void setPermissions(uint32_t c);
+ uint64_t getRVA() { return header.VirtualAddress; }
+ uint64_t getFileOff() { return header.PointerToRawData; }
+ void writeHeaderTo(uint8_t *buf);
+ void addContributingPartialSection(PartialSection *sec);
// Returns the size of this section in an executable memory image.
// This may be smaller than the raw size (the raw size is multiple
// of disk sector size, so there may be padding at end), or may be
// larger (if that's the case, the loader reserves spaces after end
// of raw data).
- uint64_t getVirtualSize() { return Header.VirtualSize; }
+ uint64_t getVirtualSize() { return header.VirtualSize; }
// Returns the size of the section in the output file.
- uint64_t getRawSize() { return Header.SizeOfRawData; }
+ uint64_t getRawSize() { return header.SizeOfRawData; }
// Set offset into the string table storing this section name.
// Used only when the name is longer than 8 bytes.
- void setStringTableOff(uint32_t V) { StringTableOff = V; }
+ void setStringTableOff(uint32_t v) { stringTableOff = v; }
// N.B. The section index is one based.
- uint32_t SectionIndex = 0;
+ uint32_t sectionIndex = 0;
+
+ llvm::StringRef name;
+ llvm::object::coff_section header = {};
- llvm::StringRef Name;
- llvm::object::coff_section Header = {};
+ std::vector<Chunk *> chunks;
+ std::vector<Chunk *> origChunks;
- std::vector<Chunk *> Chunks;
- std::vector<Chunk *> OrigChunks;
+ std::vector<PartialSection *> contribSections;
private:
- uint32_t StringTableOff = 0;
+ uint32_t stringTableOff = 0;
};
-}
-}
+} // namespace coff
+} // namespace lld
#endif
diff --git a/Common/Args.cpp b/Common/Args.cpp
index 3f0671d72a66..4ea3a435c7ae 100644
--- a/Common/Args.cpp
+++ b/Common/Args.cpp
@@ -1,9 +1,8 @@
//===- Args.cpp -----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -18,56 +17,66 @@
using namespace llvm;
using namespace lld;
-int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
- auto *A = Args.getLastArg(Key);
- if (!A)
+// TODO(sbc): Remove this once CGOptLevel can be set completely based on bitcode
+// function metadata.
+CodeGenOpt::Level lld::args::getCGOptLevel(int optLevelLTO) {
+ if (optLevelLTO == 3)
+ return CodeGenOpt::Aggressive;
+ assert(optLevelLTO < 3);
+ return CodeGenOpt::Default;
+}
+
+int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key,
+ int64_t Default) {
+ auto *a = args.getLastArg(key);
+ if (!a)
return Default;
- int V;
- if (to_integer(A->getValue(), V, 10))
- return V;
+ int64_t v;
+ if (to_integer(a->getValue(), v, 10))
+ return v;
- StringRef Spelling = Args.getArgString(A->getIndex());
- error(Spelling + ": number expected, but got '" + A->getValue() + "'");
+ StringRef spelling = args.getArgString(a->getIndex());
+ error(spelling + ": number expected, but got '" + a->getValue() + "'");
return 0;
}
-std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) {
- std::vector<StringRef> V;
- for (auto *Arg : Args.filtered(Id))
- V.push_back(Arg->getValue());
- return V;
+std::vector<StringRef> lld::args::getStrings(opt::InputArgList &args, int id) {
+ std::vector<StringRef> v;
+ for (auto *arg : args.filtered(id))
+ v.push_back(arg->getValue());
+ return v;
}
-uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id,
- StringRef Key, uint64_t Default) {
- for (auto *Arg : Args.filtered_reverse(Id)) {
- std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
- if (KV.first == Key) {
- uint64_t Result = Default;
- if (!to_integer(KV.second, Result))
- error("invalid " + Key + ": " + KV.second);
- return Result;
+uint64_t lld::args::getZOptionValue(opt::InputArgList &args, int id,
+ StringRef key, uint64_t Default) {
+ for (auto *arg : args.filtered_reverse(id)) {
+ std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+ if (kv.first == key) {
+ uint64_t result = Default;
+ if (!to_integer(kv.second, result))
+ error("invalid " + key + ": " + kv.second);
+ return result;
}
}
return Default;
}
-std::vector<StringRef> lld::args::getLines(MemoryBufferRef MB) {
- SmallVector<StringRef, 0> Arr;
- MB.getBuffer().split(Arr, '\n');
+std::vector<StringRef> lld::args::getLines(MemoryBufferRef mb) {
+ SmallVector<StringRef, 0> arr;
+ mb.getBuffer().split(arr, '\n');
- std::vector<StringRef> Ret;
- for (StringRef S : Arr) {
- S = S.trim();
- if (!S.empty() && S[0] != '#')
- Ret.push_back(S);
+ std::vector<StringRef> ret;
+ for (StringRef s : arr) {
+ s = s.trim();
+ if (!s.empty() && s[0] != '#')
+ ret.push_back(s);
}
- return Ret;
+ return ret;
}
-StringRef lld::args::getFilenameWithoutExe(StringRef Path) {
- if (Path.endswith_lower(".exe"))
- return sys::path::stem(Path);
- return sys::path::filename(Path);
+StringRef lld::args::getFilenameWithoutExe(StringRef path) {
+ if (path.endswith_lower(".exe"))
+ return sys::path::stem(path);
+ return sys::path::filename(path);
}
diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt
index a45fe209f06f..70849cc7b94b 100644
--- a/Common/CMakeLists.txt
+++ b/Common/CMakeLists.txt
@@ -2,15 +2,42 @@ if(NOT LLD_BUILT_STANDALONE)
set(tablegen_deps intrinsics_gen)
endif()
+find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc)
+find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc)
+
+set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc")
+set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake")
+
+if(lld_vc)
+ set(lld_source_dir ${LLD_SOURCE_DIR})
+endif()
+
+add_custom_command(OUTPUT "${version_inc}"
+ DEPENDS "${lld_vc}" "${generate_vcs_version_script}"
+ COMMAND ${CMAKE_COMMAND} "-DNAMES=LLD"
+ "-DLLD_SOURCE_DIR=${LLD_SOURCE_DIR}"
+ "-DHEADER_FILE=${version_inc}"
+ -P "${generate_vcs_version_script}")
+
+# Mark the generated header as being generated.
+set_source_files_properties("${version_inc}"
+ PROPERTIES GENERATED TRUE
+ HEADER_FILE_ONLY TRUE)
+
+set_property(SOURCE Version.cpp APPEND PROPERTY
+ COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC")
+
add_lld_library(lldCommon
Args.cpp
ErrorHandler.cpp
+ Filesystem.cpp
Memory.cpp
Reproduce.cpp
Strings.cpp
TargetOptionsCommandFlags.cpp
Threads.cpp
Timer.cpp
+ VCSVersion.inc
Version.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/Common/ErrorHandler.cpp b/Common/ErrorHandler.cpp
index c059516daf94..c87c0609b260 100644
--- a/Common/ErrorHandler.cpp
+++ b/Common/ErrorHandler.cpp
@@ -1,9 +1,8 @@
//===- ErrorHandler.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -17,6 +16,7 @@
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include <mutex>
+#include <regex>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
@@ -27,29 +27,29 @@ using namespace lld;
// The functions defined in this file can be called from multiple threads,
// but outs() or errs() are not thread-safe. We protect them using a mutex.
-static std::mutex Mu;
+static std::mutex mu;
// Prints "\n" or does nothing, depending on Msg contents of
// the previous call of this function.
-static void newline(raw_ostream *ErrorOS, const Twine &Msg) {
+static void newline(raw_ostream *errorOS, const Twine &msg) {
// True if the previous error message contained "\n".
// We want to separate multi-line error messages with a newline.
- static bool Flag;
+ static bool flag;
- if (Flag)
- *ErrorOS << "\n";
- Flag = StringRef(Msg.str()).contains('\n');
+ if (flag)
+ *errorOS << "\n";
+ flag = StringRef(msg.str()).contains('\n');
}
ErrorHandler &lld::errorHandler() {
- static ErrorHandler Handler;
- return Handler;
+ static ErrorHandler handler;
+ return handler;
}
-void lld::exitLld(int Val) {
+void lld::exitLld(int val) {
// Delete any temporary file, while keeping the memory mapping open.
- if (errorHandler().OutputBuffer)
- errorHandler().OutputBuffer->discard();
+ if (errorHandler().outputBuffer)
+ errorHandler().outputBuffer->discard();
// Dealloc/destroy ManagedStatic variables before calling
// _exit(). In a non-LTO build, this is a nop. In an LTO
@@ -58,87 +58,121 @@ void lld::exitLld(int Val) {
outs().flush();
errs().flush();
- _exit(Val);
+ _exit(val);
}
-void lld::diagnosticHandler(const DiagnosticInfo &DI) {
- SmallString<128> S;
- raw_svector_ostream OS(S);
- DiagnosticPrinterRawOStream DP(OS);
- DI.print(DP);
- switch (DI.getSeverity()) {
+void lld::diagnosticHandler(const DiagnosticInfo &di) {
+ SmallString<128> s;
+ raw_svector_ostream os(s);
+ DiagnosticPrinterRawOStream dp(os);
+ di.print(dp);
+ switch (di.getSeverity()) {
case DS_Error:
- error(S);
+ error(s);
break;
case DS_Warning:
- warn(S);
+ warn(s);
break;
case DS_Remark:
case DS_Note:
- message(S);
+ message(s);
break;
}
}
-void lld::checkError(Error E) {
- handleAllErrors(std::move(E),
- [&](ErrorInfoBase &EIB) { error(EIB.message()); });
+void lld::checkError(Error e) {
+ handleAllErrors(std::move(e),
+ [&](ErrorInfoBase &eib) { error(eib.message()); });
+}
+
+static std::string getLocation(std::string msg, std::string defaultMsg) {
+ static std::vector<std::regex> Regexes{
+ std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"),
+ std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),
+ std::regex(
+ R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
+ std::regex(
+ R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"),
+ std::regex(
+ R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
+ std::regex(
+ R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"),
+ std::regex(R"((\S+):(\d+): unclosed quote)"),
+ };
+
+ std::smatch Match;
+ for (std::regex &Re : Regexes) {
+ if (std::regex_search(msg, Match, Re)) {
+ return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")"
+ : Match.str(1);
+ }
+ }
+ return defaultMsg;
}
-void ErrorHandler::print(StringRef S, raw_ostream::Colors C) {
- *ErrorOS << LogName << ": ";
- if (ColorDiagnostics) {
- ErrorOS->changeColor(C, true);
- *ErrorOS << S;
- ErrorOS->resetColor();
+void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c,
+ const Twine &msg) {
+
+ if (vsDiagnostics) {
+ // A Visual Studio-style error message starts with an error location.
+ // If a location cannot be extracted then we default to LogName.
+ *errorOS << getLocation(msg.str(), logName) << ": ";
+ } else {
+ *errorOS << logName << ": ";
+ }
+
+ if (colorDiagnostics) {
+ errorOS->changeColor(c, true);
+ *errorOS << s;
+ errorOS->resetColor();
} else {
- *ErrorOS << S;
+ *errorOS << s;
}
}
-void ErrorHandler::log(const Twine &Msg) {
- if (Verbose) {
- std::lock_guard<std::mutex> Lock(Mu);
- *ErrorOS << LogName << ": " << Msg << "\n";
+void ErrorHandler::log(const Twine &msg) {
+ if (verbose) {
+ std::lock_guard<std::mutex> lock(mu);
+ *errorOS << logName << ": " << msg << "\n";
}
}
-void ErrorHandler::message(const Twine &Msg) {
- std::lock_guard<std::mutex> Lock(Mu);
- outs() << Msg << "\n";
+void ErrorHandler::message(const Twine &msg) {
+ std::lock_guard<std::mutex> lock(mu);
+ outs() << msg << "\n";
outs().flush();
}
-void ErrorHandler::warn(const Twine &Msg) {
- if (FatalWarnings) {
- error(Msg);
+void ErrorHandler::warn(const Twine &msg) {
+ if (fatalWarnings) {
+ error(msg);
return;
}
- std::lock_guard<std::mutex> Lock(Mu);
- newline(ErrorOS, Msg);
- print("warning: ", raw_ostream::MAGENTA);
- *ErrorOS << Msg << "\n";
+ std::lock_guard<std::mutex> lock(mu);
+ newline(errorOS, msg);
+ printHeader("warning: ", raw_ostream::MAGENTA, msg);
+ *errorOS << msg << "\n";
}
-void ErrorHandler::error(const Twine &Msg) {
- std::lock_guard<std::mutex> Lock(Mu);
- newline(ErrorOS, Msg);
-
- if (ErrorLimit == 0 || ErrorCount < ErrorLimit) {
- print("error: ", raw_ostream::RED);
- *ErrorOS << Msg << "\n";
- } else if (ErrorCount == ErrorLimit) {
- print("error: ", raw_ostream::RED);
- *ErrorOS << ErrorLimitExceededMsg << "\n";
- if (ExitEarly)
+void ErrorHandler::error(const Twine &msg) {
+ std::lock_guard<std::mutex> lock(mu);
+ newline(errorOS, msg);
+
+ if (errorLimit == 0 || errorCount < errorLimit) {
+ printHeader("error: ", raw_ostream::RED, msg);
+ *errorOS << msg << "\n";
+ } else if (errorCount == errorLimit) {
+ printHeader("error: ", raw_ostream::RED, msg);
+ *errorOS << errorLimitExceededMsg << "\n";
+ if (exitEarly)
exitLld(1);
}
- ++ErrorCount;
+ ++errorCount;
}
-void ErrorHandler::fatal(const Twine &Msg) {
- error(Msg);
+void ErrorHandler::fatal(const Twine &msg) {
+ error(msg);
exitLld(1);
}
diff --git a/ELF/Filesystem.cpp b/Common/Filesystem.cpp
index 5cf240eeca56..c3d3f998f03b 100644
--- a/ELF/Filesystem.cpp
+++ b/Common/Filesystem.cpp
@@ -1,9 +1,8 @@
//===- Filesystem.cpp -----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -11,8 +10,7 @@
//
//===----------------------------------------------------------------------===//
-#include "Filesystem.h"
-#include "Config.h"
+#include "lld/Common/Filesystem.h"
#include "lld/Common/Threads.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/FileOutputBuffer.h"
@@ -23,9 +21,7 @@
#include <thread>
using namespace llvm;
-
using namespace lld;
-using namespace lld::elf;
// Removes a given file asynchronously. This is a performance hack,
// so remove this when operating systems are improved.
@@ -42,26 +38,43 @@ using namespace lld::elf;
//
// This function spawns a background thread to remove the file.
// The calling thread returns almost immediately.
-void elf::unlinkAsync(StringRef Path) {
+void lld::unlinkAsync(StringRef path) {
// Removing a file is async on windows.
#if defined(_WIN32)
- sys::fs::remove(Path);
+ sys::fs::remove(path);
#else
- if (!ThreadsEnabled || !sys::fs::exists(Path) ||
- !sys::fs::is_regular_file(Path))
+ if (!threadsEnabled || !sys::fs::exists(path) ||
+ !sys::fs::is_regular_file(path))
return;
// We cannot just remove path from a different thread because we are now going
// to create path as a new file.
// Instead we open the file and unlink it on this thread. The unlink is fast
// since the open fd guarantees that it is not removing the last reference.
- int FD;
- std::error_code EC = sys::fs::openFileForRead(Path, FD);
- sys::fs::remove(Path);
+ int fd;
+ std::error_code ec = sys::fs::openFileForRead(path, fd);
+ sys::fs::remove(path);
+
+ if (ec)
+ return;
// close and therefore remove TempPath in background.
- if (!EC)
- std::thread([=] { ::close(FD); }).detach();
+ std::mutex m;
+ std::condition_variable cv;
+ bool started = false;
+ std::thread([&, fd] {
+ {
+ std::lock_guard<std::mutex> l(m);
+ started = true;
+ cv.notify_all();
+ }
+ ::close(fd);
+ }).detach();
+
+ // GLIBC 2.26 and earlier have race condition that crashes an entire process
+ // if the main thread calls exit(2) while other thread is starting up.
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l, [&] { return started; });
#endif
}
@@ -77,10 +90,10 @@ void elf::unlinkAsync(StringRef Path) {
// FileOutputBuffer doesn't touch a desitnation file until commit()
// is called. We use that class without calling commit() to predict
// if the given file is writable.
-std::error_code elf::tryCreateFile(StringRef Path) {
- if (Path.empty())
+std::error_code lld::tryCreateFile(StringRef path) {
+ if (path.empty())
return std::error_code();
- if (Path == "-")
+ if (path == "-")
return std::error_code();
- return errorToErrorCode(FileOutputBuffer::create(Path, 1).takeError());
+ return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError());
}
diff --git a/Common/Memory.cpp b/Common/Memory.cpp
index efc5bcc2218b..c53e1d3e6cfc 100644
--- a/Common/Memory.cpp
+++ b/Common/Memory.cpp
@@ -1,9 +1,8 @@
//===- Memory.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -12,12 +11,12 @@
using namespace llvm;
using namespace lld;
-BumpPtrAllocator lld::BAlloc;
-StringSaver lld::Saver{BAlloc};
-std::vector<SpecificAllocBase *> lld::SpecificAllocBase::Instances;
+BumpPtrAllocator lld::bAlloc;
+StringSaver lld::saver{bAlloc};
+std::vector<SpecificAllocBase *> lld::SpecificAllocBase::instances;
void lld::freeArena() {
- for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
- Alloc->reset();
- BAlloc.Reset();
+ for (SpecificAllocBase *alloc : SpecificAllocBase::instances)
+ alloc->reset();
+ bAlloc.Reset();
}
diff --git a/Common/Reproduce.cpp b/Common/Reproduce.cpp
index 7be4ea6bb98b..24210c420418 100644
--- a/Common/Reproduce.cpp
+++ b/Common/Reproduce.cpp
@@ -1,9 +1,8 @@
//===- Reproduce.cpp - Utilities for creating reproducers -----------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -22,45 +21,41 @@ using namespace llvm::sys;
// assuming that the current directory is "/home/john/bar".
// Returned string is a forward slash separated path even on Windows to avoid
// a mess with backslash-as-escape and backslash-as-path-separator.
-std::string lld::relativeToRoot(StringRef Path) {
- SmallString<128> Abs = Path;
- if (fs::make_absolute(Abs))
- return Path;
- path::remove_dots(Abs, /*remove_dot_dot=*/true);
+std::string lld::relativeToRoot(StringRef path) {
+ SmallString<128> abs = path;
+ if (fs::make_absolute(abs))
+ return path;
+ path::remove_dots(abs, /*remove_dot_dot=*/true);
// This is Windows specific. root_name() returns a drive letter
// (e.g. "c:") or a UNC name (//net). We want to keep it as part
// of the result.
- SmallString<128> Res;
- StringRef Root = path::root_name(Abs);
- if (Root.endswith(":"))
- Res = Root.drop_back();
- else if (Root.startswith("//"))
- Res = Root.substr(2);
-
- path::append(Res, path::relative_path(Abs));
- return path::convert_to_slash(Res);
+ SmallString<128> res;
+ StringRef root = path::root_name(abs);
+ if (root.endswith(":"))
+ res = root.drop_back();
+ else if (root.startswith("//"))
+ res = root.substr(2);
+
+ path::append(res, path::relative_path(abs));
+ return path::convert_to_slash(res);
}
// Quote a given string if it contains a space character.
-std::string lld::quote(StringRef S) {
- if (S.contains(' '))
- return ("\"" + S + "\"").str();
- return S;
-}
-
-std::string lld::rewritePath(StringRef S) {
- if (fs::exists(S))
- return relativeToRoot(S);
- return S;
+std::string lld::quote(StringRef s) {
+ if (s.contains(' '))
+ return ("\"" + s + "\"").str();
+ return s;
}
-std::string lld::toString(const opt::Arg &Arg) {
- std::string K = Arg.getSpelling();
- if (Arg.getNumValues() == 0)
- return K;
- std::string V = quote(Arg.getValue());
- if (Arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
- return K + V;
- return K + " " + V;
+// 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();
+ if (arg.getNumValues() == 0)
+ return k;
+ std::string v = quote(arg.getValue());
+ if (arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
+ return k + v;
+ return k + " " + v;
}
diff --git a/Common/Strings.cpp b/Common/Strings.cpp
index 6f74865b7f42..0bf06626cc7a 100644
--- a/Common/Strings.cpp
+++ b/Common/Strings.cpp
@@ -1,9 +1,8 @@
//===- Strings.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -20,85 +19,85 @@ using namespace llvm;
using namespace lld;
// Returns the demangled C++ symbol name for Name.
-Optional<std::string> lld::demangleItanium(StringRef Name) {
+Optional<std::string> lld::demangleItanium(StringRef name) {
// itaniumDemangle can be used to demangle strings other than symbol
// names which do not necessarily start with "_Z". Name can be
// either a C or C++ symbol. Don't call itaniumDemangle if the 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"))
+ if (!name.startswith("_Z"))
return None;
- char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
- if (!Buf)
+ char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
+ if (!buf)
return None;
- std::string S(Buf);
- free(Buf);
- return S;
+ std::string s(buf);
+ free(buf);
+ return s;
}
-Optional<std::string> lld::demangleMSVC(StringRef Name) {
- std::string Prefix;
- if (Name.consume_front("__imp_"))
- Prefix = "__declspec(dllimport) ";
+Optional<std::string> lld::demangleMSVC(StringRef name) {
+ std::string prefix;
+ if (name.consume_front("__imp_"))
+ prefix = "__declspec(dllimport) ";
// Demangle only C++ names.
- if (!Name.startswith("?"))
+ if (!name.startswith("?"))
return None;
- char *Buf = microsoftDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
- if (!Buf)
+ char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
+ if (!buf)
return None;
- std::string S(Buf);
- free(Buf);
- return Prefix + S;
+ std::string s(buf);
+ free(buf);
+ return prefix + s;
}
-StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) {
- for (StringRef S : Pat) {
- Expected<GlobPattern> Pat = GlobPattern::create(S);
- if (!Pat)
- error(toString(Pat.takeError()));
+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);
+ patterns.push_back(*pat);
}
}
-bool StringMatcher::match(StringRef S) const {
- for (const GlobPattern &Pat : Patterns)
- if (Pat.match(S))
+bool StringMatcher::match(StringRef s) const {
+ for (const GlobPattern &pat : patterns)
+ if (pat.match(s))
return true;
return false;
}
// Converts a hex string (e.g. "deadbeef") to a vector.
-std::vector<uint8_t> lld::parseHex(StringRef S) {
- std::vector<uint8_t> Hex;
- while (!S.empty()) {
- StringRef B = S.substr(0, 2);
- S = S.substr(2);
- uint8_t H;
- if (!to_integer(B, H, 16)) {
- error("not a hexadecimal value: " + B);
+std::vector<uint8_t> lld::parseHex(StringRef s) {
+ std::vector<uint8_t> hex;
+ while (!s.empty()) {
+ StringRef b = s.substr(0, 2);
+ s = s.substr(2);
+ uint8_t h;
+ if (!to_integer(b, h, 16)) {
+ error("not a hexadecimal value: " + b);
return {};
}
- Hex.push_back(H);
+ hex.push_back(h);
}
- return Hex;
+ return hex;
}
// Returns true if S is valid as a C language identifier.
-bool lld::isValidCIdentifier(StringRef S) {
- return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
- std::all_of(S.begin() + 1, S.end(),
- [](char C) { return C == '_' || isAlnum(C); });
+bool lld::isValidCIdentifier(StringRef s) {
+ return !s.empty() && (isAlpha(s[0]) || s[0] == '_') &&
+ std::all_of(s.begin() + 1, s.end(),
+ [](char c) { return c == '_' || isAlnum(c); });
}
// Write the contents of the a buffer to a file
-void lld::saveBuffer(StringRef Buffer, const Twine &Path) {
- std::error_code EC;
- raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
- if (EC)
- error("cannot create " + Path + ": " + EC.message());
- OS << Buffer;
+void lld::saveBuffer(StringRef buffer, const Twine &path) {
+ std::error_code ec;
+ raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None);
+ if (ec)
+ error("cannot create " + path + ": " + ec.message());
+ os << buffer;
}
diff --git a/Common/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp
index 7a3fc510704f..d4c29d7f88b8 100644
--- a/Common/TargetOptionsCommandFlags.cpp
+++ b/Common/TargetOptionsCommandFlags.cpp
@@ -1,9 +1,8 @@
//===-- TargetOptionsCommandFlags.cpp ---------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -20,16 +19,17 @@
#include "llvm/Target/TargetOptions.h"
// Define an externally visible version of
-// InitTargetOptionsFromCodeGenFlags, so that its functionality can be
+// 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.
-llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() {
+llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
return ::InitTargetOptionsFromCodeGenFlags();
}
-llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() {
+llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
return getCodeModel();
}
-std::string lld::GetCPUStr() { return ::getCPUStr(); }
-std::vector<std::string> lld::GetMAttrs() { return ::MAttrs; }
+std::string lld::getCPUStr() { return ::getCPUStr(); }
+
+std::vector<std::string> lld::getMAttrs() { return ::MAttrs; }
diff --git a/Common/Threads.cpp b/Common/Threads.cpp
index c64b8c38b909..af04972a3760 100644
--- a/Common/Threads.cpp
+++ b/Common/Threads.cpp
@@ -1,12 +1,11 @@
//===- Threads.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "lld/Common/Threads.h"
-bool lld::ThreadsEnabled = true;
+bool lld::threadsEnabled = true;
diff --git a/Common/Timer.cpp b/Common/Timer.cpp
index 89f9829b47cf..4b7d11003b2c 100644
--- a/Common/Timer.cpp
+++ b/Common/Timer.cpp
@@ -1,9 +1,8 @@
//===- Timer.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -14,43 +13,43 @@
using namespace lld;
using namespace llvm;
-ScopedTimer::ScopedTimer(Timer &T) : T(&T) { T.start(); }
+ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); }
void ScopedTimer::stop() {
- if (!T)
+ if (!t)
return;
- T->stop();
- T = nullptr;
+ t->stop();
+ 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) {}
+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();
+ 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);
+ total += (std::chrono::high_resolution_clock::now() - startTime);
}
Timer &Timer::root() {
- static Timer RootTimer("Total Link Time");
- return RootTimer;
+ static Timer rootTimer("Total Link Time");
+ return rootTimer;
}
void Timer::print() {
- double TotalDuration = static_cast<double>(root().millis());
+ double totalDuration = static_cast<double>(root().millis());
// 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);
+ for (const auto &child : children)
+ child->print(1, totalDuration);
message(std::string(49, '-'));
@@ -59,22 +58,22 @@ void Timer::print() {
double Timer::millis() const {
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
- Total)
+ total)
.count();
}
-void Timer::print(int Depth, double TotalDuration, bool Recurse) const {
- double P = 100.0 * millis() / TotalDuration;
+void Timer::print(int depth, double totalDuration, bool recurse) const {
+ double p = 100.0 * millis() / totalDuration;
- SmallString<32> Str;
- llvm::raw_svector_ostream Stream(Str);
- std::string S = std::string(Depth * 2, ' ') + Name + std::string(":");
- Stream << format("%-30s%5d ms (%5.1f%%)", S.c_str(), (int)millis(), P);
+ SmallString<32> str;
+ llvm::raw_svector_ostream stream(str);
+ std::string s = std::string(depth * 2, ' ') + name + std::string(":");
+ stream << format("%-30s%5d ms (%5.1f%%)", s.c_str(), (int)millis(), p);
- message(Str);
+ message(str);
- if (Recurse) {
- for (const auto &Child : Children)
- Child->print(Depth + 1, TotalDuration);
+ if (recurse) {
+ for (const auto &child : children)
+ child->print(depth + 1, totalDuration);
}
}
diff --git a/Common/Version.cpp b/Common/Version.cpp
index 6226c9a2fac6..ae10f2f28b22 100644
--- a/Common/Version.cpp
+++ b/Common/Version.cpp
@@ -1,9 +1,8 @@
//===- lib/Common/Version.cpp - LLD Version Number ---------------*- C++-=====//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -13,31 +12,16 @@
#include "lld/Common/Version.h"
-using namespace llvm;
-
-// Returns an SVN repository path, which is usually "trunk".
-static std::string getRepositoryPath() {
- StringRef S = LLD_REPOSITORY_STRING;
- size_t Pos = S.find("lld/");
- if (Pos != StringRef::npos)
- return S.substr(Pos + 4);
- return S;
-}
-
-// Returns an SVN repository name, e.g., " (trunk 284614)"
-// or an empty string if no repository info is available.
-static std::string getRepository() {
- std::string Repo = getRepositoryPath();
- std::string Rev = LLD_REVISION_STRING;
-
- if (Repo.empty() && Rev.empty())
- return "";
- if (!Repo.empty() && !Rev.empty())
- return " (" + Repo + " " + Rev + ")";
- return " (" + Repo + Rev + ")";
-}
+#ifdef HAVE_VCS_VERSION_INC
+#include "VCSVersion.inc"
+#endif
-// Returns a version string, e.g., "LLD 4.0 (lld/trunk 284614)".
+// Returns a version string, e.g.:
+// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef)
std::string lld::getLLDVersion() {
- return "LLD " + std::string(LLD_VERSION_STRING) + getRepository();
+#if defined(LLD_REPOSITORY) && defined(LLD_REVISION)
+ return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")";
+#else
+ return "LLD " LLD_VERSION_STRING;
+#endif
}
diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp
index ac753cb58265..b2eda4dcbc4e 100644
--- a/ELF/AArch64ErrataFix.cpp
+++ b/ELF/AArch64ErrataFix.cpp
@@ -1,9 +1,8 @@
//===- AArch64ErrataFix.cpp -----------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 Section Patching for the purpose of working around
@@ -57,8 +56,8 @@ using namespace lld::elf;
// ADRP
// | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) |
-static bool isADRP(uint32_t Instr) {
- return (Instr & 0x9f000000) == 0x90000000;
+static bool isADRP(uint32_t instr) {
+ return (instr & 0x9f000000) == 0x90000000;
}
// Load and store bit patterns from ARMv8-A ARM ARM.
@@ -67,8 +66,8 @@ static bool isADRP(uint32_t Instr) {
// All loads and stores have 1 (at bit postion 27), (0 at bit position 25).
// | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) |
-static bool isLoadStoreClass(uint32_t Instr) {
- return (Instr & 0x0a000000) == 0x08000000;
+static bool isLoadStoreClass(uint32_t instr) {
+ return (instr & 0x0a000000) == 0x08000000;
}
// LDN/STN multiple no offset
@@ -83,20 +82,20 @@ static bool isLoadStoreClass(uint32_t Instr) {
// opcode == 0110 ST1 3 registers.
// opcode == 0111 ST1 1 register.
// opcode == 1010 ST1 2 registers.
-static bool isST1MultipleOpcode(uint32_t Instr) {
- return (Instr & 0x0000f000) == 0x00002000 ||
- (Instr & 0x0000f000) == 0x00006000 ||
- (Instr & 0x0000f000) == 0x00007000 ||
- (Instr & 0x0000f000) == 0x0000a000;
+static bool isST1MultipleOpcode(uint32_t instr) {
+ return (instr & 0x0000f000) == 0x00002000 ||
+ (instr & 0x0000f000) == 0x00006000 ||
+ (instr & 0x0000f000) == 0x00007000 ||
+ (instr & 0x0000f000) == 0x0000a000;
}
-static bool isST1Multiple(uint32_t Instr) {
- return (Instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(Instr);
+static bool isST1Multiple(uint32_t instr) {
+ return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr);
}
// Writes to Rn (writeback).
-static bool isST1MultiplePost(uint32_t Instr) {
- return (Instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(Instr);
+static bool isST1MultiplePost(uint32_t instr) {
+ return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr);
}
// LDN/STN single no offset
@@ -111,41 +110,41 @@ static bool isST1MultiplePost(uint32_t Instr) {
// opcode == 000 ST1 8-bit.
// opcode == 010 ST1 16-bit.
// opcode == 100 ST1 32 or 64-bit (Size determines which).
-static bool isST1SingleOpcode(uint32_t Instr) {
- return (Instr & 0x0040e000) == 0x00000000 ||
- (Instr & 0x0040e000) == 0x00004000 ||
- (Instr & 0x0040e000) == 0x00008000;
+static bool isST1SingleOpcode(uint32_t instr) {
+ return (instr & 0x0040e000) == 0x00000000 ||
+ (instr & 0x0040e000) == 0x00004000 ||
+ (instr & 0x0040e000) == 0x00008000;
}
-static bool isST1Single(uint32_t Instr) {
- return (Instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(Instr);
+static bool isST1Single(uint32_t instr) {
+ return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr);
}
// Writes to Rn (writeback).
-static bool isST1SinglePost(uint32_t Instr) {
- return (Instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(Instr);
+static bool isST1SinglePost(uint32_t instr) {
+ return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr);
}
-static bool isST1(uint32_t Instr) {
- return isST1Multiple(Instr) || isST1MultiplePost(Instr) ||
- isST1Single(Instr) || isST1SinglePost(Instr);
+static bool isST1(uint32_t instr) {
+ return isST1Multiple(instr) || isST1MultiplePost(instr) ||
+ isST1Single(instr) || isST1SinglePost(instr);
}
// Load/store exclusive
// | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) |
// L == 0 for Stores.
-static bool isLoadStoreExclusive(uint32_t Instr) {
- return (Instr & 0x3f000000) == 0x08000000;
+static bool isLoadStoreExclusive(uint32_t instr) {
+ return (instr & 0x3f000000) == 0x08000000;
}
-static bool isLoadExclusive(uint32_t Instr) {
- return (Instr & 0x3f400000) == 0x08400000;
+static bool isLoadExclusive(uint32_t instr) {
+ return (instr & 0x3f400000) == 0x08400000;
}
// Load register literal
// | opc (2) 01 | 1 V 00 | imm19 | Rt (5) |
-static bool isLoadLiteral(uint32_t Instr) {
- return (Instr & 0x3b000000) == 0x18000000;
+static bool isLoadLiteral(uint32_t instr) {
+ return (instr & 0x3b000000) == 0x18000000;
}
// Load/store no-allocate pair
@@ -153,8 +152,8 @@ static bool isLoadLiteral(uint32_t Instr) {
// | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
// L == 0 for stores.
// Never writes to register
-static bool isSTNP(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x28000000;
+static bool isSTNP(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x28000000;
}
// Load/store register pair
@@ -162,69 +161,69 @@ static bool isSTNP(uint32_t Instr) {
// | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
// L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP
// Writes to Rn.
-static bool isSTPPost(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x28800000;
+static bool isSTPPost(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x28800000;
}
// (offset)
// | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
-static bool isSTPOffset(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x29000000;
+static bool isSTPOffset(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x29000000;
}
// (pre-index)
// | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
// Writes to Rn.
-static bool isSTPPre(uint32_t Instr) {
- return (Instr & 0x3bc00000) == 0x29800000;
+static bool isSTPPre(uint32_t instr) {
+ return (instr & 0x3bc00000) == 0x29800000;
}
-static bool isSTP(uint32_t Instr) {
- return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr);
+static bool isSTP(uint32_t instr) {
+ return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr);
}
// Load/store register (unscaled immediate)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) |
// V == 0 for Scalar, V == 1 for Simd/FP.
-static bool isLoadStoreUnscaled(uint32_t Instr) {
- return (Instr & 0x3b000c00) == 0x38000000;
+static bool isLoadStoreUnscaled(uint32_t instr) {
+ return (instr & 0x3b000c00) == 0x38000000;
}
// Load/store register (immediate post-indexed)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) |
-static bool isLoadStoreImmediatePost(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38000400;
+static bool isLoadStoreImmediatePost(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38000400;
}
// Load/store register (unprivileged)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) |
-static bool isLoadStoreUnpriv(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38000800;
+static bool isLoadStoreUnpriv(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38000800;
}
// Load/store register (immediate pre-indexed)
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) |
-static bool isLoadStoreImmediatePre(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38000c00;
+static bool isLoadStoreImmediatePre(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38000c00;
}
// Load/store register (register offset)
// | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt |
-static bool isLoadStoreRegisterOff(uint32_t Instr) {
- return (Instr & 0x3b200c00) == 0x38200800;
+static bool isLoadStoreRegisterOff(uint32_t instr) {
+ return (instr & 0x3b200c00) == 0x38200800;
}
// Load/store register (unsigned immediate)
// | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) |
-static bool isLoadStoreRegisterUnsigned(uint32_t Instr) {
- return (Instr & 0x3b000000) == 0x39000000;
+static bool isLoadStoreRegisterUnsigned(uint32_t instr) {
+ return (instr & 0x3b000000) == 0x39000000;
}
// Rt is always in bit position 0 - 4.
-static uint32_t getRt(uint32_t Instr) { return (Instr & 0x1f); }
+static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); }
// Rn is always in bit position 5 - 9.
-static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; }
+static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; }
// C4.1.2 Branches, Exception Generating and System instructions
// | op0 (3) 1 | 01 op1 (4) | x (22) |
@@ -233,41 +232,41 @@ static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; }
// op0 == x00 101 op1 == xxxx Unconditional Branch immediate.
// op0 == x01 101 op1 == 0xxx Compare and branch immediate.
// op0 == x01 101 op1 == 1xxx Test and branch immediate.
-static bool isBranch(uint32_t Instr) {
- return ((Instr & 0xfe000000) == 0xd6000000) || // Cond branch.
- ((Instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
- ((Instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
- ((Instr & 0x7c000000) == 0x34000000); // Compare and test branch.
+static bool isBranch(uint32_t instr) {
+ return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch.
+ ((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
+ ((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
+ ((instr & 0x7c000000) == 0x34000000); // Compare and test branch.
}
-static bool isV8SingleRegisterNonStructureLoadStore(uint32_t Instr) {
- return isLoadStoreUnscaled(Instr) || isLoadStoreImmediatePost(Instr) ||
- isLoadStoreUnpriv(Instr) || isLoadStoreImmediatePre(Instr) ||
- isLoadStoreRegisterOff(Instr) || isLoadStoreRegisterUnsigned(Instr);
+static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) {
+ return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) ||
+ isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) ||
+ isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr);
}
// Note that this function refers to v8.0 only and does not include the
// additional load and store instructions added for in later revisions of
// the architecture such as the Atomic memory operations introduced
// in v8.1.
-static bool isV8NonStructureLoad(uint32_t Instr) {
- if (isLoadExclusive(Instr))
+static bool isV8NonStructureLoad(uint32_t instr) {
+ if (isLoadExclusive(instr))
return true;
- if (isLoadLiteral(Instr))
+ if (isLoadLiteral(instr))
return true;
- else if (isV8SingleRegisterNonStructureLoadStore(Instr)) {
+ else if (isV8SingleRegisterNonStructureLoadStore(instr)) {
// For Load and Store single register, Loads are derived from a
// combination of the Size, V and Opc fields.
- uint32_t Size = (Instr >> 30) & 0xff;
- uint32_t V = (Instr >> 26) & 0x1;
- uint32_t Opc = (Instr >> 22) & 0x3;
+ uint32_t size = (instr >> 30) & 0xff;
+ uint32_t v = (instr >> 26) & 0x1;
+ uint32_t opc = (instr >> 22) & 0x3;
// For the load and store instructions that we are decoding.
// Opc == 0 are all stores.
// Opc == 1 with a couple of exceptions are loads. The exceptions are:
// Size == 00 (0), V == 1, Opc == 10 (2) which is a store and
// Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch.
- return Opc != 0 && !(Size == 0 && V == 1 && Opc == 2) &&
- !(Size == 3 && V == 0 && Opc == 2);
+ return opc != 0 && !(size == 0 && v == 1 && opc == 2) &&
+ !(size == 3 && v == 0 && opc == 2);
}
return false;
}
@@ -276,18 +275,18 @@ static bool isV8NonStructureLoad(uint32_t Instr) {
// needed for errata 843419.
// Instruction with writeback updates the index register after the load/store.
-static bool hasWriteback(uint32_t Instr) {
- return isLoadStoreImmediatePre(Instr) || isLoadStoreImmediatePost(Instr) ||
- isSTPPre(Instr) || isSTPPost(Instr) || isST1SinglePost(Instr) ||
- isST1MultiplePost(Instr);
+static bool hasWriteback(uint32_t instr) {
+ return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) ||
+ isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) ||
+ isST1MultiplePost(instr);
}
// For the load and store class of instructions, a load can write to the
// destination register, a load and a store can write to the base register when
// the instruction has writeback.
-static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) {
- return (isV8NonStructureLoad(Instr) && getRt(Instr) == Reg) ||
- (hasWriteback(Instr) && getRn(Instr) == Reg);
+static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) {
+ return (isV8NonStructureLoad(instr) && getRt(instr) == reg) ||
+ (hasWriteback(instr) && getRn(instr) == reg);
}
// Scanner for Cortex-A53 errata 843419
@@ -319,18 +318,18 @@ static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) {
// Return true if the Instruction sequence Adrp, Instr2, and Instr4 match
// the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.),
// and 4.) in the Scanner for Cortex-A53 errata comment above.
-static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
- uint32_t Instr4) {
- if (!isADRP(Instr1))
+static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2,
+ uint32_t instr4) {
+ if (!isADRP(instr1))
return false;
- uint32_t Rn = getRt(Instr1);
- return isLoadStoreClass(Instr2) &&
- (isLoadStoreExclusive(Instr2) || isLoadLiteral(Instr2) ||
- isV8SingleRegisterNonStructureLoadStore(Instr2) || isSTP(Instr2) ||
- isSTNP(Instr2) || isST1(Instr2)) &&
- !doesLoadStoreWriteToReg(Instr2, Rn) &&
- isLoadStoreRegisterUnsigned(Instr4) && getRn(Instr4) == Rn;
+ uint32_t rn = getRt(instr1);
+ return isLoadStoreClass(instr2) &&
+ (isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) ||
+ isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) ||
+ isSTNP(instr2) || isST1(instr2)) &&
+ !doesLoadStoreWriteToReg(instr2, rn) &&
+ isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn;
}
// Scan the instruction sequence starting at Offset Off from the base of
@@ -339,143 +338,143 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
// instructions we've scanned.
// Return the offset of the load or store instruction in IS that we want to
// patch or 0 if no patch required.
-static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
- uint64_t Limit) {
- uint64_t ISAddr = IS->getVA(0);
+static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
+ uint64_t limit) {
+ uint64_t isecAddr = isec->getVA(0);
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
- uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
- if (InitialPageOff < 0xff8)
- Off += 0xff8 - InitialPageOff;
+ uint64_t initialPageOff = (isecAddr + off) & 0xfff;
+ if (initialPageOff < 0xff8)
+ off += 0xff8 - initialPageOff;
- bool OptionalAllowed = Limit - Off > 12;
- if (Off >= Limit || Limit - Off < 12) {
+ bool optionalAllowed = limit - off > 12;
+ if (off >= limit || limit - off < 12) {
// Need at least 3 4-byte sized instructions to trigger erratum.
- Off = Limit;
+ off = limit;
return 0;
}
- uint64_t PatchOff = 0;
- const uint8_t *Buf = IS->data().begin();
- const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off);
- uint32_t Instr1 = *InstBuf++;
- uint32_t Instr2 = *InstBuf++;
- uint32_t Instr3 = *InstBuf++;
- if (is843419ErratumSequence(Instr1, Instr2, Instr3)) {
- PatchOff = Off + 8;
- } else if (OptionalAllowed && !isBranch(Instr3)) {
- uint32_t Instr4 = *InstBuf++;
- if (is843419ErratumSequence(Instr1, Instr2, Instr4))
- PatchOff = Off + 12;
+ uint64_t patchOff = 0;
+ const uint8_t *buf = isec->data().begin();
+ const ulittle32_t *instBuf = reinterpret_cast<const ulittle32_t *>(buf + off);
+ uint32_t instr1 = *instBuf++;
+ uint32_t instr2 = *instBuf++;
+ uint32_t instr3 = *instBuf++;
+ if (is843419ErratumSequence(instr1, instr2, instr3)) {
+ patchOff = off + 8;
+ } else if (optionalAllowed && !isBranch(instr3)) {
+ uint32_t instr4 = *instBuf++;
+ if (is843419ErratumSequence(instr1, instr2, instr4))
+ patchOff = off + 12;
}
- if (((ISAddr + Off) & 0xfff) == 0xff8)
- Off += 4;
+ if (((isecAddr + off) & 0xfff) == 0xff8)
+ off += 4;
else
- Off += 0xffc;
- return PatchOff;
+ off += 0xffc;
+ return patchOff;
}
class lld::elf::Patch843419Section : public SyntheticSection {
public:
- Patch843419Section(InputSection *P, uint64_t Off);
+ Patch843419Section(InputSection *p, uint64_t off);
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override { return 8; }
uint64_t getLDSTAddr() const;
// The Section we are patching.
- const InputSection *Patchee;
+ const InputSection *patchee;
// The offset of the instruction in the Patchee section we are patching.
- uint64_t PatcheeOffset;
+ uint64_t patcheeOffset;
// A label for the start of the Patch that we can use as a relocation target.
- Symbol *PatchSym;
+ Symbol *patchSym;
};
-lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off)
+lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
".text.patch"),
- Patchee(P), PatcheeOffset(Off) {
- this->Parent = P->getParent();
- PatchSym = addSyntheticLocal(
- Saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
+ patchee(p), patcheeOffset(off) {
+ this->parent = p->getParent();
+ patchSym = addSyntheticLocal(
+ saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
getSize(), *this);
- addSyntheticLocal(Saver.save("$x"), STT_NOTYPE, 0, 0, *this);
+ addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
}
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
- return Patchee->getVA(PatcheeOffset);
+ return patchee->getVA(patcheeOffset);
}
-void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
+void lld::elf::Patch843419Section::writeTo(uint8_t *buf) {
// Copy the instruction that we will be replacing with a branch in the
// Patchee Section.
- write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset));
+ write32le(buf, read32le(patchee->data().begin() + patcheeOffset));
// Apply any relocation transferred from the original PatcheeSection.
- // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc
- // also adds OutSecOff so we need to subtract to avoid double counting.
- this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize());
+ // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc
+ // also adds outSecOff so we need to subtract to avoid double counting.
+ this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
// 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);
+ uint64_t s = getLDSTAddr() + 4;
+ uint64_t p = patchSym->getVA() + 4;
+ target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p);
}
void AArch64Err843419Patcher::init() {
// The AArch64 ABI permits data in executable sections. We must avoid scanning
// this data as if it were instructions to avoid false matches. We use the
// mapping symbols in the InputObjects to identify this data, caching the
- // results in SectionMap so we don't have to recalculate it each pass.
+ // results in sectionMap so we don't have to recalculate it each pass.
// The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe
// half open intervals [Symbol Value, Next Symbol Value) of code and data
// within sections. If there is no next symbol then the half open interval is
// [Symbol Value, End of section). The type, code or data, is determined by
// the mapping symbol name, $x for code, $d for data.
- auto IsCodeMapSymbol = [](const Symbol *B) {
- return B->getName() == "$x" || B->getName().startswith("$x.");
+ auto isCodeMapSymbol = [](const Symbol *b) {
+ return b->getName() == "$x" || b->getName().startswith("$x.");
};
- auto IsDataMapSymbol = [](const Symbol *B) {
- return B->getName() == "$d" || B->getName().startswith("$d.");
+ auto isDataMapSymbol = [](const Symbol *b) {
+ return b->getName() == "$d" || b->getName().startswith("$d.");
};
// Collect mapping symbols for every executable InputSection.
- for (InputFile *File : ObjectFiles) {
- auto *F = cast<ObjFile<ELF64LE>>(File);
- for (Symbol *B : F->getLocalSymbols()) {
- auto *Def = dyn_cast<Defined>(B);
- if (!Def)
+ for (InputFile *file : objectFiles) {
+ auto *f = cast<ObjFile<ELF64LE>>(file);
+ for (Symbol *b : f->getLocalSymbols()) {
+ auto *def = dyn_cast<Defined>(b);
+ if (!def)
continue;
- if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def))
+ if (!isCodeMapSymbol(def) && !isDataMapSymbol(def))
continue;
- if (auto *Sec = dyn_cast_or_null<InputSection>(Def->Section))
- if (Sec->Flags & SHF_EXECINSTR)
- SectionMap[Sec].push_back(Def);
+ if (auto *sec = dyn_cast_or_null<InputSection>(def->section))
+ if (sec->flags & SHF_EXECINSTR)
+ sectionMap[sec].push_back(def);
}
}
// For each InputSection make sure the mapping symbols are in sorted in
// ascending order and free from consecutive runs of mapping symbols with
// the same type. For example we must remove the redundant $d.1 from $x.0
// $d.0 $d.1 $x.1.
- for (auto &KV : SectionMap) {
- std::vector<const Defined *> &MapSyms = KV.second;
- if (MapSyms.size() <= 1)
+ for (auto &kv : sectionMap) {
+ std::vector<const Defined *> &mapSyms = kv.second;
+ if (mapSyms.size() <= 1)
continue;
- std::stable_sort(
- MapSyms.begin(), MapSyms.end(),
- [](const Defined *A, const Defined *B) { return A->Value < B->Value; });
- MapSyms.erase(
- std::unique(MapSyms.begin(), MapSyms.end(),
- [=](const Defined *A, const Defined *B) {
- return (IsCodeMapSymbol(A) && IsCodeMapSymbol(B)) ||
- (IsDataMapSymbol(A) && IsDataMapSymbol(B));
+ llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
+ return a->value < b->value;
+ });
+ mapSyms.erase(
+ std::unique(mapSyms.begin(), mapSyms.end(),
+ [=](const Defined *a, const Defined *b) {
+ return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) ||
+ (isDataMapSymbol(a) && isDataMapSymbol(b));
}),
- MapSyms.end());
+ mapSyms.end());
}
- Initialized = true;
+ initialized = true;
}
// Insert the PatchSections we have created back into the
@@ -484,60 +483,60 @@ void AArch64Err843419Patcher::init() {
// executable sections, although we may need to insert them earlier if the
// InputSectionDescription is larger than the maximum branch range.
void AArch64Err843419Patcher::insertPatches(
- InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) {
- uint64_t ISLimit;
- uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff;
- uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
- uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr;
+ InputSectionDescription &isd, std::vector<Patch843419Section *> &patches) {
+ uint64_t isecLimit;
+ uint64_t prevIsecLimit = isd.sections.front()->outSecOff;
+ uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing();
+ uint64_t outSecAddr = isd.sections.front()->getParent()->addr;
- // Set the OutSecOff of patches to the place where we want to insert them.
+ // Set the outSecOff of patches to the place where we want to insert them.
// We use a similar strategy to Thunk placement. Place patches roughly
// every multiple of maximum branch range.
- auto PatchIt = Patches.begin();
- auto PatchEnd = Patches.end();
- for (const InputSection *IS : ISD.Sections) {
- ISLimit = IS->OutSecOff + IS->getSize();
- if (ISLimit > PatchUpperBound) {
- while (PatchIt != PatchEnd) {
- if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit)
+ auto patchIt = patches.begin();
+ auto patchEnd = patches.end();
+ for (const InputSection *isec : isd.sections) {
+ isecLimit = isec->outSecOff + isec->getSize();
+ if (isecLimit > patchUpperBound) {
+ while (patchIt != patchEnd) {
+ if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit)
break;
- (*PatchIt)->OutSecOff = PrevISLimit;
- ++PatchIt;
+ (*patchIt)->outSecOff = prevIsecLimit;
+ ++patchIt;
}
- PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
+ patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing();
}
- PrevISLimit = ISLimit;
+ prevIsecLimit = isecLimit;
}
- for (; PatchIt != PatchEnd; ++PatchIt) {
- (*PatchIt)->OutSecOff = ISLimit;
+ for (; patchIt != patchEnd; ++patchIt) {
+ (*patchIt)->outSecOff = isecLimit;
}
- // merge all patch sections. We use the OutSecOff assigned above to
+ // merge all patch sections. We use the outSecOff assigned above to
// determine the insertion point. This is ok as we only merge into an
// InputSectionDescription once per pass, and at the end of the pass
- // assignAddresses() will recalculate all the OutSecOff values.
- std::vector<InputSection *> Tmp;
- Tmp.reserve(ISD.Sections.size() + Patches.size());
- auto MergeCmp = [](const InputSection *A, const InputSection *B) {
- if (A->OutSecOff < B->OutSecOff)
+ // assignAddresses() will recalculate all the outSecOff values.
+ std::vector<InputSection *> tmp;
+ tmp.reserve(isd.sections.size() + patches.size());
+ auto mergeCmp = [](const InputSection *a, const InputSection *b) {
+ if (a->outSecOff < b->outSecOff)
return true;
- if (A->OutSecOff == B->OutSecOff && isa<Patch843419Section>(A) &&
- !isa<Patch843419Section>(B))
+ if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) &&
+ !isa<Patch843419Section>(b))
return true;
return false;
};
- std::merge(ISD.Sections.begin(), ISD.Sections.end(), Patches.begin(),
- Patches.end(), std::back_inserter(Tmp), MergeCmp);
- ISD.Sections = std::move(Tmp);
+ std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
+ patches.end(), std::back_inserter(tmp), mergeCmp);
+ isd.sections = std::move(tmp);
}
-// Given an erratum sequence that starts at address AdrpAddr, with an
-// instruction that we need to patch at PatcheeOffset from the start of
+// Given an erratum sequence that starts at address adrpAddr, with an
+// instruction that we need to patch at patcheeOffset from the start of
// InputSection IS, create a Patch843419 Section and add it to the
// Patches that we need to insert.
-static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
- InputSection *IS,
- std::vector<Patch843419Section *> &Patches) {
+static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
+ InputSection *isec,
+ std::vector<Patch843419Section *> &patches) {
// There may be a relocation at the same offset that we are patching. There
// are four cases that we need to consider.
// Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
@@ -552,29 +551,29 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
// and replace the relocation with a R_AARCH_JUMP26 branch relocation.
// Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
// relocation at the offset.
- auto RelIt = std::find_if(
- IS->Relocations.begin(), IS->Relocations.end(),
- [=](const Relocation &R) { return R.Offset == PatcheeOffset; });
- if (RelIt != IS->Relocations.end() &&
- (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE))
+ auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) {
+ return r.offset == patcheeOffset;
+ });
+ if (relIt != isec->relocations.end() &&
+ (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE))
return;
log("detected cortex-a53-843419 erratum sequence starting at " +
- utohexstr(AdrpAddr) + " in unpatched output.");
+ utohexstr(adrpAddr) + " in unpatched output.");
- auto *PS = make<Patch843419Section>(IS, PatcheeOffset);
- Patches.push_back(PS);
+ auto *ps = make<Patch843419Section>(isec, patcheeOffset);
+ patches.push_back(ps);
- auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) {
- return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym};
+ auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) {
+ return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym};
};
- if (RelIt != IS->Relocations.end()) {
- PS->Relocations.push_back(
- {RelIt->Expr, RelIt->Type, 0, RelIt->Addend, RelIt->Sym});
- *RelIt = MakeRelToPatch(PatcheeOffset, PS->PatchSym);
+ if (relIt != isec->relocations.end()) {
+ ps->relocations.push_back(
+ {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym});
+ *relIt = makeRelToPatch(patcheeOffset, ps->patchSym);
} else
- IS->Relocations.push_back(MakeRelToPatch(PatcheeOffset, PS->PatchSym));
+ isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym));
}
// Scan all the instructions in InputSectionDescription, for each instance of
@@ -582,40 +581,40 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
// Patch843419Sections that need to be applied to ISD.
std::vector<Patch843419Section *>
AArch64Err843419Patcher::patchInputSectionDescription(
- InputSectionDescription &ISD) {
- std::vector<Patch843419Section *> Patches;
- for (InputSection *IS : ISD.Sections) {
+ InputSectionDescription &isd) {
+ std::vector<Patch843419Section *> patches;
+ for (InputSection *isec : isd.sections) {
// LLD doesn't use the erratum sequence in SyntheticSections.
- if (isa<SyntheticSection>(IS))
+ if (isa<SyntheticSection>(isec))
continue;
- // Use SectionMap to make sure we only scan code and not inline data.
+ // Use sectionMap to make sure we only scan code and not inline data.
// We have already sorted MapSyms in ascending order and removed consecutive
// mapping symbols of the same type. Our range of executable instructions to
- // scan is therefore [CodeSym->Value, DataSym->Value) or [CodeSym->Value,
+ // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value,
// section size).
- std::vector<const Defined *> &MapSyms = SectionMap[IS];
+ std::vector<const Defined *> &mapSyms = sectionMap[isec];
- auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) {
- return MS->getName().startswith("$x");
+ auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) {
+ return ms->getName().startswith("$x");
});
- while (CodeSym != MapSyms.end()) {
- auto DataSym = std::next(CodeSym);
- uint64_t Off = (*CodeSym)->Value;
- uint64_t Limit =
- (DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value;
+ while (codeSym != mapSyms.end()) {
+ auto dataSym = std::next(codeSym);
+ uint64_t off = (*codeSym)->value;
+ uint64_t limit =
+ (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value;
- while (Off < Limit) {
- uint64_t StartAddr = IS->getVA(Off);
- if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
- implementPatch(StartAddr, PatcheeOffset, IS, Patches);
+ while (off < limit) {
+ uint64_t startAddr = isec->getVA(off);
+ if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit))
+ implementPatch(startAddr, patcheeOffset, isec, patches);
}
- if (DataSym == MapSyms.end())
+ if (dataSym == mapSyms.end())
break;
- CodeSym = std::next(DataSym);
+ codeSym = std::next(dataSym);
}
}
- return Patches;
+ return patches;
}
// For each InputSectionDescription make one pass over the executable sections
@@ -631,22 +630,22 @@ AArch64Err843419Patcher::patchInputSectionDescription(
// Ouptut and Input Sections may have been changed.
// Returns false if no patches were required and no changes were made.
bool AArch64Err843419Patcher::createFixes() {
- if (Initialized == false)
+ if (initialized == false)
init();
- bool AddressesChanged = false;
- for (OutputSection *OS : OutputSections) {
- if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
+ bool addressesChanged = false;
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
continue;
- for (BaseCommand *BC : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
- std::vector<Patch843419Section *> Patches =
- patchInputSectionDescription(*ISD);
- if (!Patches.empty()) {
- insertPatches(*ISD, Patches);
- AddressesChanged = true;
+ for (BaseCommand *bc : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
+ std::vector<Patch843419Section *> patches =
+ patchInputSectionDescription(*isd);
+ if (!patches.empty()) {
+ insertPatches(*isd, patches);
+ addressesChanged = true;
}
}
}
- return AddressesChanged;
+ return addressesChanged;
}
diff --git a/ELF/AArch64ErrataFix.h b/ELF/AArch64ErrataFix.h
index edd154d4cab3..0548b58751ff 100644
--- a/ELF/AArch64ErrataFix.h
+++ b/ELF/AArch64ErrataFix.h
@@ -1,9 +1,8 @@
//===- AArch64ErrataFix.h ---------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -30,19 +29,19 @@ public:
private:
std::vector<Patch843419Section *>
- patchInputSectionDescription(InputSectionDescription &ISD);
+ patchInputSectionDescription(InputSectionDescription &isd);
- void insertPatches(InputSectionDescription &ISD,
- std::vector<Patch843419Section *> &Patches);
+ void insertPatches(InputSectionDescription &isd,
+ std::vector<Patch843419Section *> &patches);
void init();
- // A cache of the mapping symbols defined by the InputSecion sorted in order
+ // A cache of the mapping symbols defined by the InputSection sorted in order
// of ascending value with redundant symbols removed. These describe
// the ranges of code and data in an executable InputSection.
- std::map<InputSection *, std::vector<const Defined *>> SectionMap;
+ std::map<InputSection *, std::vector<const Defined *>> sectionMap;
- bool Initialized = false;
+ bool initialized = false;
};
} // namespace elf
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index 08ffe2a08c0f..4d4789702f03 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -1,9 +1,8 @@
//===- AArch64.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -24,60 +23,59 @@ 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 elf::getAArch64Page(uint64_t Expr) {
- return Expr & ~static_cast<uint64_t>(0xFFF);
+uint64_t elf::getAArch64Page(uint64_t expr) {
+ return expr & ~static_cast<uint64_t>(0xFFF);
}
namespace {
-class AArch64 final : public TargetInfo {
+class AArch64 : public TargetInfo {
public:
AArch64();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
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;
- 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;
+ 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;
+ 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;
};
} // namespace
AArch64::AArch64() {
- CopyRel = R_AARCH64_COPY;
- RelativeRel = R_AARCH64_RELATIVE;
- IRelativeRel = R_AARCH64_IRELATIVE;
- GotRel = R_AARCH64_GLOB_DAT;
- NoneRel = R_AARCH64_NONE;
- PltRel = R_AARCH64_JUMP_SLOT;
- TlsDescRel = R_AARCH64_TLSDESC;
- TlsGotRel = R_AARCH64_TLS_TPREL64;
- GotEntrySize = 8;
- GotPltEntrySize = 8;
- PltEntrySize = 16;
- PltHeaderSize = 32;
- DefaultMaxPageSize = 65536;
+ copyRel = R_AARCH64_COPY;
+ relativeRel = R_AARCH64_RELATIVE;
+ iRelativeRel = R_AARCH64_IRELATIVE;
+ gotRel = R_AARCH64_GLOB_DAT;
+ noneRel = R_AARCH64_NONE;
+ pltRel = R_AARCH64_JUMP_SLOT;
+ symbolicRel = R_AARCH64_ABS64;
+ tlsDescRel = R_AARCH64_TLSDESC;
+ tlsGotRel = R_AARCH64_TLS_TPREL64;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+ defaultMaxPageSize = 65536;
// Align to the 2 MiB page size (known as a superpage or huge page).
// FreeBSD automatically promotes 2 MiB-aligned allocations.
- DefaultImageBase = 0x200000;
+ defaultImageBase = 0x200000;
- NeedsThunks = true;
+ needsThunks = true;
}
-RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_AARCH64_TLSDESC_ADR_PAGE21:
return R_AARCH64_TLSDESC_PAGE;
case R_AARCH64_TLSDESC_LD64_LO12:
@@ -105,6 +103,7 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
case R_AARCH64_LD_PREL_LO19:
return R_PC;
case R_AARCH64_ADR_PREL_PG_HI21:
+ case R_AARCH64_ADR_PREL_PG_HI21_NC:
return R_AARCH64_PAGE_PC;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
@@ -119,18 +118,18 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
}
}
-RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- if (Expr == R_RELAX_TLS_GD_TO_IE) {
- if (Type == R_AARCH64_TLSDESC_ADR_PAGE21)
+RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ if (expr == R_RELAX_TLS_GD_TO_IE) {
+ if (type == R_AARCH64_TLSDESC_ADR_PAGE21)
return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
return R_RELAX_TLS_GD_TO_IE_ABS;
}
- return Expr;
+ return expr;
}
-bool AArch64::usesOnlyLowPageBits(RelType Type) const {
- switch (Type) {
+bool AArch64::usesOnlyLowPageBits(RelType type) const {
+ switch (type) {
default:
return false;
case R_AARCH64_ADD_ABS_LO12_NC:
@@ -147,18 +146,18 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const {
}
}
-RelType AArch64::getDynRel(RelType Type) const {
- if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64)
- return Type;
+RelType AArch64::getDynRel(RelType type) const {
+ if (type == R_AARCH64_ABS64)
+ return type;
return R_AARCH64_NONE;
}
-void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- write64le(Buf, In.Plt->getVA());
+void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const {
+ write64le(buf, in.plt->getVA());
}
-void AArch64::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+void AArch64::writePltHeader(uint8_t *buf) const {
+ const uint8_t pltData[] = {
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
@@ -168,42 +167,42 @@ void AArch64::writePltHeader(uint8_t *Buf) const {
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5 // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
-
- 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);
+ memcpy(buf, pltData, sizeof(pltData));
+
+ 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);
}
-void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
+void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t inst[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n]))
0x20, 0x02, 0x1f, 0xd6 // br x17
};
- memcpy(Buf, Inst, sizeof(Inst));
+ memcpy(buf, inst, sizeof(inst));
- 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);
+ 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);
}
-bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
// 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)
return false;
- uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
- return !inBranchRange(Type, BranchAddr, Dst);
+ uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
+ return !inBranchRange(type, branchAddr, dst);
}
uint32_t AArch64::getThunkSectionSpacing() const {
@@ -213,71 +212,72 @@ uint32_t AArch64::getThunkSectionSpacing() const {
return (128 * 1024 * 1024) - 0x30000;
}
-bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
- if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
return true;
// The AArch64 call and unconditional branch instructions have a range of
// +/- 128 MiB.
- uint64_t Range = 128 * 1024 * 1024;
- if (Dst > Src) {
+ uint64_t range = 128 * 1024 * 1024;
+ if (dst > src) {
// Immediate of branch is signed.
- Range -= 4;
- return Dst - Src <= Range;
+ range -= 4;
+ return dst - src <= range;
}
- return Src - Dst <= Range;
+ return src - dst <= range;
}
-static void write32AArch64Addr(uint8_t *L, uint64_t Imm) {
- uint32_t ImmLo = (Imm & 0x3) << 29;
- uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
- uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
- write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi);
+static void write32AArch64Addr(uint8_t *l, uint64_t imm) {
+ uint32_t immLo = (imm & 0x3) << 29;
+ uint32_t immHi = (imm & 0x1FFFFC) << 3;
+ uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
+ write32le(l, (read32le(l) & ~mask) | immLo | immHi);
}
// Return the bits [Start, End] from Val shifted Start bits.
// For instance, getBits(0xF0, 4, 8) returns 0xF.
-static uint64_t getBits(uint64_t Val, int Start, int End) {
- uint64_t Mask = ((uint64_t)1 << (End + 1 - Start)) - 1;
- return (Val >> Start) & Mask;
+static uint64_t getBits(uint64_t val, int start, int end) {
+ uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1;
+ return (val >> start) & mask;
}
-static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
+static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
-static void or32AArch64Imm(uint8_t *L, uint64_t Imm) {
- or32le(L, (Imm & 0xFFF) << 10);
+static void or32AArch64Imm(uint8_t *l, uint64_t imm) {
+ or32le(l, (imm & 0xFFF) << 10);
}
-void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkIntUInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_AARCH64_ABS64:
- case R_AARCH64_GLOB_DAT:
case R_AARCH64_PREL64:
- write64le(Loc, Val);
+ write64le(loc, val);
break;
case R_AARCH64_ADD_ABS_LO12_NC:
- or32AArch64Imm(Loc, Val);
+ or32AArch64Imm(loc, val);
break;
case R_AARCH64_ADR_GOT_PAGE:
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);
- write32AArch64Addr(Loc, Val >> 12);
+ checkInt(loc, val, 33, type);
+ 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);
- write32AArch64Addr(Loc, Val);
+ checkInt(loc, val, 21, type);
+ write32AArch64Addr(loc, val);
break;
case R_AARCH64_JUMP26:
// Normally we would just write the bits of the immediate field, however
@@ -287,75 +287,75 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// opcode and the immediate (0 001 | 01 imm26) we can do this
// transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
// the instruction we want to patch.
- write32le(Loc, 0x14000000);
+ write32le(loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
- checkInt(Loc, Val, 28, Type);
- or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
+ checkInt(loc, val, 28, type);
+ 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);
- or32le(Loc, (Val & 0x1FFFFC) << 3);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 21, type);
+ or32le(loc, (val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
- or32AArch64Imm(Loc, getBits(Val, 0, 11));
+ or32AArch64Imm(loc, getBits(val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
- checkAlignment(Loc, Val, 2, Type);
- or32AArch64Imm(Loc, getBits(Val, 1, 11));
+ checkAlignment(loc, val, 2, type);
+ 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);
- or32AArch64Imm(Loc, getBits(Val, 2, 11));
+ checkAlignment(loc, val, 4, type);
+ or32AArch64Imm(loc, getBits(val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LD64_GOT_LO12_NC:
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);
- or32AArch64Imm(Loc, getBits(Val, 3, 11));
+ checkAlignment(loc, val, 8, type);
+ 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);
- or32AArch64Imm(Loc, getBits(Val, 4, 11));
+ checkAlignment(loc, val, 16, type);
+ or32AArch64Imm(loc, getBits(val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0_NC:
- or32le(Loc, (Val & 0xFFFF) << 5);
+ or32le(loc, (val & 0xFFFF) << 5);
break;
case R_AARCH64_MOVW_UABS_G1_NC:
- or32le(Loc, (Val & 0xFFFF0000) >> 11);
+ or32le(loc, (val & 0xFFFF0000) >> 11);
break;
case R_AARCH64_MOVW_UABS_G2_NC:
- or32le(Loc, (Val & 0xFFFF00000000) >> 27);
+ or32le(loc, (val & 0xFFFF00000000) >> 27);
break;
case R_AARCH64_MOVW_UABS_G3:
- or32le(Loc, (Val & 0xFFFF000000000000) >> 43);
+ or32le(loc, (val & 0xFFFF000000000000) >> 43);
break;
case R_AARCH64_TSTBR14:
- checkInt(Loc, Val, 16, Type);
- or32le(Loc, (Val & 0xFFFC) << 3);
+ checkInt(loc, val, 16, type);
+ or32le(loc, (val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkUInt(Loc, Val, 24, Type);
- or32AArch64Imm(Loc, Val >> 12);
+ checkUInt(loc, val, 24, type);
+ or32AArch64Imm(loc, val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_ADD_LO12:
- or32AArch64Imm(Loc, Val);
+ or32AArch64Imm(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
-void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, 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]
@@ -367,25 +367,25 @@ 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, type);
- switch (Type) {
+ switch (type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
- write32le(Loc, 0xd503201f); // nop
+ write32le(loc, 0xd503201f); // nop
return;
case R_AARCH64_TLSDESC_ADR_PAGE21:
- write32le(Loc, 0xd2a00000 | (((Val >> 16) & 0xffff) << 5)); // movz
+ write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz
return;
case R_AARCH64_TLSDESC_LD64_LO12:
- write32le(Loc, 0xf2800000 | ((Val & 0xffff) << 5)); // movk
+ write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk
return;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, 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]
@@ -398,43 +398,193 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// nop
// nop
- switch (Type) {
+ switch (type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
- write32le(Loc, 0xd503201f); // nop
+ write32le(loc, 0xd503201f); // nop
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
- write32le(Loc, 0x90000000); // adrp
- relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val);
+ write32le(loc, 0x90000000); // adrp
+ relocateOne(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);
+ write32le(loc, 0xf9400000); // ldr
+ relocateOne(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, RelType type, uint64_t val) const {
+ checkUInt(loc, val, 32, type);
- if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
+ if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
- uint32_t RegNo = read32le(Loc) & 0x1f;
- write32le(Loc, (0xd2a00000 | RegNo) | (((Val >> 16) & 0xffff) << 5));
+ 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 (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
// Generate MOVK.
- uint32_t RegNo = read32le(Loc) & 0x1f;
- write32le(Loc, (0xf2800000 | RegNo) | ((Val & 0xffff) << 5));
+ uint32_t regNo = read32le(loc) & 0x1f;
+ write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5));
return;
}
llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
}
-TargetInfo *elf::getAArch64TargetInfo() {
- static AArch64 Target;
- return &Target;
+// AArch64 may use security features in variant PLT sequences. These are:
+// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target
+// Indicator (BTI) introduced in armv8.5-a. The additional instructions used
+// in the variant Plt sequences are encoded in the Hint space so they can be
+// deployed on older architectures, which treat the instructions as a nop.
+// PAC and BTI can be combined leading to the following combinations:
+// writePltHeader
+// writePltHeaderBti (no PAC Header needed)
+// writePlt
+// writePltBti (BTI only)
+// writePltPac (PAC only)
+// writePltBtiPac (BTI and PAC)
+//
+// When PAC is enabled the dynamic loader encrypts the address that it places
+// in the .got.plt using the pacia1716 instruction which encrypts the value in
+// x17 using the modifier in x16. The static linker places autia1716 before the
+// indirect branch to x17 to authenticate the address in x17 with the modifier
+// in x16. This makes it more difficult for an attacker to modify the value in
+// the .got.plt.
+//
+// When BTI is enabled all indirect branches must land on a bti instruction.
+// The static linker must place a bti instruction at the start of any PLT entry
+// that may be the target of an indirect branch. As the PLT entries call the
+// lazy resolver indirectly this must have a bti instruction at start. In
+// general a bti instruction is not needed for a PLT entry as indirect calls
+// are resolved to the function address and not the PLT entry for the function.
+// There are a small number of cases where the PLT address can escape, such as
+// taking the address of a function or ifunc via a non got-generating
+// relocation, and a shared library refers to that symbol.
+//
+// We use the bti c variant of the instruction which permits indirect branches
+// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI
+// guarantees that all indirect branches from code requiring BTI protection
+// will go via x16/x17
+
+namespace {
+class AArch64BtiPac final : public AArch64 {
+public:
+ AArch64BtiPac();
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+
+private:
+ bool btiHeader; // bti instruction needed in PLT Header
+ bool btiEntry; // bti instruction needed in PLT Entry
+ bool pacEntry; // autia1716 instruction needed in PLT Entry
+};
+} // namespace
+
+AArch64BtiPac::AArch64BtiPac() {
+ btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
+ // A BTI (Branch Target Indicator) Plt Entry is only required if the
+ // address of the PLT entry can be taken by the program, which permits an
+ // indirect jump to the PLT entry. This can happen when the address
+ // of the PLT entry for a function is canonicalised due to the address of
+ // 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.
+ btiEntry = btiHeader && !config->shared;
+ pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC);
+
+ if (btiEntry || pacEntry)
+ pltEntrySize = 24;
}
+
+void AArch64BtiPac::writePltHeader(uint8_t *buf) const {
+ const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
+ const uint8_t pltData[] = {
+ 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2]))
+ 0x20, 0x02, 0x1f, 0xd6, // br x17
+ 0x1f, 0x20, 0x03, 0xd5, // nop
+ 0x1f, 0x20, 0x03, 0xd5 // nop
+ };
+ const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
+
+ uint64_t got = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+
+ if (btiHeader) {
+ // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C
+ // instruction.
+ memcpy(buf, btiData, sizeof(btiData));
+ buf += sizeof(btiData);
+ plt += sizeof(btiData);
+ }
+ 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);
+ if (!btiHeader)
+ // We didn't add the BTI c instruction so round out size with NOP.
+ memcpy(buf + sizeof(pltData), nopData, sizeof(nopData));
+}
+
+void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ // The PLT entry is of the form:
+ // [btiData] addrInst (pacBr | stdBr) [nopData]
+ const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
+ const uint8_t addrInst[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
+ 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n]))
+ };
+ const uint8_t pacBr[] = {
+ 0x9f, 0x21, 0x03, 0xd5, // autia1716
+ 0x20, 0x02, 0x1f, 0xd6 // br x17
+ };
+ const uint8_t stdBr[] = {
+ 0x20, 0x02, 0x1f, 0xd6, // br x17
+ 0x1f, 0x20, 0x03, 0xd5 // nop
+ };
+ const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
+
+ if (btiEntry) {
+ memcpy(buf, btiData, sizeof(btiData));
+ buf += sizeof(btiData);
+ pltEntryAddr += sizeof(btiData);
+ }
+
+ 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);
+
+ if (pacEntry)
+ memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
+ else
+ memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr));
+ if (!btiEntry)
+ // We didn't add the BTI c instruction so round out size with NOP.
+ memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData));
+}
+
+static TargetInfo *getTargetInfo() {
+ if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI |
+ GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
+ static AArch64BtiPac t;
+ return &t;
+ }
+ static AArch64 t;
+ return &t;
+}
+
+TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp
index a7c6c84ceecd..f2e32ca0996d 100644
--- a/ELF/Arch/AMDGPU.cpp
+++ b/ELF/Arch/AMDGPU.cpp
@@ -1,9 +1,8 @@
//===- AMDGPU.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -26,62 +25,63 @@ class AMDGPU final : public TargetInfo {
public:
AMDGPU();
uint32_t calcEFlags() const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) 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;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
};
} // namespace
AMDGPU::AMDGPU() {
- RelativeRel = R_AMDGPU_RELATIVE64;
- GotRel = R_AMDGPU_ABS64;
- NoneRel = R_AMDGPU_NONE;
- GotEntrySize = 8;
+ relativeRel = R_AMDGPU_RELATIVE64;
+ gotRel = R_AMDGPU_ABS64;
+ noneRel = R_AMDGPU_NONE;
+ symbolicRel = R_AMDGPU_ABS64;
}
-static uint32_t getEFlags(InputFile *File) {
- return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
+static uint32_t getEFlags(InputFile *file) {
+ return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags;
}
uint32_t AMDGPU::calcEFlags() const {
- assert(!ObjectFiles.empty());
- uint32_t Ret = getEFlags(ObjectFiles[0]);
+ assert(!objectFiles.empty());
+ uint32_t ret = getEFlags(objectFiles[0]);
// Verify that all input files have the same e_flags.
- for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) {
- if (Ret == getEFlags(F))
+ for (InputFile *f : makeArrayRef(objectFiles).slice(1)) {
+ if (ret == getEFlags(f))
continue;
- error("incompatible e_flags: " + toString(F));
+ error("incompatible e_flags: " + toString(f));
return 0;
}
- return Ret;
+ return ret;
}
-void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
case R_AMDGPU_GOTPCREL32_LO:
case R_AMDGPU_REL32:
case R_AMDGPU_REL32_LO:
- write32le(Loc, Val);
+ write32le(loc, val);
break;
case R_AMDGPU_ABS64:
case R_AMDGPU_REL64:
- write64le(Loc, Val);
+ write64le(loc, val);
break;
case R_AMDGPU_GOTPCREL32_HI:
case R_AMDGPU_REL32_HI:
- write32le(Loc, Val >> 32);
+ write32le(loc, val >> 32);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_ABS64:
return R_ABS;
@@ -95,11 +95,19 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
case R_AMDGPU_GOTPCREL32_HI:
return R_GOT_PC;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
+RelType AMDGPU::getDynRel(RelType type) const {
+ if (type == R_AMDGPU_ABS64)
+ return type;
+ return R_AMDGPU_NONE;
+}
+
TargetInfo *elf::getAMDGPUTargetInfo() {
- static AMDGPU Target;
- return &Target;
+ static AMDGPU target;
+ return &target;
}
diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp
index 120caca671af..64adc33c07ae 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -1,9 +1,8 @@
//===- ARM.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -27,63 +26,62 @@ class ARM final : public TargetInfo {
public:
ARM();
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- RelType getDynRel(RelType Type) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void addPltSymbols(InputSection &IS, uint64_t Off) const override;
- void addPltHeaderSymbols(InputSection &ISD) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void addPltSymbols(InputSection &isec, uint64_t off) const override;
+ void addPltHeaderSymbols(InputSection &isd) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) 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;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
ARM::ARM() {
- CopyRel = R_ARM_COPY;
- RelativeRel = R_ARM_RELATIVE;
- IRelativeRel = R_ARM_IRELATIVE;
- GotRel = R_ARM_GLOB_DAT;
- NoneRel = R_ARM_NONE;
- PltRel = R_ARM_JUMP_SLOT;
- TlsGotRel = R_ARM_TLS_TPOFF32;
- TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
- TlsOffsetRel = R_ARM_TLS_DTPOFF32;
- GotBaseSymInGotPlt = false;
- GotEntrySize = 4;
- GotPltEntrySize = 4;
- PltEntrySize = 16;
- PltHeaderSize = 32;
- TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
- NeedsThunks = true;
+ copyRel = R_ARM_COPY;
+ relativeRel = R_ARM_RELATIVE;
+ iRelativeRel = R_ARM_IRELATIVE;
+ gotRel = R_ARM_GLOB_DAT;
+ noneRel = R_ARM_NONE;
+ pltRel = R_ARM_JUMP_SLOT;
+ symbolicRel = R_ARM_ABS32;
+ tlsGotRel = R_ARM_TLS_TPOFF32;
+ tlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
+ tlsOffsetRel = R_ARM_TLS_DTPOFF32;
+ gotBaseSymInGotPlt = false;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+ trapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
+ needsThunks = true;
}
uint32_t ARM::calcEFlags() const {
// The ABIFloatType is used by loaders to detect the floating point calling
// convention.
- uint32_t ABIFloatType = 0;
- if (Config->ARMVFPArgs == ARMVFPArgKind::Base ||
- Config->ARMVFPArgs == ARMVFPArgKind::Default)
- ABIFloatType = EF_ARM_ABI_FLOAT_SOFT;
- else if (Config->ARMVFPArgs == ARMVFPArgKind::VFP)
- ABIFloatType = EF_ARM_ABI_FLOAT_HARD;
+ uint32_t abiFloatType = 0;
+ if (config->armVFPArgs == ARMVFPArgKind::Base ||
+ config->armVFPArgs == ARMVFPArgKind::Default)
+ abiFloatType = EF_ARM_ABI_FLOAT_SOFT;
+ else if (config->armVFPArgs == ARMVFPArgKind::VFP)
+ abiFloatType = EF_ARM_ABI_FLOAT_HARD;
// We don't currently use any features incompatible with EF_ARM_EABI_VER5,
// but we don't have any firm guarantees of conformance. Linux AArch64
// kernels (as of 2016) require an EABI version to be set.
- return EF_ARM_EABI_VER5 | ABIFloatType;
+ return EF_ARM_EABI_VER5 | abiFloatType;
}
-RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_ARM_THM_JUMP11:
return R_PC;
case R_ARM_CALL:
@@ -108,11 +106,11 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
case R_ARM_SBREL32:
return R_ARM_SBREL;
case R_ARM_TARGET1:
- return Config->Target1Rel ? R_PC : R_ABS;
+ return config->target1Rel ? R_PC : R_ABS;
case R_ARM_TARGET2:
- if (Config->Target2 == Target2Policy::Rel)
+ if (config->target2 == Target2Policy::Rel)
return R_PC;
- if (Config->Target2 == Target2Policy::Abs)
+ if (config->target2 == Target2Policy::Abs)
return R_ABS;
return R_GOT_PC;
case R_ARM_TLS_GD32:
@@ -145,25 +143,25 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
}
}
-RelType ARM::getDynRel(RelType Type) const {
- if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel))
+RelType ARM::getDynRel(RelType type) const {
+ if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel))
return R_ARM_ABS32;
return R_ARM_NONE;
}
-void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- write32le(Buf, In.Plt->getVA());
+void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const {
+ write32le(buf, in.plt->getVA());
}
-void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
+void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
// An ARM entry is the address of the ifunc resolver function.
- write32le(Buf, S.getVA());
+ write32le(buf, s.getVA());
}
// Long form PLT Header that does not have any restrictions on the displacement
// of the .plt from the .plt.got.
-static void writePltHeaderLong(uint8_t *Buf) {
- const uint8_t PltData[] = {
+static void writePltHeaderLong(uint8_t *buf) {
+ const uint8_t pltData[] = {
0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]!
0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2
0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr
@@ -172,128 +170,128 @@ static void writePltHeaderLong(uint8_t *Buf) {
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
0xd4, 0xd4, 0xd4, 0xd4};
- memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t L1 = In.Plt->getVA() + 8;
- write32le(Buf + 16, GotPlt - L1 - 8);
+ memcpy(buf, pltData, sizeof(pltData));
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t l1 = in.plt->getVA() + 8;
+ write32le(buf + 16, gotPlt - l1 - 8);
}
// The default PLT header requires the .plt.got to be within 128 Mb of the
// .plt in the positive direction.
-void ARM::writePltHeader(uint8_t *Buf) const {
+void ARM::writePltHeader(uint8_t *buf) const {
// Use a similar sequence to that in writePlt(), the difference is the calling
// conventions mean we use lr instead of ip. The PLT entry is responsible for
// saving lr on the stack, the dynamic loader is responsible for reloading
// it.
- const uint32_t PltData[] = {
+ const uint32_t pltData[] = {
0xe52de004, // L1: str lr, [sp,#-4]!
0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4)
0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4)
0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
};
- uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4;
- if (!llvm::isUInt<27>(Offset)) {
+ uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4;
+ if (!llvm::isUInt<27>(offset)) {
// We cannot encode the Offset, use the long form.
- writePltHeaderLong(Buf);
+ writePltHeaderLong(buf);
return;
}
- write32le(Buf + 0, PltData[0]);
- write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff));
- write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff));
- write32le(Buf + 12, PltData[3] | (Offset & 0xfff));
- memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary
- memcpy(Buf + 20, TrapInstr.data(), 4);
- memcpy(Buf + 24, TrapInstr.data(), 4);
- memcpy(Buf + 28, TrapInstr.data(), 4);
+ write32le(buf + 0, pltData[0]);
+ write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff));
+ write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff));
+ write32le(buf + 12, pltData[3] | (offset & 0xfff));
+ memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary
+ memcpy(buf + 20, trapInstr.data(), 4);
+ memcpy(buf + 24, trapInstr.data(), 4);
+ memcpy(buf + 28, trapInstr.data(), 4);
}
-void ARM::addPltHeaderSymbols(InputSection &IS) const {
- addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS);
- addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS);
+void ARM::addPltHeaderSymbols(InputSection &isec) const {
+ addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec);
+ addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec);
}
// Long form PLT entries that do not have any restrictions on the displacement
// of the .plt from the .plt.got.
-static void writePltLong(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) {
- const uint8_t PltData[] = {
+static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) {
+ const uint8_t pltData[] = {
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip]
0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8
};
- memcpy(Buf, PltData, sizeof(PltData));
- uint64_t L1 = PltEntryAddr + 4;
- write32le(Buf + 12, GotPltEntryAddr - L1 - 8);
+ memcpy(buf, pltData, sizeof(pltData));
+ uint64_t l1 = pltEntryAddr + 4;
+ write32le(buf + 12, gotPltEntryAddr - l1 - 8);
}
// The default PLT entries require the .plt.got to be within 128 Mb of the
// .plt in the positive direction.
-void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
+void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
// The PLT entry is similar to the example given in Appendix A of ELF for
// the Arm Architecture. Instead of using the Group Relocations to find the
// optimal rotation for the 8-bit immediate used in the add instructions we
// hard code the most compact rotations for simplicity. This saves a load
// instruction over the long plt sequences.
- const uint32_t PltData[] = {
+ const uint32_t pltData[] = {
0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8
0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8
0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8
};
- uint64_t Offset = GotPltEntryAddr - PltEntryAddr - 8;
- if (!llvm::isUInt<27>(Offset)) {
+ uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8;
+ if (!llvm::isUInt<27>(offset)) {
// We cannot encode the Offset, use the long form.
- writePltLong(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff);
+ writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff);
return;
}
- write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff));
- write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff));
- write32le(Buf + 8, PltData[2] | (Offset & 0xfff));
- memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary
+ write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff));
+ write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff));
+ write32le(buf + 8, pltData[2] | (offset & 0xfff));
+ memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary
}
-void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const {
- addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS);
- addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS);
+void ARM::addPltSymbols(InputSection &isec, uint64_t off) const {
+ addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec);
+ addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec);
}
-bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) 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())
+ if (s.isUndefWeak() && !s.isInPlt())
return false;
// A state change from ARM to Thumb and vice versa must go through an
// interworking thunk if the relocation type is not R_ARM_CALL or
// R_ARM_THM_CALL.
- switch (Type) {
+ switch (type) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
// Source is ARM, all PLT entries are ARM so no interworking required.
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
- if (Expr == R_PC && ((S.getVA() & 1) == 1))
+ if (expr == R_PC && ((s.getVA() & 1) == 1))
return true;
LLVM_FALLTHROUGH;
case R_ARM_CALL: {
- uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
- return !inBranchRange(Type, BranchAddr, Dst);
+ uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
+ return !inBranchRange(type, branchAddr, dst);
}
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
// Source is Thumb, all PLT entries are ARM so interworking is required.
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
- if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0))
+ if (expr == R_PLT_PC || ((s.getVA() & 1) == 0))
return true;
LLVM_FALLTHROUGH;
case R_ARM_THM_CALL: {
- uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
- return !inBranchRange(Type, BranchAddr, Dst);
+ uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
+ return !inBranchRange(type, branchAddr, dst);
}
}
return false;
@@ -301,13 +299,13 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint32_t ARM::getThunkSectionSpacing() const {
// The placing of pre-created ThunkSections is controlled by the value
- // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
+ // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
// place the ThunkSection such that all branches from the InputSections
// prior to the ThunkSection can reach a Thunk placed at the end of the
// ThunkSection. Graphically:
- // | up to ThunkSectionSpacing .text input sections |
+ // | up to thunkSectionSpacing .text input sections |
// | ThunkSection |
- // | up to ThunkSectionSpacing .text input sections |
+ // | up to thunkSectionSpacing .text input sections |
// | ThunkSection |
// Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This
@@ -318,69 +316,68 @@ uint32_t ARM::getThunkSectionSpacing() const {
// Thumb B<cc>.W range +/- 1MiB
// If a branch cannot reach a pre-created ThunkSection a new one will be
// created so we can handle the rare cases of a Thumb 2 conditional branch.
- // We intentionally use a lower size for ThunkSectionSpacing than the maximum
+ // We intentionally use a lower size for thunkSectionSpacing than the maximum
// branch range so the end of the ThunkSection is more likely to be within
// range of the branch instruction that is furthest away. The value we shorten
- // ThunkSectionSpacing by is set conservatively to allow us to create 16,384
+ // thunkSectionSpacing by is set conservatively to allow us to create 16,384
// 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
// one of the Thunks going out of range.
- // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch
+ // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch
// range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except
// ARMv6T2) the range is +/- 4MiB.
- return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000
+ return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000
: 0x400000 - 0x7500;
}
-bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
- uint64_t Range;
- uint64_t InstrSize;
+bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ uint64_t range;
+ uint64_t instrSize;
- switch (Type) {
+ switch (type) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
case R_ARM_CALL:
- Range = 0x2000000;
- InstrSize = 4;
+ range = 0x2000000;
+ instrSize = 4;
break;
case R_ARM_THM_JUMP19:
- Range = 0x100000;
- InstrSize = 2;
+ range = 0x100000;
+ instrSize = 2;
break;
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000;
- InstrSize = 2;
+ range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000;
+ instrSize = 2;
break;
default:
return true;
}
// PC at Src is 2 instructions ahead, immediate of branch is signed
- if (Src > Dst)
- Range -= 2 * InstrSize;
+ if (src > dst)
+ range -= 2 * instrSize;
else
- Range += InstrSize;
+ range += instrSize;
- if ((Dst & 0x1) == 0)
+ if ((dst & 0x1) == 0)
// Destination is ARM, if ARM caller then Src is already 4-byte aligned.
// If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure
// destination will be 4 byte aligned.
- Src &= ~0x3;
+ src &= ~0x3;
else
// Bit 0 == 1 denotes Thumb state, it is not part of the range
- Dst &= ~0x1;
+ dst &= ~0x1;
- uint64_t Distance = (Src > Dst) ? Src - Dst : Dst - Src;
- return Distance <= Range;
+ uint64_t distance = (src > dst) ? src - dst : dst - src;
+ return distance <= range;
}
-void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
- case R_ARM_GLOB_DAT:
case R_ARM_GOTOFF32:
case R_ARM_GOT_BREL:
case R_ARM_GOT_PREL:
@@ -396,135 +393,132 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_ARM_TLS_LE32:
case R_ARM_TLS_TPOFF32:
case R_ARM_TLS_DTPOFF32:
- write32le(Loc, Val);
- break;
- case R_ARM_TLS_DTPMOD32:
- write32le(Loc, 1);
+ write32le(loc, val);
break;
case R_ARM_PREL31:
- checkInt(Loc, Val, 31, Type);
- write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
+ checkInt(loc, val, 31, type);
+ 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 (val & 1) {
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
- checkInt(Loc, Val, 26, Type);
- write32le(Loc, 0xfa000000 | // opcode
- ((Val & 2) << 23) | // H
- ((Val >> 2) & 0x00ffffff)); // imm24
+ checkInt(loc, val, 26, type);
+ write32le(loc, 0xfa000000 | // opcode
+ ((val & 2) << 23) | // H
+ ((val >> 2) & 0x00ffffff)); // imm24
break;
}
- if ((read32le(Loc) & 0xfe000000) == 0xfa000000)
+ if ((read32le(loc) & 0xfe000000) == 0xfa000000)
// BLX (always unconditional) instruction to an ARM Target, select an
// unconditional BL.
- write32le(Loc, 0xeb000000 | (read32le(Loc) & 0x00ffffff));
+ 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);
- write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
+ checkInt(loc, val, 26, type);
+ write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt(Loc, Val, 12, Type);
- write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff));
+ checkInt(loc, val, 12, type);
+ 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);
- write16le(Loc,
- (read16le(Loc) & 0xfbc0) | // opcode cond
- ((Val >> 10) & 0x0400) | // S
- ((Val >> 12) & 0x003f)); // imm6
- write16le(Loc + 2,
+ checkInt(loc, val, 21, type);
+ write16le(loc,
+ (read16le(loc) & 0xfbc0) | // opcode cond
+ ((val >> 10) & 0x0400) | // S
+ ((val >> 12) & 0x003f)); // imm6
+ write16le(loc + 2,
0x8000 | // opcode
- ((Val >> 8) & 0x0800) | // J2
- ((Val >> 5) & 0x2000) | // J1
- ((Val >> 1) & 0x07ff)); // imm11
+ ((val >> 8) & 0x0800) | // J2
+ ((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) {
+ 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
- Val = alignTo(Val, 4);
+ val = alignTo(val, 4);
}
// Bit 12 is 0 for BLX, 1 for BL
- write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12);
- if (!Config->ARMJ1J2BranchEncoding) {
+ 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);
- write16le(Loc,
+ checkInt(loc, val, 23, type);
+ write16le(loc,
0xf000 | // opcode
- ((Val >> 12) & 0x07ff)); // imm11
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0xd000) | // opcode
+ ((val >> 12) & 0x07ff)); // imm11
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0xd000) | // opcode
0x2800 | // J1 == J2 == 1
- ((Val >> 1) & 0x07ff)); // imm11
+ ((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);
- write16le(Loc,
+ checkInt(loc, val, 25, type);
+ write16le(loc,
0xf000 | // opcode
- ((Val >> 14) & 0x0400) | // S
- ((Val >> 12) & 0x03ff)); // imm10
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0xd000) | // opcode
- (((~(Val >> 10)) ^ (Val >> 11)) & 0x2000) | // J1
- (((~(Val >> 11)) ^ (Val >> 13)) & 0x0800) | // J2
- ((Val >> 1) & 0x07ff)); // imm11
+ ((val >> 14) & 0x0400) | // S
+ ((val >> 12) & 0x03ff)); // imm10
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0xd000) | // opcode
+ (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1
+ (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2
+ ((val >> 1) & 0x07ff)); // imm11
break;
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVW_PREL_NC:
- write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) |
- (Val & 0x0fff));
+ write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) |
+ (val & 0x0fff));
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
- write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
- (((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
+ 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:
// Encoding T1: A = imm4:i:imm3:imm8
- write16le(Loc,
+ write16le(loc,
0xf2c0 | // opcode
- ((Val >> 17) & 0x0400) | // i
- ((Val >> 28) & 0x000f)); // imm4
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0x8f00) | // opcode
- ((Val >> 12) & 0x7000) | // imm3
- ((Val >> 16) & 0x00ff)); // imm8
+ ((val >> 17) & 0x0400) | // i
+ ((val >> 28) & 0x000f)); // imm4
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0x8f00) | // opcode
+ ((val >> 12) & 0x7000) | // imm3
+ ((val >> 16) & 0x00ff)); // imm8
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVW_PREL_NC:
// Encoding T3: A = imm4:i:imm3:imm8
- write16le(Loc,
+ write16le(loc,
0xf240 | // opcode
- ((Val >> 1) & 0x0400) | // i
- ((Val >> 12) & 0x000f)); // imm4
- write16le(Loc + 2,
- (read16le(Loc + 2) & 0x8f00) | // opcode
- ((Val << 4) & 0x7000) | // imm3
- (Val & 0x00ff)); // imm8
+ ((val >> 1) & 0x0400) | // i
+ ((val >> 12) & 0x000f)); // imm4
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0x8f00) | // opcode
+ ((val << 4) & 0x7000) | // imm3
+ (val & 0x00ff)); // imm8
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
-int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
- switch (Type) {
+int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ switch (type) {
default:
return 0;
case R_ARM_ABS32:
@@ -540,47 +534,47 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_ARM_TLS_LDO32:
case R_ARM_TLS_IE32:
case R_ARM_TLS_LE32:
- return SignExtend64<32>(read32le(Buf));
+ return SignExtend64<32>(read32le(buf));
case R_ARM_PREL31:
- return SignExtend64<31>(read32le(Buf));
+ return SignExtend64<31>(read32le(buf));
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- return SignExtend64<26>(read32le(Buf) << 2);
+ return SignExtend64<26>(read32le(buf) << 2);
case R_ARM_THM_JUMP11:
- return SignExtend64<12>(read16le(Buf) << 1);
+ return SignExtend64<12>(read16le(buf) << 1);
case R_ARM_THM_JUMP19: {
// Encoding T3: A = S:J2:J1:imm10:imm6:0
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<20>(((Hi & 0x0400) << 10) | // S
- ((Lo & 0x0800) << 8) | // J2
- ((Lo & 0x2000) << 5) | // J1
- ((Hi & 0x003f) << 12) | // imm6
- ((Lo & 0x07ff) << 1)); // imm11:0
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<20>(((hi & 0x0400) << 10) | // S
+ ((lo & 0x0800) << 8) | // J2
+ ((lo & 0x2000) << 5) | // J1
+ ((hi & 0x003f) << 12) | // imm6
+ ((lo & 0x07ff) << 1)); // imm11:0
}
case R_ARM_THM_CALL:
- if (!Config->ARMJ1J2BranchEncoding) {
+ 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.
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11
- ((Lo & 0x7ff) << 1)); // imm11:0
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11
+ ((lo & 0x7ff) << 1)); // imm11:0
break;
}
LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24: {
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<24>(((Hi & 0x0400) << 14) | // S
- (~((Lo ^ (Hi << 3)) << 10) & 0x00800000) | // I1
- (~((Lo ^ (Hi << 1)) << 11) & 0x00400000) | // I2
- ((Hi & 0x003ff) << 12) | // imm0
- ((Lo & 0x007ff) << 1)); // imm11:0
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<24>(((hi & 0x0400) << 14) | // S
+ (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1
+ (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2
+ ((hi & 0x003ff) << 12) | // imm0
+ ((lo & 0x007ff) << 1)); // imm11:0
}
// ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and
// MOVT is in the range -32768 <= A < 32768
@@ -588,25 +582,25 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVT_PREL: {
- uint64_t Val = read32le(Buf) & 0x000f0fff;
- return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff));
+ 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: {
// Encoding T3: A = imm4:i:imm3:imm8
- uint16_t Hi = read16le(Buf);
- uint16_t Lo = read16le(Buf + 2);
- return SignExtend64<16>(((Hi & 0x000f) << 12) | // imm4
- ((Hi & 0x0400) << 1) | // i
- ((Lo & 0x7000) >> 4) | // imm3
- (Lo & 0x00ff)); // imm8
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4
+ ((hi & 0x0400) << 1) | // i
+ ((lo & 0x7000) >> 4) | // imm3
+ (lo & 0x00ff)); // imm8
}
}
}
TargetInfo *elf::getARMTargetInfo() {
- static ARM Target;
- return &Target;
+ static ARM target;
+ return &target;
}
diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp
index 637da3778bd2..869f0fe0c525 100644
--- a/ELF/Arch/AVR.cpp
+++ b/ELF/Arch/AVR.cpp
@@ -1,9 +1,8 @@
//===- AVR.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -44,34 +43,34 @@ namespace {
class AVR final : public TargetInfo {
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;
+ 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;
};
} // namespace
-AVR::AVR() { NoneRel = R_AVR_NONE; }
+AVR::AVR() { noneRel = R_AVR_NONE; }
-RelExpr AVR::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
+RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
return R_ABS;
}
-void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_AVR_CALL: {
- uint16_t Hi = Val >> 17;
- uint16_t Lo = Val >> 1;
- write16le(Loc, read16le(Loc) | ((Hi >> 1) << 4) | (Hi & 1));
- write16le(Loc + 2, Lo);
+ uint16_t hi = val >> 17;
+ uint16_t lo = val >> 1;
+ write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1));
+ write16le(loc + 2, lo);
break;
}
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
TargetInfo *elf::getAVRTargetInfo() {
- static AVR Target;
- return &Target;
+ static AVR target;
+ return &target;
}
diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp
index b4d33be2ad39..c497a6df7987 100644
--- a/ELF/Arch/Hexagon.cpp
+++ b/ELF/Arch/Hexagon.cpp
@@ -1,9 +1,8 @@
//===-- Hexagon.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -28,65 +27,65 @@ class Hexagon final : public TargetInfo {
public:
Hexagon();
uint32_t calcEFlags() 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 writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) 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 writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
} // namespace
Hexagon::Hexagon() {
- PltRel = R_HEX_JMP_SLOT;
- RelativeRel = R_HEX_RELATIVE;
- GotRel = R_HEX_GLOB_DAT;
- GotEntrySize = 4;
+ pltRel = R_HEX_JMP_SLOT;
+ relativeRel = R_HEX_RELATIVE;
+ gotRel = R_HEX_GLOB_DAT;
+ symbolicRel = R_HEX_32;
+
// The zero'th GOT entry is reserved for the address of _DYNAMIC. The
// next 3 are reserved for the dynamic loader.
- GotPltHeaderEntriesNum = 4;
- GotPltEntrySize = 4;
+ gotPltHeaderEntriesNum = 4;
- PltEntrySize = 16;
- PltHeaderSize = 32;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
// Hexagon Linux uses 64K pages by default.
- DefaultMaxPageSize = 0x10000;
- NoneRel = R_HEX_NONE;
+ defaultMaxPageSize = 0x10000;
+ noneRel = R_HEX_NONE;
}
uint32_t Hexagon::calcEFlags() const {
- assert(!ObjectFiles.empty());
+ assert(!objectFiles.empty());
// The architecture revision must always be equal to or greater than
// greatest revision in the list of inputs.
- uint32_t Ret = 0;
- for (InputFile *F : ObjectFiles) {
- uint32_t EFlags = cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
- if (EFlags > Ret)
- Ret = EFlags;
+ uint32_t ret = 0;
+ for (InputFile *f : objectFiles) {
+ uint32_t eflags = cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags;
+ if (eflags > ret)
+ ret = eflags;
}
- return Ret;
+ return ret;
}
-static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
- uint32_t Result = 0;
- size_t Off = 0;
+static uint32_t applyMask(uint32_t mask, uint32_t data) {
+ uint32_t result = 0;
+ size_t off = 0;
- for (size_t Bit = 0; Bit != 32; ++Bit) {
- uint32_t ValBit = (Data >> Off) & 1;
- uint32_t MaskBit = (Mask >> Bit) & 1;
- if (MaskBit) {
- Result |= (ValBit << Bit);
- ++Off;
+ for (size_t bit = 0; bit != 32; ++bit) {
+ uint32_t valBit = (data >> off) & 1;
+ uint32_t maskBit = (mask >> bit) & 1;
+ if (maskBit) {
+ result |= (valBit << bit);
+ ++off;
}
}
- return Result;
+ return result;
}
-RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_HEX_B9_PCREL:
case R_HEX_B9_PCREL_X:
case R_HEX_B13_PCREL:
@@ -109,16 +108,16 @@ RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
}
}
-static uint32_t findMaskR6(uint32_t Insn) {
+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
// for the given instruction.
struct InstructionMask {
- uint32_t CmpMask;
- uint32_t RelocMask;
+ uint32_t cmpMask;
+ uint32_t relocMask;
};
- static const InstructionMask R6[] = {
+ static const InstructionMask r6[] = {
{0x38000000, 0x0000201f}, {0x39000000, 0x0000201f},
{0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80},
{0x40000000, 0x000020f8}, {0x41000000, 0x000007e0},
@@ -136,124 +135,124 @@ static uint32_t findMaskR6(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.
- if ((0xC000 & Insn) == 0x0)
+ if ((0xC000 & insn) == 0x0)
return 0x03f00000;
- for (InstructionMask I : R6)
- if ((0xff000000 & Insn) == I.CmpMask)
- return I.RelocMask;
+ for (InstructionMask i : r6)
+ if ((0xff000000 & insn) == i.cmpMask)
+ return i.relocMask;
error("unrecognized instruction for R_HEX_6 relocation: 0x" +
- utohexstr(Insn));
+ utohexstr(insn));
return 0;
}
-static uint32_t findMaskR8(uint32_t Insn) {
- if ((0xff000000 & Insn) == 0xde000000)
+static uint32_t findMaskR8(uint32_t insn) {
+ if ((0xff000000 & insn) == 0xde000000)
return 0x00e020e8;
- if ((0xff000000 & Insn) == 0x3c000000)
+ if ((0xff000000 & insn) == 0x3c000000)
return 0x0000207f;
return 0x00001fe0;
}
-static uint32_t findMaskR11(uint32_t Insn) {
- if ((0xff000000 & Insn) == 0xa1000000)
+static uint32_t findMaskR11(uint32_t insn) {
+ if ((0xff000000 & insn) == 0xa1000000)
return 0x060020ff;
return 0x06003fe0;
}
-static uint32_t findMaskR16(uint32_t Insn) {
- if ((0xff000000 & Insn) == 0x48000000)
+static uint32_t findMaskR16(uint32_t insn) {
+ if ((0xff000000 & insn) == 0x48000000)
return 0x061f20ff;
- if ((0xff000000 & Insn) == 0x49000000)
+ if ((0xff000000 & insn) == 0x49000000)
return 0x061f3fe0;
- if ((0xff000000 & Insn) == 0x78000000)
+ if ((0xff000000 & insn) == 0x78000000)
return 0x00df3fe0;
- if ((0xff000000 & Insn) == 0xb0000000)
+ if ((0xff000000 & insn) == 0xb0000000)
return 0x0fe03fe0;
error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
- utohexstr(Insn));
+ utohexstr(insn));
return 0;
}
-static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
+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::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_HEX_NONE:
break;
case R_HEX_6_PCREL_X:
case R_HEX_6_X:
- or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val));
+ or32le(loc, applyMask(findMaskR6(read32le(loc)), val));
break;
case R_HEX_8_X:
- or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val));
+ or32le(loc, applyMask(findMaskR8(read32le(loc)), val));
break;
case R_HEX_9_X:
- or32le(Loc, applyMask(0x00003fe0, Val & 0x3f));
+ or32le(loc, applyMask(0x00003fe0, val & 0x3f));
break;
case R_HEX_10_X:
- or32le(Loc, applyMask(0x00203fe0, Val & 0x3f));
+ or32le(loc, applyMask(0x00203fe0, val & 0x3f));
break;
case R_HEX_11_X:
case R_HEX_GOT_11_X:
- or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f));
+ or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
break;
case R_HEX_12_X:
- or32le(Loc, applyMask(0x000007e0, Val));
+ or32le(loc, applyMask(0x000007e0, val));
break;
case R_HEX_16_X: // These relocs only have 6 effective bits.
case R_HEX_GOT_16_X:
- or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f));
+ or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
break;
case R_HEX_32:
case R_HEX_32_PCREL:
- or32le(Loc, Val);
+ or32le(loc, val);
break;
case R_HEX_32_6_X:
case R_HEX_GOT_32_6_X:
- or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
+ or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_B9_PCREL:
- or32le(Loc, applyMask(0x003000fe, Val >> 2));
+ or32le(loc, applyMask(0x003000fe, val >> 2));
break;
case R_HEX_B9_PCREL_X:
- or32le(Loc, applyMask(0x003000fe, Val & 0x3f));
+ or32le(loc, applyMask(0x003000fe, val & 0x3f));
break;
case R_HEX_B13_PCREL:
- or32le(Loc, applyMask(0x00202ffe, Val >> 2));
+ or32le(loc, applyMask(0x00202ffe, val >> 2));
break;
case R_HEX_B15_PCREL:
- or32le(Loc, applyMask(0x00df20fe, Val >> 2));
+ or32le(loc, applyMask(0x00df20fe, val >> 2));
break;
case R_HEX_B15_PCREL_X:
- or32le(Loc, applyMask(0x00df20fe, Val & 0x3f));
+ or32le(loc, applyMask(0x00df20fe, val & 0x3f));
break;
case R_HEX_B22_PCREL:
case R_HEX_PLT_B22_PCREL:
- or32le(Loc, applyMask(0x1ff3ffe, Val >> 2));
+ or32le(loc, applyMask(0x1ff3ffe, val >> 2));
break;
case R_HEX_B22_PCREL_X:
- or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f));
+ or32le(loc, applyMask(0x1ff3ffe, val & 0x3f));
break;
case R_HEX_B32_PCREL_X:
- or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
+ or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_HI16:
- or32le(Loc, applyMask(0x00c03fff, Val >> 16));
+ or32le(loc, applyMask(0x00c03fff, val >> 16));
break;
case R_HEX_LO16:
- or32le(Loc, applyMask(0x00c03fff, Val));
+ or32le(loc, applyMask(0x00c03fff, val));
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
break;
}
}
-void Hexagon::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+void Hexagon::writePltHeader(uint8_t *buf) const {
+ const uint8_t pltData[] = {
0x00, 0x40, 0x00, 0x00, // { immext (#0)
0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0
0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn
@@ -263,30 +262,30 @@ void Hexagon::writePltHeader(uint8_t *Buf) const {
0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker
0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment
};
- memcpy(Buf, PltData, sizeof(PltData));
+ memcpy(buf, pltData, sizeof(pltData));
// 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);
+ 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);
}
-void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
+void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t inst[] = {
0x00, 0x40, 0x00, 0x00, // { immext (#0)
0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) }
0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14)
0x00, 0xc0, 0x9c, 0x52, // jumpr r28
};
- memcpy(Buf, Inst, sizeof(Inst));
+ memcpy(buf, inst, sizeof(inst));
- relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr);
- relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr);
+ relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
+ relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
}
TargetInfo *elf::getHexagonTargetInfo() {
- static Hexagon Target;
- return &Target;
+ static Hexagon target;
+ return &target;
}
diff --git a/ELF/Arch/MSP430.cpp b/ELF/Arch/MSP430.cpp
index fe0c0fe64daf..90664396c85e 100644
--- a/ELF/Arch/MSP430.cpp
+++ b/ELF/Arch/MSP430.cpp
@@ -1,9 +1,8 @@
//===- MSP430.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -34,20 +33,20 @@ namespace {
class MSP430 final : public TargetInfo {
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;
+ 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;
};
} // namespace
MSP430::MSP430() {
// mov.b #0, r3
- TrapInstr = {0x43, 0x43, 0x43, 0x43};
+ trapInstr = {0x43, 0x43, 0x43, 0x43};
}
-RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr MSP430::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_MSP430_10_PCREL:
case R_MSP430_16_PCREL:
case R_MSP430_16_PCREL_BYTE:
@@ -60,35 +59,35 @@ RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S,
}
}
-void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_MSP430_8:
- checkIntUInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkIntUInt(loc, val, 8, type);
+ *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);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_MSP430_32:
- checkIntUInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkIntUInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_MSP430_10_PCREL: {
- int16_t Offset = ((int16_t)Val >> 1) - 1;
- checkInt(Loc, Offset, 10, Type);
- write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF));
+ int16_t offset = ((int16_t)val >> 1) - 1;
+ checkInt(loc, offset, 10, type);
+ write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF));
break;
}
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
TargetInfo *elf::getMSP430TargetInfo() {
- static MSP430 Target;
- return &Target;
+ static MSP430 target;
+ return &target;
}
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index 23b0c1dd8a2d..24b3957acd99 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -1,9 +1,8 @@
//===- MIPS.cpp -----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -29,47 +28,47 @@ template <class ELFT> class MIPS final : public TargetInfo {
public:
MIPS();
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- bool usesOnlyLowPageBits(RelType Type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ bool usesOnlyLowPageBits(RelType type) const override;
};
} // namespace
template <class ELFT> MIPS<ELFT>::MIPS() {
- GotPltHeaderEntriesNum = 2;
- DefaultMaxPageSize = 65536;
- GotEntrySize = sizeof(typename ELFT::uint);
- GotPltEntrySize = sizeof(typename ELFT::uint);
- GotBaseSymInGotPlt = false;
- PltEntrySize = 16;
- PltHeaderSize = 32;
- CopyRel = R_MIPS_COPY;
- NoneRel = R_MIPS_NONE;
- PltRel = R_MIPS_JUMP_SLOT;
- NeedsThunks = true;
+ gotPltHeaderEntriesNum = 2;
+ defaultMaxPageSize = 65536;
+ gotBaseSymInGotPlt = false;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+ copyRel = R_MIPS_COPY;
+ noneRel = R_MIPS_NONE;
+ pltRel = R_MIPS_JUMP_SLOT;
+ needsThunks = true;
// Set `sigrie 1` as a trap instruction.
- write32(TrapInstr.data(), 0x04170001);
+ write32(trapInstr.data(), 0x04170001);
if (ELFT::Is64Bits) {
- RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
- TlsGotRel = R_MIPS_TLS_TPREL64;
- TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
- TlsOffsetRel = R_MIPS_TLS_DTPREL64;
+ relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
+ symbolicRel = R_MIPS_64;
+ tlsGotRel = R_MIPS_TLS_TPREL64;
+ tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
+ tlsOffsetRel = R_MIPS_TLS_DTPREL64;
} else {
- RelativeRel = R_MIPS_REL32;
- TlsGotRel = R_MIPS_TLS_TPREL32;
- TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
- TlsOffsetRel = R_MIPS_TLS_DTPREL32;
+ relativeRel = R_MIPS_REL32;
+ symbolicRel = R_MIPS_32;
+ tlsGotRel = R_MIPS_TLS_TPREL32;
+ tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
+ tlsOffsetRel = R_MIPS_TLS_DTPREL32;
}
}
@@ -78,13 +77,13 @@ template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
}
template <class ELFT>
-RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
+RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
// See comment in the calculateMipsRelChain.
- if (ELFT::Is64Bits || Config->MipsN32Abi)
- Type &= 0xff;
+ if (ELFT::Is64Bits || config->mipsN32Abi)
+ type &= 0xff;
- switch (Type) {
+ switch (type) {
case R_MIPS_JALR:
case R_MICROMIPS_JALR:
return R_HINT;
@@ -108,9 +107,9 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
// offset between start of function and 'gp' value which by default
// equal to the start of .got section. In that case we consider these
// relocations as relative.
- if (&S == ElfSym::MipsGpDisp)
+ if (&s == ElfSym::mipsGpDisp)
return R_MIPS_GOT_GP_PC;
- if (&S == ElfSym::MipsLocalGp)
+ if (&s == ElfSym::mipsLocalGp)
return R_MIPS_GOT_GP;
LLVM_FALLTHROUGH;
case R_MIPS_32:
@@ -147,7 +146,7 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
return R_PC;
case R_MIPS_GOT16:
case R_MICROMIPS_GOT16:
- if (S.isLocal())
+ if (s.isLocal())
return R_MIPS_GOT_LOCAL_PAGE;
LLVM_FALLTHROUGH;
case R_MIPS_CALL16:
@@ -176,209 +175,213 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
- if (Type == R_MIPS_32 || Type == R_MIPS_64)
- return RelativeRel;
+template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType type) const {
+ if (type == symbolicRel)
+ return type;
return R_MIPS_NONE;
}
template <class ELFT>
-void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- uint64_t VA = In.Plt->getVA();
+void MIPS<ELFT>::writeGotPlt(uint8_t *buf, const Symbol &) const {
+ uint64_t va = in.plt->getVA();
if (isMicroMips())
- VA |= 1;
- write32<ELFT::TargetEndianness>(Buf, VA);
+ va |= 1;
+ write32<ELFT::TargetEndianness>(buf, va);
}
-template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) {
+template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
// The major opcode of a microMIPS instruction needs to appear
// in the first 16-bit word (lowest address) for efficient hardware
// decode so that it knows if the instruction is 16-bit or 32-bit
// as early as possible. To do so, little-endian binaries keep 16-bit
// words in a big-endian order. That is why we have to swap these
// words to get a correct value.
- uint32_t V = read32<E>(Loc);
+ uint32_t v = read32<E>(loc);
if (E == support::little)
- return (V << 16) | (V >> 16);
- return V;
+ return (v << 16) | (v >> 16);
+ return v;
}
template <endianness E>
-static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
- uint32_t Instr = read32<E>(Loc);
- uint32_t Mask = 0xffffffff >> (32 - BitsSize);
- uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
- write32<E>(Loc, Data);
+static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
+ uint8_t shift) {
+ uint32_t instr = read32<E>(loc);
+ uint32_t mask = 0xffffffff >> (32 - bitsSize);
+ uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
+ write32<E>(loc, data);
}
template <endianness E>
-static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
+static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
+ uint8_t shift) {
// See comments in readShuffle for purpose of this code.
- uint16_t *Words = (uint16_t *)Loc;
+ uint16_t *words = (uint16_t *)loc;
if (E == support::little)
- std::swap(Words[0], Words[1]);
+ std::swap(words[0], words[1]);
- writeValue<E>(Loc, V, BitsSize, Shift);
+ writeValue<E>(loc, v, bitsSize, shift);
if (E == support::little)
- std::swap(Words[0], Words[1]);
+ std::swap(words[0], words[1]);
}
template <endianness E>
-static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
- uint16_t Instr = read16<E>(Loc);
- uint16_t Mask = 0xffff >> (16 - BitsSize);
- uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
- write16<E>(Loc, Data);
+static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
+ uint8_t shift) {
+ uint16_t instr = read16<E>(loc);
+ uint16_t mask = 0xffff >> (16 - bitsSize);
+ uint16_t data = (instr & ~mask) | ((v >> shift) & mask);
+ write16<E>(loc, data);
}
-template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
- const endianness E = ELFT::TargetEndianness;
+template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
+ const endianness e = ELFT::TargetEndianness;
if (isMicroMips()) {
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
// Overwrite trap instructions written by Writer::writeTrapInstr.
- memset(Buf, 0, PltHeaderSize);
-
- write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
- write16<E>(Buf + 4, 0xff23); // lw $25, 0($3)
- write16<E>(Buf + 8, 0x0535); // subu16 $2, $2, $3
- write16<E>(Buf + 10, 0x2525); // srl16 $2, $2, 2
- write16<E>(Buf + 12, 0x3302); // addiu $24, $2, -2
- write16<E>(Buf + 14, 0xfffe);
- write16<E>(Buf + 16, 0x0dff); // move $15, $31
+ memset(buf, 0, pltHeaderSize);
+
+ write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
+ write16<e>(buf + 4, 0xff23); // lw $25, 0($3)
+ write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3
+ write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2
+ write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2
+ write16<e>(buf + 14, 0xfffe);
+ write16<e>(buf + 16, 0x0dff); // move $15, $31
if (isMipsR6()) {
- write16<E>(Buf + 18, 0x0f83); // move $28, $3
- write16<E>(Buf + 20, 0x472b); // jalrc $25
- write16<E>(Buf + 22, 0x0c00); // nop
- relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt);
+ write16<e>(buf + 18, 0x0f83); // move $28, $3
+ write16<e>(buf + 20, 0x472b); // jalrc $25
+ write16<e>(buf + 22, 0x0c00); // nop
+ relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
} else {
- write16<E>(Buf + 18, 0x45f9); // jalrc $25
- write16<E>(Buf + 20, 0x0f83); // move $28, $3
- write16<E>(Buf + 22, 0x0c00); // nop
- relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt);
+ write16<e>(buf + 18, 0x45f9); // jalrc $25
+ write16<e>(buf + 20, 0x0f83); // move $28, $3
+ write16<e>(buf + 22, 0x0c00); // nop
+ relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
}
return;
}
- if (Config->MipsN32Abi) {
- write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
- write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
+ if (config->mipsN32Abi) {
+ write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
+ write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
+ write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
+ write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
+ write32<e>(buf + 16, 0x03e07825); // move $15, $31
+ write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
} else if (ELFT::Is64Bits) {
- write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
- write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c0c2); // srl $24, $24, 3
+ write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
+ write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
+ write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
+ write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
+ write32<e>(buf + 16, 0x03e07825); // move $15, $31
+ write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3
} else {
- write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
- write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
+ write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
+ write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
+ write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
+ write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28
+ write32<e>(buf + 16, 0x03e07825); // move $15, $31
+ write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
}
- uint32_t JalrInst = Config->ZHazardplt ? 0x0320fc09 : 0x0320f809;
- write32<E>(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25
- write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
+ uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
+ write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
+ write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2
- uint64_t GotPlt = In.GotPlt->getVA();
- writeValue<E>(Buf, GotPlt + 0x8000, 16, 16);
- writeValue<E>(Buf + 4, GotPlt, 16, 0);
- writeValue<E>(Buf + 8, GotPlt, 16, 0);
+ uint64_t gotPlt = in.gotPlt->getVA();
+ writeValue<e>(buf, gotPlt + 0x8000, 16, 16);
+ writeValue<e>(buf + 4, gotPlt, 16, 0);
+ writeValue<e>(buf + 8, gotPlt, 16, 0);
}
template <class ELFT>
-void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const endianness E = ELFT::TargetEndianness;
+void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const endianness e = ELFT::TargetEndianness;
if (isMicroMips()) {
// Overwrite trap instructions written by Writer::writeTrapInstr.
- memset(Buf, 0, PltEntrySize);
+ memset(buf, 0, pltEntrySize);
if (isMipsR6()) {
- write16<E>(Buf, 0x7840); // addiupc $2, (GOTPLT) - .
- write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
- write16<E>(Buf + 8, 0x0f02); // move $24, $2
- write16<E>(Buf + 10, 0x4723); // jrc $25 / jr16 $25
- relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr);
+ write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - .
+ write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
+ write16<e>(buf + 8, 0x0f02); // move $24, $2
+ write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25
+ relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
} else {
- write16<E>(Buf, 0x7900); // addiupc $2, (GOTPLT) - .
- write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
- write16<E>(Buf + 8, 0x4599); // jrc $25 / jr16 $25
- write16<E>(Buf + 10, 0x0f02); // move $24, $2
- relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr);
+ write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - .
+ write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
+ write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25
+ write16<e>(buf + 10, 0x0f02); // move $24, $2
+ relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
}
return;
}
- uint32_t JrInst = isMipsR6() ? (Config->ZHazardplt ? 0x03200409 : 0x03200009)
- : (Config->ZHazardplt ? 0x03200408 : 0x03200008);
-
- write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
- write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
- write32<E>(Buf + 8, JrInst); // jr $25 / jr.hb $25
- write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
- writeValue<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
- writeValue<E>(Buf + 4, GotPltEntryAddr, 16, 0);
- writeValue<E>(Buf + 12, GotPltEntryAddr, 16, 0);
+ uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000;
+ uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009)
+ : (config->zHazardplt ? 0x03200408 : 0x03200008);
+ uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
+
+ write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
+ write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
+ write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25
+ write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
+ writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16);
+ writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0);
+ writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0);
}
template <class ELFT>
-bool MIPS<ELFT>::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool MIPS<ELFT>::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
// Any MIPS PIC code function is invoked with its address in register $t9.
// So if we have a branch instruction from non-PIC code to the PIC one
// we cannot make the jump directly and need to create a small stubs
// to save the target function address.
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Type != R_MIPS_26 && Type != R_MICROMIPS_26_S1 &&
- Type != R_MICROMIPS_PC26_S1)
+ if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 &&
+ type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1)
return false;
- auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File);
- if (!F)
+ auto *f = dyn_cast_or_null<ObjFile<ELFT>>(file);
+ if (!f)
return false;
// If current file has PIC code, LA25 stub is not required.
- if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
+ if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC)
return false;
- auto *D = dyn_cast<Defined>(&S);
+ auto *d = dyn_cast<Defined>(&s);
// LA25 is required if target file has PIC code
// or target symbol is a PIC symbol.
- return D && isMipsPIC<ELFT>(D);
+ return d && isMipsPIC<ELFT>(d);
}
template <class ELFT>
-int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
- const endianness E = ELFT::TargetEndianness;
- switch (Type) {
+int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ const endianness e = ELFT::TargetEndianness;
+ switch (type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32:
- return SignExtend64<32>(read32<E>(Buf));
+ return SignExtend64<32>(read32<e>(buf));
case R_MIPS_26:
// FIXME (simon): If the relocation target symbol is not a PLT entry
// we should use another expression for calculation:
// ((A << 2) | (P & 0xf0000000)) >> 2
- return SignExtend64<28>(read32<E>(Buf) << 2);
+ return SignExtend64<28>(read32<e>(buf) << 2);
case R_MIPS_GOT16:
case R_MIPS_HI16:
case R_MIPS_PCHI16:
- return SignExtend64<16>(read32<E>(Buf)) << 16;
+ return SignExtend64<16>(read32<e>(buf)) << 16;
case R_MIPS_GPREL16:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
@@ -386,54 +389,54 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
- return SignExtend64<16>(read32<E>(Buf));
+ return SignExtend64<16>(read32<e>(buf));
case R_MICROMIPS_GOT16:
case R_MICROMIPS_HI16:
- return SignExtend64<16>(readShuffle<E>(Buf)) << 16;
+ return SignExtend64<16>(readShuffle<e>(buf)) << 16;
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_LO16:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_TPREL_HI16:
case R_MICROMIPS_TLS_TPREL_LO16:
- return SignExtend64<16>(readShuffle<E>(Buf));
+ return SignExtend64<16>(readShuffle<e>(buf));
case R_MICROMIPS_GPREL7_S2:
- return SignExtend64<9>(readShuffle<E>(Buf) << 2);
+ return SignExtend64<9>(readShuffle<e>(buf) << 2);
case R_MIPS_PC16:
- return SignExtend64<18>(read32<E>(Buf) << 2);
+ return SignExtend64<18>(read32<e>(buf) << 2);
case R_MIPS_PC19_S2:
- return SignExtend64<21>(read32<E>(Buf) << 2);
+ return SignExtend64<21>(read32<e>(buf) << 2);
case R_MIPS_PC21_S2:
- return SignExtend64<23>(read32<E>(Buf) << 2);
+ return SignExtend64<23>(read32<e>(buf) << 2);
case R_MIPS_PC26_S2:
- return SignExtend64<28>(read32<E>(Buf) << 2);
+ return SignExtend64<28>(read32<e>(buf) << 2);
case R_MIPS_PC32:
- return SignExtend64<32>(read32<E>(Buf));
+ return SignExtend64<32>(read32<e>(buf));
case R_MICROMIPS_26_S1:
- return SignExtend64<27>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<27>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC7_S1:
- return SignExtend64<8>(read16<E>(Buf) << 1);
+ return SignExtend64<8>(read16<e>(buf) << 1);
case R_MICROMIPS_PC10_S1:
- return SignExtend64<11>(read16<E>(Buf) << 1);
+ return SignExtend64<11>(read16<e>(buf) << 1);
case R_MICROMIPS_PC16_S1:
- return SignExtend64<17>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<17>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC18_S3:
- return SignExtend64<21>(readShuffle<E>(Buf) << 3);
+ return SignExtend64<21>(readShuffle<e>(buf) << 3);
case R_MICROMIPS_PC19_S2:
- return SignExtend64<21>(readShuffle<E>(Buf) << 2);
+ return SignExtend64<21>(readShuffle<e>(buf) << 2);
case R_MICROMIPS_PC21_S1:
- return SignExtend64<22>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<22>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC23_S2:
- return SignExtend64<25>(readShuffle<E>(Buf) << 2);
+ return SignExtend64<25>(readShuffle<e>(buf) << 2);
case R_MICROMIPS_PC26_S1:
- return SignExtend64<27>(readShuffle<E>(Buf) << 1);
+ return SignExtend64<27>(readShuffle<e>(buf) << 1);
default:
return 0;
}
}
static std::pair<uint32_t, uint64_t>
-calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
+calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) {
// MIPS N64 ABI packs multiple relocations into the single relocation
// record. In general, all up to three relocations can have arbitrary
// types. In fact, Clang and GCC uses only a few combinations. For now,
@@ -446,72 +449,134 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
// relocations used to modify result of the first one: extend it to
// 64-bit, extract high or low part etc. For details, see part 2.9 Relocation
// at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
- RelType Type2 = (Type >> 8) & 0xff;
- RelType Type3 = (Type >> 16) & 0xff;
- if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE)
- return std::make_pair(Type, Val);
- if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE)
- return std::make_pair(Type2, Val);
- if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16))
- return std::make_pair(Type3, -Val);
- error(getErrorLocation(Loc) + "unsupported relocations combination " +
- Twine(Type));
- return std::make_pair(Type & 0xff, Val);
+ RelType type2 = (type >> 8) & 0xff;
+ RelType type3 = (type >> 16) & 0xff;
+ if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE)
+ return std::make_pair(type, val);
+ if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE)
+ return std::make_pair(type2, val);
+ if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16))
+ return std::make_pair(type3, -val);
+ error(getErrorLocation(loc) + "unsupported relocations combination " +
+ Twine(type));
+ return std::make_pair(type & 0xff, val);
+}
+
+static bool isBranchReloc(RelType type) {
+ return type == R_MIPS_26 || type == R_MIPS_PC26_S2 ||
+ type == R_MIPS_PC21_S2 || type == R_MIPS_PC16;
+}
+
+static bool isMicroBranchReloc(RelType type) {
+ return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 ||
+ type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1;
}
template <class ELFT>
-void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- const endianness E = ELFT::TargetEndianness;
+static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
+ // Here we need to detect jump/branch from regular MIPS code
+ // to a microMIPS target and vice versa. In that cases jump
+ // instructions need to be replaced by their "cross-mode"
+ // equivalents.
+ const endianness e = ELFT::TargetEndianness;
+ bool isMicroTgt = val & 0x1;
+ bool isCrossJump = (isMicroTgt && isBranchReloc(type)) ||
+ (!isMicroTgt && isMicroBranchReloc(type));
+ if (!isCrossJump)
+ return val;
+
+ switch (type) {
+ case R_MIPS_26: {
+ uint32_t inst = read32<e>(loc) >> 26;
+ if (inst == 0x3 || inst == 0x1d) { // JAL or JALX
+ writeValue<e>(loc, 0x1d << 26, 32, 0);
+ return val;
+ }
+ break;
+ }
+ case R_MICROMIPS_26_S1: {
+ uint32_t inst = readShuffle<e>(loc) >> 26;
+ if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32
+ val >>= 1;
+ writeShuffleValue<e>(loc, 0x3c << 26, 32, 0);
+ return val;
+ }
+ break;
+ }
+ case R_MIPS_PC26_S2:
+ case R_MIPS_PC21_S2:
+ case R_MIPS_PC16:
+ case R_MICROMIPS_PC16_S1:
+ case R_MICROMIPS_PC10_S1:
+ case R_MICROMIPS_PC7_S1:
+ // FIXME (simon): Support valid branch relocations.
+ break;
+ default:
+ llvm_unreachable("unexpected jump/branch relocation");
+ }
+
+ error(getErrorLocation(loc) +
+ "unsupported jump/branch instruction between ISA modes referenced by " +
+ toString(type) + " relocation");
+ return val;
+}
+
+template <class ELFT>
+void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ const endianness e = ELFT::TargetEndianness;
+
+ if (ELFT::Is64Bits || config->mipsN32Abi)
+ std::tie(type, val) = calculateMipsRelChain(loc, type, val);
- if (ELFT::Is64Bits || Config->MipsN32Abi)
- std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
+ // Detect cross-mode jump/branch and fix instruction.
+ val = fixupCrossModeJump<ELFT>(loc, type, val);
// Thread pointer and DRP offsets from the start of TLS data area.
// https://www.linux-mips.org/wiki/NPTL
- if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 ||
- Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64 ||
- Type == R_MICROMIPS_TLS_DTPREL_HI16 ||
- Type == R_MICROMIPS_TLS_DTPREL_LO16) {
- Val -= 0x8000;
- } else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
- Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64 ||
- Type == R_MICROMIPS_TLS_TPREL_HI16 ||
- Type == R_MICROMIPS_TLS_TPREL_LO16) {
- Val -= 0x7000;
+ if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 ||
+ type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 ||
+ type == R_MICROMIPS_TLS_DTPREL_HI16 ||
+ type == R_MICROMIPS_TLS_DTPREL_LO16) {
+ val -= 0x8000;
+ } else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 ||
+ type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 ||
+ type == R_MICROMIPS_TLS_TPREL_HI16 ||
+ type == R_MICROMIPS_TLS_TPREL_LO16) {
+ val -= 0x7000;
}
- switch (Type) {
+ switch (type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32:
- write32<E>(Loc, Val);
+ write32<e>(loc, val);
break;
case R_MIPS_64:
case R_MIPS_TLS_DTPREL64:
case R_MIPS_TLS_TPREL64:
- write64<E>(Loc, Val);
+ write64<e>(loc, val);
break;
case R_MIPS_26:
- writeValue<E>(Loc, Val, 26, 2);
+ writeValue<e>(loc, val, 26, 2);
break;
case R_MIPS_GOT16:
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
// is updated addend (not a GOT index). In that case write high 16 bits
// to store a correct addend value.
- if (Config->Relocatable) {
- writeValue<E>(Loc, Val + 0x8000, 16, 16);
+ if (config->relocatable) {
+ writeValue<e>(loc, val + 0x8000, 16, 16);
} else {
- checkInt(Loc, Val, 16, Type);
- writeValue<E>(Loc, Val, 16, 0);
+ checkInt(loc, val, 16, type);
+ writeValue<e>(loc, val, 16, 0);
}
break;
case R_MICROMIPS_GOT16:
- if (Config->Relocatable) {
- writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
+ if (config->relocatable) {
+ writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
} else {
- checkInt(Loc, Val, 16, Type);
- writeShuffleValue<E>(Loc, Val, 16, 0);
+ checkInt(loc, val, 16, type);
+ writeShuffleValue<e>(loc, val, 16, 0);
}
break;
case R_MIPS_CALL16:
@@ -521,7 +586,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, type);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
@@ -530,13 +595,13 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_PCLO16:
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_LO16:
- writeValue<E>(Loc, Val, 16, 0);
+ writeValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
- checkInt(Loc, Val, 16, Type);
- writeShuffleValue<E>(Loc, Val, 16, 0);
+ checkInt(loc, val, 16, type);
+ writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_CALL16:
case R_MICROMIPS_CALL_LO16:
@@ -544,11 +609,11 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_TPREL_LO16:
- writeShuffleValue<E>(Loc, Val, 16, 0);
+ writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
- checkInt(Loc, Val, 7, Type);
- writeShuffleValue<E>(Loc, Val, 7, 2);
+ checkInt(loc, val, 7, type);
+ writeShuffleValue<e>(loc, val, 7, 2);
break;
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_HI16:
@@ -556,113 +621,113 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_PCHI16:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16:
- writeValue<E>(Loc, Val + 0x8000, 16, 16);
+ writeValue<e>(loc, val + 0x8000, 16, 16);
break;
case R_MICROMIPS_CALL_HI16:
case R_MICROMIPS_GOT_HI16:
case R_MICROMIPS_HI16:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_TPREL_HI16:
- writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
+ writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
break;
case R_MIPS_HIGHER:
- writeValue<E>(Loc, Val + 0x80008000, 16, 32);
+ writeValue<e>(loc, val + 0x80008000, 16, 32);
break;
case R_MIPS_HIGHEST:
- writeValue<E>(Loc, Val + 0x800080008000, 16, 48);
+ writeValue<e>(loc, val + 0x800080008000, 16, 48);
break;
case R_MIPS_JALR:
case R_MICROMIPS_JALR:
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 18, Type);
- writeValue<E>(Loc, Val, 16, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 18, type);
+ writeValue<e>(loc, val, 16, 2);
break;
case R_MIPS_PC19_S2:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 21, Type);
- writeValue<E>(Loc, Val, 19, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 21, type);
+ writeValue<e>(loc, val, 19, 2);
break;
case R_MIPS_PC21_S2:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 23, Type);
- writeValue<E>(Loc, Val, 21, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 23, type);
+ writeValue<e>(loc, val, 21, 2);
break;
case R_MIPS_PC26_S2:
- checkAlignment(Loc, Val, 4, Type);
- checkInt(Loc, Val, 28, Type);
- writeValue<E>(Loc, Val, 26, 2);
+ checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 28, type);
+ writeValue<e>(loc, val, 26, 2);
break;
case R_MIPS_PC32:
- writeValue<E>(Loc, Val, 32, 0);
+ writeValue<e>(loc, val, 32, 0);
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
- checkInt(Loc, Val, 27, Type);
- writeShuffleValue<E>(Loc, Val, 26, 1);
+ checkInt(loc, val, 27, type);
+ writeShuffleValue<e>(loc, val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
- checkInt(Loc, Val, 8, Type);
- writeMicroRelocation16<E>(Loc, Val, 7, 1);
+ checkInt(loc, val, 8, type);
+ writeMicroRelocation16<e>(loc, val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
- checkInt(Loc, Val, 11, Type);
- writeMicroRelocation16<E>(Loc, Val, 10, 1);
+ checkInt(loc, val, 11, type);
+ writeMicroRelocation16<e>(loc, val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
- checkInt(Loc, Val, 17, Type);
- writeShuffleValue<E>(Loc, Val, 16, 1);
+ checkInt(loc, val, 17, type);
+ writeShuffleValue<e>(loc, val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
- checkInt(Loc, Val, 21, Type);
- writeShuffleValue<E>(Loc, Val, 18, 3);
+ checkInt(loc, val, 21, type);
+ writeShuffleValue<e>(loc, val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
- checkInt(Loc, Val, 21, Type);
- writeShuffleValue<E>(Loc, Val, 19, 2);
+ checkInt(loc, val, 21, type);
+ writeShuffleValue<e>(loc, val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
- checkInt(Loc, Val, 22, Type);
- writeShuffleValue<E>(Loc, Val, 21, 1);
+ checkInt(loc, val, 22, type);
+ writeShuffleValue<e>(loc, val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
- checkInt(Loc, Val, 25, Type);
- writeShuffleValue<E>(Loc, Val, 23, 2);
+ checkInt(loc, val, 25, type);
+ writeShuffleValue<e>(loc, val, 23, 2);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const {
- return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST ||
- Type == R_MICROMIPS_LO16;
+template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const {
+ return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST ||
+ type == R_MICROMIPS_LO16;
}
// Return true if the symbol is a PIC function.
-template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) {
- if (!Sym->isFunc())
+template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
+ if (!sym->isFunc())
return false;
- if (Sym->StOther & STO_MIPS_PIC)
+ if (sym->stOther & STO_MIPS_PIC)
return true;
- if (!Sym->Section)
+ if (!sym->section)
return false;
- ObjFile<ELFT> *File =
- cast<InputSectionBase>(Sym->Section)->template getFile<ELFT>();
- if (!File)
+ ObjFile<ELFT> *file =
+ cast<InputSectionBase>(sym->section)->template getFile<ELFT>();
+ if (!file)
return false;
- return File->getObj().getHeader()->e_flags & EF_MIPS_PIC;
+ return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
}
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
- static MIPS<ELFT> Target;
- return &Target;
+ static MIPS<ELFT> target;
+ return &target;
}
template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
diff --git a/ELF/Arch/MipsArchTree.cpp b/ELF/Arch/MipsArchTree.cpp
index 98ceac3075e0..f64d03756457 100644
--- a/ELF/Arch/MipsArchTree.cpp
+++ b/ELF/Arch/MipsArchTree.cpp
@@ -1,9 +1,8 @@
//===- MipsArchTree.cpp --------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===---------------------------------------------------------------------===//
//
@@ -29,18 +28,18 @@ using namespace lld::elf;
namespace {
struct ArchTreeEdge {
- uint32_t Child;
- uint32_t Parent;
+ uint32_t child;
+ uint32_t parent;
};
struct FileFlags {
- InputFile *File;
- uint32_t Flags;
+ InputFile *file;
+ uint32_t flags;
};
} // namespace
-static StringRef getAbiName(uint32_t Flags) {
- switch (Flags) {
+static StringRef getAbiName(uint32_t flags) {
+ switch (flags) {
case 0:
return "n64";
case EF_MIPS_ABI2:
@@ -58,76 +57,76 @@ static StringRef getAbiName(uint32_t Flags) {
}
}
-static StringRef getNanName(bool IsNan2008) {
- return IsNan2008 ? "2008" : "legacy";
+static StringRef getNanName(bool isNan2008) {
+ return isNan2008 ? "2008" : "legacy";
}
-static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; }
+static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; }
-static void checkFlags(ArrayRef<FileFlags> Files) {
- assert(!Files.empty() && "expected non-empty file list");
+static void checkFlags(ArrayRef<FileFlags> files) {
+ assert(!files.empty() && "expected non-empty file list");
- uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
- bool Nan = Files[0].Flags & EF_MIPS_NAN2008;
- bool Fp = Files[0].Flags & EF_MIPS_FP64;
+ uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ bool nan = files[0].flags & EF_MIPS_NAN2008;
+ bool fp = files[0].flags & EF_MIPS_FP64;
- for (const FileFlags &F : Files) {
- if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS)
- error(toString(F.File) + ": microMIPS 64-bit is not supported");
+ for (const FileFlags &f : files) {
+ if (config->is64 && f.flags & EF_MIPS_MICROMIPS)
+ error(toString(f.file) + ": microMIPS 64-bit is not supported");
- uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
- if (ABI != ABI2)
- error(toString(F.File) + ": ABI '" + getAbiName(ABI2) +
- "' is incompatible with target ABI '" + getAbiName(ABI) + "'");
+ uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ if (abi != abi2)
+ error(toString(f.file) + ": ABI '" + getAbiName(abi2) +
+ "' is incompatible with target ABI '" + getAbiName(abi) + "'");
- bool Nan2 = F.Flags & EF_MIPS_NAN2008;
- if (Nan != Nan2)
- error(toString(F.File) + ": -mnan=" + getNanName(Nan2) +
- " is incompatible with target -mnan=" + getNanName(Nan));
+ bool nan2 = f.flags & EF_MIPS_NAN2008;
+ if (nan != nan2)
+ error(toString(f.file) + ": -mnan=" + getNanName(nan2) +
+ " is incompatible with target -mnan=" + getNanName(nan));
- bool Fp2 = F.Flags & EF_MIPS_FP64;
- if (Fp != Fp2)
- error(toString(F.File) + ": -mfp" + getFpName(Fp2) +
- " is incompatible with target -mfp" + getFpName(Fp));
+ bool fp2 = f.flags & EF_MIPS_FP64;
+ if (fp != fp2)
+ error(toString(f.file) + ": -mfp" + getFpName(fp2) +
+ " is incompatible with target -mfp" + getFpName(fp));
}
}
-static uint32_t getMiscFlags(ArrayRef<FileFlags> Files) {
- uint32_t Ret = 0;
- for (const FileFlags &F : Files)
- Ret |= F.Flags &
+static uint32_t getMiscFlags(ArrayRef<FileFlags> files) {
+ uint32_t ret = 0;
+ for (const FileFlags &f : files)
+ ret |= f.flags &
(EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER |
EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE);
- return Ret;
+ return ret;
}
-static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
+static uint32_t getPicFlags(ArrayRef<FileFlags> files) {
// Check PIC/non-PIC compatibility.
- bool IsPic = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
- for (const FileFlags &F : Files.slice(1)) {
- bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
- if (IsPic && !IsPic2)
- warn(toString(F.File) +
+ bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &f : files.slice(1)) {
+ bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ if (isPic && !isPic2)
+ warn(toString(f.file) +
": linking non-abicalls code with abicalls code " +
- toString(Files[0].File));
- if (!IsPic && IsPic2)
- warn(toString(F.File) +
+ toString(files[0].file));
+ if (!isPic && isPic2)
+ warn(toString(f.file) +
": linking abicalls code with non-abicalls code " +
- toString(Files[0].File));
+ toString(files[0].file));
}
// Compute the result PIC/non-PIC flag.
- uint32_t Ret = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
- for (const FileFlags &F : Files.slice(1))
- Ret &= F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &f : files.slice(1))
+ ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
// PIC code is inherently CPIC and may not set CPIC flag explicitly.
- if (Ret & EF_MIPS_PIC)
- Ret |= EF_MIPS_CPIC;
- return Ret;
+ if (ret & EF_MIPS_PIC)
+ ret |= EF_MIPS_CPIC;
+ return ret;
}
-static ArchTreeEdge ArchTree[] = {
+static ArchTreeEdge archTree[] = {
// MIPS32R6 and MIPS64R6 are not compatible with other extensions
// MIPS64R2 extensions.
{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2},
@@ -167,25 +166,25 @@ static ArchTreeEdge ArchTree[] = {
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
};
-static bool isArchMatched(uint32_t New, uint32_t Res) {
- if (New == Res)
+static bool isArchMatched(uint32_t New, uint32_t res) {
+ if (New == res)
return true;
- if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, Res))
+ if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
return true;
- if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, Res))
+ if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
return true;
- for (const auto &Edge : ArchTree) {
- if (Res == Edge.Child) {
- Res = Edge.Parent;
- if (Res == New)
+ for (const auto &edge : archTree) {
+ if (res == edge.child) {
+ res = edge.parent;
+ if (res == New)
return true;
}
}
return false;
}
-static StringRef getMachName(uint32_t Flags) {
- switch (Flags & EF_MIPS_MACH) {
+static StringRef getMachName(uint32_t flags) {
+ switch (flags & EF_MIPS_MACH) {
case EF_MIPS_MACH_NONE:
return "";
case EF_MIPS_MACH_3900:
@@ -229,8 +228,8 @@ static StringRef getMachName(uint32_t Flags) {
}
}
-static StringRef getArchName(uint32_t Flags) {
- switch (Flags & EF_MIPS_ARCH) {
+static StringRef getArchName(uint32_t flags) {
+ switch (flags & EF_MIPS_ARCH) {
case EF_MIPS_ARCH_1:
return "mips1";
case EF_MIPS_ARCH_2:
@@ -258,12 +257,12 @@ static StringRef getArchName(uint32_t Flags) {
}
}
-static std::string getFullArchName(uint32_t Flags) {
- StringRef Arch = getArchName(Flags);
- StringRef Mach = getMachName(Flags);
- if (Mach.empty())
- return Arch.str();
- return (Arch + " (" + Mach + ")").str();
+static std::string getFullArchName(uint32_t flags) {
+ StringRef arch = getArchName(flags);
+ StringRef mach = getMachName(flags);
+ if (mach.empty())
+ return arch.str();
+ return (arch + " (" + mach + ")").str();
}
// There are (arguably too) many MIPS ISAs out there. Their relationships
@@ -275,55 +274,55 @@ static std::string getFullArchName(uint32_t Flags) {
// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32
// are incompatible because nor mips3 is a parent for misp32, nor mips32
// is a parent for mips3.
-static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
- uint32_t Ret = Files[0].Flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
+ uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
- for (const FileFlags &F : Files.slice(1)) {
- uint32_t New = F.Flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+ for (const FileFlags &f : files.slice(1)) {
+ uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
// Check ISA compatibility.
- if (isArchMatched(New, Ret))
+ if (isArchMatched(New, ret))
continue;
- if (!isArchMatched(Ret, New)) {
- error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " +
- getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " +
+ if (!isArchMatched(ret, New)) {
+ error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
+ getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
getFullArchName(New));
return 0;
}
- Ret = New;
+ ret = New;
}
- return Ret;
+ return ret;
}
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});
- if (V.empty())
+ std::vector<FileFlags> v;
+ for (InputFile *f : objectFiles)
+ v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
+ if (v.empty())
return 0;
- checkFlags(V);
- return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V);
+ checkFlags(v);
+ return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
}
-static int compareMipsFpAbi(uint8_t FpA, uint8_t FpB) {
- if (FpA == FpB)
+static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) {
+ if (fpA == fpB)
return 0;
- if (FpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
+ if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
return 1;
- if (FpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
- FpA == Mips::Val_GNU_MIPS_ABI_FP_64)
+ if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64)
return 1;
- if (FpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
+ if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
return -1;
- if (FpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
- FpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
- FpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
+ if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
return 1;
return -1;
}
-static StringRef getMipsFpAbiName(uint8_t FpAbi) {
- switch (FpAbi) {
+static StringRef getMipsFpAbiName(uint8_t fpAbi) {
+ switch (fpAbi) {
case Mips::Val_GNU_MIPS_ABI_FP_ANY:
return "any";
case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
@@ -345,43 +344,43 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) {
}
}
-uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
- StringRef FileName) {
- if (compareMipsFpAbi(NewFlag, OldFlag) >= 0)
- return NewFlag;
- if (compareMipsFpAbi(OldFlag, NewFlag) < 0)
- error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) +
+uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ StringRef fileName) {
+ if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
+ return newFlag;
+ if (compareMipsFpAbi(oldFlag, newFlag) < 0)
+ error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) +
"' is incompatible with target floating point ABI '" +
- getMipsFpAbiName(OldFlag) + "'");
- return OldFlag;
+ getMipsFpAbiName(oldFlag) + "'");
+ return oldFlag;
}
-template <class ELFT> static bool isN32Abi(const InputFile *F) {
- if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(F))
- return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2;
+template <class ELFT> static bool isN32Abi(const InputFile *f) {
+ if (auto *ef = dyn_cast<ELFFileBase>(f))
+ return ef->template getObj<ELFT>().getHeader()->e_flags & EF_MIPS_ABI2;
return false;
}
-bool elf::isMipsN32Abi(const InputFile *F) {
- switch (Config->EKind) {
+bool elf::isMipsN32Abi(const InputFile *f) {
+ switch (config->ekind) {
case ELF32LEKind:
- return isN32Abi<ELF32LE>(F);
+ return isN32Abi<ELF32LE>(f);
case ELF32BEKind:
- return isN32Abi<ELF32BE>(F);
+ return isN32Abi<ELF32BE>(f);
case ELF64LEKind:
- return isN32Abi<ELF64LE>(F);
+ return isN32Abi<ELF64LE>(f);
case ELF64BEKind:
- return isN32Abi<ELF64BE>(F);
+ return isN32Abi<ELF64BE>(f);
default:
llvm_unreachable("unknown Config->EKind");
}
}
-bool elf::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; }
+bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
bool elf::isMipsR6() {
- uint32_t Arch = Config->EFlags & EF_MIPS_ARCH;
- return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
+ uint32_t arch = config->eflags & EF_MIPS_ARCH;
+ return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
}
template uint32_t elf::calcMipsEFlags<ELF32LE>();
diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp
index 767378067341..46c5891e4f8a 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -1,13 +1,14 @@
//===- PPC.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "OutputSections.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
@@ -22,60 +23,410 @@ namespace {
class PPC final : public TargetInfo {
public:
PPC();
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotHeader(uint8_t *buf) const override;
+ void writePltHeader(uint8_t *buf) const override {
+ llvm_unreachable("should call writePPC32GlinkSection() instead");
+ }
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override {
+ llvm_unreachable("should call writePPC32GlinkSection() instead");
+ }
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) 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;
+ 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;
};
} // namespace
+static uint16_t lo(uint32_t v) { return v; }
+static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; }
+
+static uint32_t readFromHalf16(const uint8_t *loc) {
+ return read32(config->isLE ? loc : loc - 2);
+}
+
+static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
+ write32(config->isLE ? loc : loc - 2, insn);
+}
+
+void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
+ // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
+ // absolute address from a specific .plt slot (usually called .got.plt on
+ // other targets) and jumps there.
+ //
+ // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load
+ // time. The .glink section is not used.
+ // b) With lazy binding, the .plt entry points to a `b PLTresolve`
+ // instruction in .glink, filled in by PPC::writeGotPlt().
+
+ // Write N `b PLTresolve` first.
+ for (size_t i = 0; i != numEntries; ++i)
+ write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i));
+ buf += 4 * numEntries;
+
+ // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve()
+ // computes the PLT index (by computing the distance from the landing b to
+ // itself) and calls _dl_runtime_resolve() (in glibc).
+ uint32_t got = in.got->getVA();
+ uint32_t glink = in.plt->getVA(); // VA of .glink
+ const uint8_t *end = buf + 64;
+ if (config->isPic) {
+ uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12;
+ uint32_t gotBcl = got + 4 - (glink + afterBcl);
+ write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha
+ write32(buf + 4, 0x7c0802a6); // mflr r0
+ write32(buf + 8, 0x429f0005); // bcl 20,30,.+4
+ write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l
+ write32(buf + 16, 0x7d8802a6); // mflr r12
+ write32(buf + 20, 0x7c0803a6); // mtlr r0
+ write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12
+ write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha
+ if (ha(gotBcl) == ha(gotBcl + 4)) {
+ write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12)
+ write32(buf + 36,
+ 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12)
+ } else {
+ write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12)
+ write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12)
+ }
+ write32(buf + 40, 0x7c0903a6); // mtctr 0
+ write32(buf + 44, 0x7c0b5a14); // add r0,11,11
+ write32(buf + 48, 0x7d605a14); // add r11,0,11
+ write32(buf + 52, 0x4e800420); // bctr
+ buf += 56;
+ } else {
+ write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha
+ write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha
+ if (ha(got + 4) == ha(got + 8))
+ write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12)
+ else
+ write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12)
+ write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l
+ write32(buf + 16, 0x7c0903a6); // mtctr r0
+ write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11
+ if (ha(got + 4) == ha(got + 8))
+ write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12)
+ else
+ write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12)
+ write32(buf + 28, 0x7d605a14); // add r11,r0,r11
+ write32(buf + 32, 0x4e800420); // bctr
+ buf += 36;
+ }
+
+ // Pad with nop. They should not be executed.
+ for (; buf < end; buf += 4)
+ write32(buf, 0x60000000);
+}
+
PPC::PPC() {
- NoneRel = R_PPC_NONE;
- GotBaseSymOff = 0x8000;
- GotBaseSymInGotPlt = false;
+ gotRel = R_PPC_GLOB_DAT;
+ noneRel = R_PPC_NONE;
+ pltRel = R_PPC_JMP_SLOT;
+ relativeRel = R_PPC_RELATIVE;
+ iRelativeRel = R_PPC_IRELATIVE;
+ symbolicRel = R_PPC_ADDR32;
+ gotBaseSymInGotPlt = false;
+ gotHeaderEntriesNum = 3;
+ gotPltHeaderEntriesNum = 0;
+ pltHeaderSize = 64; // size of PLTresolve in .glink
+ pltEntrySize = 4;
+
+ needsThunks = true;
+
+ tlsModuleIndexRel = R_PPC_DTPMOD32;
+ tlsOffsetRel = R_PPC_DTPREL32;
+ tlsGotRel = R_PPC_TPREL32;
+
+ defaultMaxPageSize = 65536;
+ defaultImageBase = 0x10000000;
+
+ write32(trapInstr.data(), 0x7fe00008);
+}
+
+void PPC::writeGotHeader(uint8_t *buf) const {
+ // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC
+ // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1],
+ // link_map in _GLOBAL_OFFSET_TABLE_[2].
+ write32(buf, mainPart->dynamic->getVA());
+}
+
+void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ // Address of the symbol resolver stub in .glink .
+ write32(buf, in.plt->getVA() + 4 * s.pltIndex);
+}
+
+bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
+ if (type != R_PPC_REL24 && type != R_PPC_PLTREL24)
+ return false;
+ if (s.isInPlt())
+ return true;
+ if (s.isUndefWeak())
+ return false;
+ return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA()));
}
-RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; }
+
+bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ uint64_t offset = dst - src;
+ if (type == R_PPC_REL24 || type == R_PPC_PLTREL24)
+ return isInt<26>(offset);
+ llvm_unreachable("unsupported relocation type used in branch");
+}
+
+RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_DTPREL32:
+ return R_DTPREL;
case R_PPC_REL14:
- case R_PPC_REL24:
case R_PPC_REL32:
+ case R_PPC_LOCAL24PC:
+ case R_PPC_REL16_LO:
+ case R_PPC_REL16_HI:
+ case R_PPC_REL16_HA:
return R_PC;
- case R_PPC_PLTREL24:
+ case R_PPC_GOT16:
+ return R_GOT_OFF;
+ case R_PPC_REL24:
return R_PLT_PC;
+ case R_PPC_PLTREL24:
+ return R_PPC32_PLTREL;
+ case R_PPC_GOT_TLSGD16:
+ return R_TLSGD_GOT;
+ case R_PPC_GOT_TLSLD16:
+ return R_TLSLD_GOT;
+ case R_PPC_GOT_TPREL16:
+ return R_GOT_OFF;
+ case R_PPC_TLS:
+ return R_TLSIE_HINT;
+ case R_PPC_TLSGD:
+ return R_TLSDESC_CALL;
+ case R_PPC_TLSLD:
+ return R_TLSLD_HINT;
+ case R_PPC_TPREL16:
+ case R_PPC_TPREL16_HA:
+ case R_PPC_TPREL16_LO:
+ case R_PPC_TPREL16_HI:
+ return R_TLS;
default:
return R_ABS;
}
}
-void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+RelType PPC::getDynRel(RelType type) const {
+ if (type == R_PPC_ADDR32)
+ return type;
+ return R_PPC_NONE;
+}
+
+static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) {
+ uint64_t dtpBiasedVal = val - 0x8000;
+ switch (type) {
+ case R_PPC_DTPREL16:
+ return {R_PPC64_ADDR16, dtpBiasedVal};
+ case R_PPC_DTPREL16_HA:
+ return {R_PPC_ADDR16_HA, dtpBiasedVal};
+ case R_PPC_DTPREL16_HI:
+ return {R_PPC_ADDR16_HI, dtpBiasedVal};
+ case R_PPC_DTPREL16_LO:
+ return {R_PPC_ADDR16_LO, dtpBiasedVal};
+ case R_PPC_DTPREL32:
+ return {R_PPC_ADDR32, dtpBiasedVal};
+ default:
+ return {type, val};
+ }
+}
+
+void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ RelType newType;
+ std::tie(newType, val) = fromDTPREL(type, val);
+ switch (newType) {
+ case R_PPC_ADDR16:
+ checkIntUInt(loc, val, 16, type);
+ write16(loc, val);
+ break;
+ case R_PPC_GOT16:
+ case R_PPC_GOT_TLSGD16:
+ case R_PPC_GOT_TLSLD16:
+ case R_PPC_GOT_TPREL16:
+ case R_PPC_TPREL16:
+ checkInt(loc, val, 16, type);
+ write16(loc, val);
+ break;
case R_PPC_ADDR16_HA:
- write16be(Loc, (Val + 0x8000) >> 16);
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_GOT_TLSGD16_HA:
+ case R_PPC_GOT_TLSLD16_HA:
+ case R_PPC_GOT_TPREL16_HA:
+ case R_PPC_REL16_HA:
+ case R_PPC_TPREL16_HA:
+ write16(loc, ha(val));
break;
case R_PPC_ADDR16_HI:
- write16be(Loc, Val >> 16);
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_GOT_TLSGD16_HI:
+ case R_PPC_GOT_TLSLD16_HI:
+ case R_PPC_GOT_TPREL16_HI:
+ case R_PPC_REL16_HI:
+ case R_PPC_TPREL16_HI:
+ write16(loc, val >> 16);
break;
case R_PPC_ADDR16_LO:
- write16be(Loc, Val);
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_GOT_TLSGD16_LO:
+ case R_PPC_GOT_TLSLD16_LO:
+ case R_PPC_GOT_TPREL16_LO:
+ case R_PPC_REL16_LO:
+ case R_PPC_TPREL16_LO:
+ write16(loc, val);
break;
case R_PPC_ADDR32:
case R_PPC_REL32:
- write32be(Loc, Val);
+ write32(loc, val);
break;
- case R_PPC_REL14:
- write32be(Loc, read32be(Loc) | (Val & 0xFFFC));
+ case R_PPC_REL14: {
+ uint32_t mask = 0x0000FFFC;
+ checkInt(loc, val, 16, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
- case R_PPC_PLTREL24:
+ }
case R_PPC_REL24:
- write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
+ case R_PPC_LOCAL24PC:
+ case R_PPC_PLTREL24: {
+ uint32_t mask = 0x03FFFFFC;
+ checkInt(loc, val, 26, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
+ }
+ default:
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ }
+}
+
+RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ if (expr == R_RELAX_TLS_GD_TO_IE)
+ return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
+ if (expr == R_RELAX_TLS_LD_TO_LE)
+ return R_RELAX_TLS_LD_TO_LE_ABS;
+ return expr;
+}
+
+int PPC::getTlsGdRelaxSkip(RelType type) const {
+ // A __tls_get_addr call instruction is marked with 2 relocations:
+ //
+ // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation
+ // R_PPC_REL24: __tls_get_addr
+ //
+ // After the relaxation we no longer call __tls_get_addr and should skip both
+ // relocations to not create a false dependence on __tls_get_addr being
+ // defined.
+ if (type == R_PPC_TLSGD || type == R_PPC_TLSLD)
+ return 2;
+ return 1;
+}
+
+void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (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);
+ break;
+ }
+ case R_PPC_TLSGD:
+ // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2
+ write32(loc, 0x7c631214);
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
+ }
+}
+
+void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC_GOT_TLSGD16:
+ // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha
+ writeFromHalf16(loc, 0x3c620000 | ha(val));
+ break;
+ case R_PPC_TLSGD:
+ // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l
+ write32(loc, 0x38630000 | lo(val));
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
+ }
+}
+
+void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
+ case R_PPC_GOT_TLSLD16:
+ // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0
+ writeFromHalf16(loc, 0x3c620000);
+ break;
+ case R_PPC_TLSLD:
+ // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel
+ // = r3+x-0x7000, so add 4096 to r3.
+ // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096
+ write32(loc, 0x38631000);
+ break;
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ relocateOne(loc, type, 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) {
+ case R_PPC_GOT_TPREL16: {
+ // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha
+ uint32_t rt = readFromHalf16(loc) & 0x03e00000;
+ writeFromHalf16(loc, 0x3c020000 | rt | ha(val));
+ break;
+ }
+ case R_PPC_TLS: {
+ uint32_t insn = read32(loc);
+ if (insn >> 26 != 31)
+ error("unrecognized instruction for IE to LE R_PPC_TLS");
+ // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l
+ uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1);
+ if (dFormOp == 0)
+ error("unrecognized instruction for IE to LE R_PPC_TLS");
+ write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val));
+ break;
+ }
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unsupported relocation for TLS IE to LE relaxation");
}
}
TargetInfo *elf::getPPCTargetInfo() {
- static PPC Target;
- return &Target;
+ static PPC target;
+ return &target;
}
diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp
index 8a320c9a4e9e..70d284cfad71 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -1,9 +1,8 @@
//===- PPC64.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -20,8 +19,8 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-static uint64_t PPC64TocOffset = 0x8000;
-static uint64_t DynamicThreadPointerOffset = 0x8000;
+static uint64_t ppc64TocOffset = 0x8000;
+static uint64_t dynamicThreadPointerOffset = 0x8000;
// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform
// instructions that can be used as part of the initial exec TLS sequence.
@@ -65,16 +64,16 @@ uint64_t elf::getPPC64TocBase() {
// 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
// the .got.
- uint64_t TocVA = In.Got->getVA();
+ uint64_t tocVA = in.got->getVA();
// Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
// thus permitting a full 64 Kbytes segment. Note that the glibc startup
// code (crt1.o) assumes that you can get from the TOC base to the
// start of the .toc section with only a single (signed) 16-bit relocation.
- return TocVA + PPC64TocOffset;
+ return tocVA + ppc64TocOffset;
}
-unsigned elf::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
@@ -86,43 +85,134 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) {
// 2 --> 2^2 = 4 bytes --> 1 instruction.
// 6 --> 2^6 = 64 bytes --> 16 instructions.
// 7 --> Reserved.
- uint8_t GepToLep = (StOther >> 5) & 7;
- if (GepToLep < 2)
+ uint8_t gepToLep = (stOther >> 5) & 7;
+ if (gepToLep < 2)
return 0;
// The value encoded in the st_other bits is the
// log-base-2(offset).
- if (GepToLep < 7)
- return 1 << GepToLep;
+ if (gepToLep < 7)
+ return 1 << gepToLep;
error("reserved value of 7 in the 3 most-significant-bits of st_other");
return 0;
}
+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;
+}
+
+// Find the R_PPC64_ADDR64 in .rela.toc with matching offset.
+template <typename ELFT>
+static std::pair<Defined *, int64_t>
+getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
+ if (tocSec->numRelocations == 0)
+ return {};
+
+ // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by
+ // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the
+ // relocation index in most cases.
+ //
+ // In rare cases a TOC entry may store a constant that doesn't need an
+ // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8
+ // points to a relocation with larger r_offset. Do a linear probe then.
+ // Constants are extremely uncommon in .toc and the extra number of array
+ // accesses can be seen as a small constant.
+ ArrayRef<typename ELFT::Rela> relas = tocSec->template relas<ELFT>();
+ uint64_t index = std::min<uint64_t>(offset / 8, relas.size() - 1);
+ for (;;) {
+ if (relas[index].r_offset == offset) {
+ Symbol &sym = tocSec->getFile<ELFT>()->getRelocTargetSym(relas[index]);
+ return {dyn_cast<Defined>(&sym), getAddend<ELFT>(relas[index])};
+ }
+ if (relas[index].r_offset < offset || index == 0)
+ break;
+ --index;
+ }
+ return {};
+}
+
+// When accessing a symbol defined in another translation unit, compilers
+// reserve a .toc entry, allocate a local label and generate toc-indirect
+// instuctions:
+//
+// 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
+// ld/lwa 3, 0(3) # load the value from the address
+//
+// .section .toc,"aw",@progbits
+// .LC0: .tc var[TC],var
+//
+// If var is defined, non-preemptable and addressable with a 32-bit signed
+// offset from the toc base, the address of var can be computed by adding an
+// offset to the toc base, saving a load.
+//
+// addis 3,2,var@toc@ha # this may be relaxed to a nop,
+// addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc
+// ld/lwa 3, 0(3) # load the value from the address
+//
+// Returns true if the relaxation is performed.
+bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
+ uint8_t *bufLoc) {
+ assert(config->tocOptimize);
+ if (rel.addend < 0)
+ return false;
+
+ // If the symbol is not the .toc section, this isn't a toc-indirection.
+ Defined *defSym = dyn_cast<Defined>(rel.sym);
+ if (!defSym || !defSym->isSection() || defSym->section->name != ".toc")
+ return false;
+
+ Defined *d;
+ int64_t addend;
+ auto *tocISB = cast<InputSectionBase>(defSym->section);
+ std::tie(d, addend) =
+ config->isLE ? getRelaTocSymAndAddend<ELF64LE>(tocISB, rel.addend)
+ : getRelaTocSymAndAddend<ELF64BE>(tocISB, rel.addend);
+
+ // Only non-preemptable defined symbols can be relaxed.
+ if (!d || d->isPreemptible)
+ return false;
+
+ // Two instructions can materialize a 32-bit signed offset from the toc base.
+ uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase();
+ if (!isInt<32>(tocRelative))
+ return false;
+
+ // Add PPC64TocOffset that will be subtracted by relocateOne().
+ target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset);
+ return true;
+}
+
namespace {
class PPC64 final : public TargetInfo {
public:
PPC64();
+ int getTlsGdRelaxSkip(RelType type) const override;
uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, 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) const override;
- 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 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;
-
- bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, 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) const override;
+ uint32_t getThunkSectionSpacing() const override;
+ 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;
+
+ bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const override;
};
} // namespace
@@ -130,19 +220,19 @@ public:
// #higher(value), #highera(value), #highest(value), and #highesta(value)
// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi
// document.
-static uint16_t lo(uint64_t V) { return V; }
-static uint16_t hi(uint64_t V) { return V >> 16; }
-static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; }
-static uint16_t higher(uint64_t V) { return V >> 32; }
-static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; }
-static uint16_t highest(uint64_t V) { return V >> 48; }
-static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; }
+static uint16_t lo(uint64_t v) { return v; }
+static uint16_t hi(uint64_t v) { return v >> 16; }
+static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; }
+static uint16_t higher(uint64_t v) { return v >> 32; }
+static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; }
+static uint16_t highest(uint64_t v) { return v >> 48; }
+static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; }
// Extracts the 'PO' field of an instruction encoding.
-static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); }
+static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); }
-static bool isDQFormInstruction(uint32_t Encoding) {
- switch (getPrimaryOpCode(Encoding)) {
+static bool isDQFormInstruction(uint32_t encoding) {
+ switch (getPrimaryOpCode(encoding)) {
default:
return false;
case 56:
@@ -152,12 +242,12 @@ static bool isDQFormInstruction(uint32_t Encoding) {
// There are both DS and DQ instruction forms with this primary opcode.
// Namely `lxv` and `stxv` are the DQ-forms that use it.
// The DS 'XO' bits being set to 01 is restricted to DQ form.
- return (Encoding & 3) == 0x1;
+ return (encoding & 3) == 0x1;
}
}
-static bool isInstructionUpdateForm(uint32_t Encoding) {
- switch (getPrimaryOpCode(Encoding)) {
+static bool isInstructionUpdateForm(uint32_t encoding) {
+ switch (getPrimaryOpCode(encoding)) {
default:
return false;
case LBZU:
@@ -176,7 +266,7 @@ static bool isInstructionUpdateForm(uint32_t Encoding) {
// between LD/LDU/LWA
case LD:
case STD:
- return (Encoding & 3) == 1;
+ return (encoding & 3) == 1;
}
}
@@ -185,40 +275,38 @@ static bool isInstructionUpdateForm(uint32_t Encoding) {
// pointer is pointing into the middle of the word we want to extract, and on
// little-endian it is pointing to the start of the word. These 2 helpers are to
// simplify reading and writing in that context.
-static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) {
- write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr);
+static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
+ write32(config->isLE ? loc : loc - 2, insn);
}
-static uint32_t readInstrFromHalf16(const uint8_t *Loc) {
- return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0));
+static uint32_t readFromHalf16(const uint8_t *loc) {
+ return read32(config->isLE ? loc : loc - 2);
}
PPC64::PPC64() {
- GotRel = R_PPC64_GLOB_DAT;
- NoneRel = R_PPC64_NONE;
- PltRel = R_PPC64_JMP_SLOT;
- RelativeRel = R_PPC64_RELATIVE;
- IRelativeRel = R_PPC64_IRELATIVE;
- GotEntrySize = 8;
- PltEntrySize = 4;
- GotPltEntrySize = 8;
- GotBaseSymInGotPlt = false;
- GotBaseSymOff = 0x8000;
- GotHeaderEntriesNum = 1;
- GotPltHeaderEntriesNum = 2;
- PltHeaderSize = 60;
- NeedsThunks = true;
-
- TlsModuleIndexRel = R_PPC64_DTPMOD64;
- TlsOffsetRel = R_PPC64_DTPREL64;
-
- TlsGotRel = R_PPC64_TPREL64;
-
- NeedsMoreStackNonSplit = false;
+ gotRel = R_PPC64_GLOB_DAT;
+ noneRel = R_PPC64_NONE;
+ pltRel = R_PPC64_JMP_SLOT;
+ relativeRel = R_PPC64_RELATIVE;
+ iRelativeRel = R_PPC64_IRELATIVE;
+ symbolicRel = R_PPC64_ADDR64;
+ pltEntrySize = 4;
+ gotBaseSymInGotPlt = false;
+ gotHeaderEntriesNum = 1;
+ gotPltHeaderEntriesNum = 2;
+ pltHeaderSize = 60;
+ needsThunks = true;
+
+ tlsModuleIndexRel = R_PPC64_DTPMOD64;
+ tlsOffsetRel = R_PPC64_DTPREL64;
+
+ tlsGotRel = R_PPC64_TPREL64;
+
+ needsMoreStackNonSplit = false;
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
- DefaultMaxPageSize = 65536;
+ defaultMaxPageSize = 65536;
// The PPC64 ELF ABI v1 spec, says:
//
@@ -228,31 +316,66 @@ PPC64::PPC64() {
//
// And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers
// use 0x10000000 as the starting address.
- DefaultImageBase = 0x10000000;
+ defaultImageBase = 0x10000000;
- write32(TrapInstr.data(), 0x7fe00008);
+ write32(trapInstr.data(), 0x7fe00008);
+}
+
+int PPC64::getTlsGdRelaxSkip(RelType type) const {
+ // A __tls_get_addr call instruction is marked with 2 relocations:
+ //
+ // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation
+ // R_PPC64_REL24: __tls_get_addr
+ //
+ // After the relaxation we no longer call __tls_get_addr and should skip both
+ // relocations to not create a false dependence on __tls_get_addr being
+ // defined.
+ if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD)
+ return 2;
+ return 1;
}
-static uint32_t getEFlags(InputFile *File) {
- if (Config->EKind == ELF64BEKind)
- return cast<ObjFile<ELF64BE>>(File)->getObj().getHeader()->e_flags;
- return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
+static uint32_t getEFlags(InputFile *file) {
+ if (config->ekind == ELF64BEKind)
+ return cast<ObjFile<ELF64BE>>(file)->getObj().getHeader()->e_flags;
+ return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags;
}
// This file implements v2 ABI. This function makes sure that all
// object files have v2 or an unspecified version as an ABI version.
uint32_t PPC64::calcEFlags() const {
- for (InputFile *F : ObjectFiles) {
- uint32_t Flag = getEFlags(F);
- if (Flag == 1)
- error(toString(F) + ": ABI version 1 is not supported");
- else if (Flag > 2)
- error(toString(F) + ": unrecognized e_flags: " + Twine(Flag));
+ for (InputFile *f : objectFiles) {
+ uint32_t flag = getEFlags(f);
+ if (flag == 1)
+ error(toString(f) + ": ABI version 1 is not supported");
+ else if (flag > 2)
+ error(toString(f) + ": unrecognized e_flags: " + Twine(flag));
}
return 2;
}
-void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (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);
+ break;
+ case R_PPC64_TOC16_LO_DS: {
+ // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or
+ // "addi reg, 2, var@toc".
+ uint32_t insn = readFromHalf16(loc);
+ 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);
+ break;
+ }
+ default:
+ llvm_unreachable("unexpected relocation type");
+ }
+}
+
+void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, 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
@@ -268,30 +391,30 @@ 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 (type) {
case R_PPC64_GOT_TLSGD16_HA:
- writeInstrFromHalf16(Loc, 0x60000000); // nop
+ writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO:
- writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13
- relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
+ writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13
+ relocateOne(loc, R_PPC64_TPREL16_HA, val);
break;
case R_PPC64_TLSGD:
- write32(Loc, 0x60000000); // nop
- write32(Loc + 4, 0x38630000); // addi r3, r3
+ write32(loc, 0x60000000); // nop
+ write32(loc + 4, 0x38630000); // addi r3, r3
// 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);
+ relocateOne(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, RelType type, 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
@@ -307,16 +430,16 @@ 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 (type) {
case R_PPC64_GOT_TLSLD16_HA:
- writeInstrFromHalf16(Loc, 0x60000000); // nop
+ writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSLD16_LO:
- writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0
+ writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0
break;
case R_PPC64_TLSLD:
- write32(Loc, 0x60000000); // nop
- write32(Loc + 4, 0x38631000); // addi r3, r3, 4096
+ write32(loc, 0x60000000); // nop
+ write32(loc + 4, 0x38631000); // addi r3, r3, 4096
break;
case R_PPC64_DTPREL16:
case R_PPC64_DTPREL16_HA:
@@ -324,19 +447,15 @@ 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:
- case R_PPC64_GOT_DTPREL16_HA:
- case R_PPC64_GOT_DTPREL16_LO_DS:
- case R_PPC64_GOT_DTPREL16_DS:
- case R_PPC64_GOT_DTPREL16_HI:
- relocateOne(Loc, Type, Val);
+ relocateOne(loc, type, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
}
-static unsigned getDFormOp(unsigned SecondaryOp) {
- switch (SecondaryOp) {
+unsigned elf::getPPCDFormOp(unsigned secondaryOp) {
+ switch (secondaryOp) {
case LBZX:
return LBZ;
case LHZX:
@@ -356,12 +475,11 @@ static unsigned getDFormOp(unsigned SecondaryOp) {
case ADD:
return ADDI;
default:
- error("unrecognized instruction for IE to LE R_PPC64_TLS");
return 0;
}
}
-void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, 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
@@ -381,26 +499,28 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// instruction, if we are accessing memory it will use any of the X-form
// indexed load or store instructions.
- unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0;
- switch (Type) {
+ unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0;
+ switch (type) {
case R_PPC64_GOT_TPREL16_HA:
- write32(Loc - Offset, 0x60000000); // nop
+ write32(loc - offset, 0x60000000); // nop
break;
case R_PPC64_GOT_TPREL16_LO_DS:
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);
+ 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);
break;
}
case R_PPC64_TLS: {
- uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc));
- if (PrimaryOp != 31)
+ uint32_t primaryOp = getPrimaryOpCode(read32(loc));
+ if (primaryOp != 31)
error("unrecognized instruction for IE to LE R_PPC64_TLS");
- uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30
- uint32_t DFormOp = getDFormOp(SecondaryOp);
- write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF)));
- relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val);
+ uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30
+ uint32_t dFormOp = getPPCDFormOp(secondaryOp);
+ 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);
break;
}
default:
@@ -409,9 +529,9 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
}
-RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_PPC64_GOT16:
case R_PPC64_GOT16_DS:
case R_PPC64_GOT16_HA:
@@ -421,16 +541,17 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
return R_GOT_OFF;
case R_PPC64_TOC16:
case R_PPC64_TOC16_DS:
- case R_PPC64_TOC16_HA:
case R_PPC64_TOC16_HI:
case R_PPC64_TOC16_LO:
- case R_PPC64_TOC16_LO_DS:
return R_GOTREL;
+ case R_PPC64_TOC16_HA:
+ case R_PPC64_TOC16_LO_DS:
+ return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL;
case R_PPC64_TOC:
- return R_PPC_TOC;
+ return R_PPC64_TOCBASE;
case R_PPC64_REL14:
case R_PPC64_REL24:
- return R_PPC_CALL_PLT;
+ return R_PPC64_CALL_PLT;
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HA:
case R_PPC64_REL32:
@@ -478,7 +599,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
case R_PPC64_DTPREL64:
- return R_ABS;
+ return R_DTPREL;
case R_PPC64_TLSGD:
return R_TLSDESC_CALL;
case R_PPC64_TLSLD:
@@ -490,115 +611,121 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
}
}
-void PPC64::writeGotHeader(uint8_t *Buf) const {
- write64(Buf, getPPC64TocBase());
+RelType PPC64::getDynRel(RelType type) const {
+ if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC)
+ return R_PPC64_ADDR64;
+ return R_PPC64_NONE;
}
-void PPC64::writePltHeader(uint8_t *Buf) const {
+void PPC64::writeGotHeader(uint8_t *buf) const {
+ write64(buf, getPPC64TocBase());
+}
+
+void PPC64::writePltHeader(uint8_t *buf) const {
// The generic resolver stub goes first.
- write32(Buf + 0, 0x7c0802a6); // mflr r0
- write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8>
- write32(Buf + 8, 0x7d6802a6); // mflr r11
- write32(Buf + 12, 0x7c0803a6); // mtlr r0
- write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12
- write32(Buf + 20, 0x380cffcc); // subi r0,r12,52
- write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2
- write32(Buf + 28, 0xe98b002c); // ld r12,44(r11)
- write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11
- write32(Buf + 36, 0xe98b0000); // ld r12,0(r11)
- write32(Buf + 40, 0xe96b0008); // ld r11,8(r11)
- write32(Buf + 44, 0x7d8903a6); // mtctr r12
- write32(Buf + 48, 0x4e800420); // bctr
+ write32(buf + 0, 0x7c0802a6); // mflr r0
+ write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8>
+ write32(buf + 8, 0x7d6802a6); // mflr r11
+ write32(buf + 12, 0x7c0803a6); // mtlr r0
+ write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12
+ write32(buf + 20, 0x380cffcc); // subi r0,r12,52
+ write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2
+ write32(buf + 28, 0xe98b002c); // ld r12,44(r11)
+ write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11
+ write32(buf + 36, 0xe98b0000); // ld r12,0(r11)
+ write32(buf + 40, 0xe96b0008); // ld r11,8(r11)
+ write32(buf + 44, 0x7d8903a6); // mtctr r12
+ write32(buf + 48, 0x4e800420); // bctr
// The 'bcl' instruction will set the link register to the address of the
// following instruction ('mflr r11'). Here we store the offset from that
// instruction to the first entry in the GotPlt section.
- int64_t GotPltOffset = In.GotPlt->getVA() - (In.Plt->getVA() + 8);
- write64(Buf + 52, GotPltOffset);
+ int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8);
+ write64(buf + 52, gotPltOffset);
}
-void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- int32_t Offset = PltHeaderSize + Index * PltEntrySize;
+void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ int32_t offset = pltHeaderSize + index * pltEntrySize;
// bl __glink_PLTresolve
- write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
+ write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc));
}
-static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
+static std::pair<RelType, uint64_t> toAddr16Rel(RelType type, uint64_t val) {
// Relocations relative to the toc-base need to be adjusted by the Toc offset.
- uint64_t TocBiasedVal = Val - PPC64TocOffset;
+ uint64_t tocBiasedVal = val - ppc64TocOffset;
// Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset.
- uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset;
+ uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset;
- switch (Type) {
+ switch (type) {
// TOC biased relocation.
case R_PPC64_GOT16:
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSLD16:
case R_PPC64_TOC16:
- return {R_PPC64_ADDR16, TocBiasedVal};
+ return {R_PPC64_ADDR16, tocBiasedVal};
case R_PPC64_GOT16_DS:
case R_PPC64_TOC16_DS:
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_DTPREL16_DS:
- return {R_PPC64_ADDR16_DS, TocBiasedVal};
+ return {R_PPC64_ADDR16_DS, tocBiasedVal};
case R_PPC64_GOT16_HA:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TPREL16_HA:
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_TOC16_HA:
- return {R_PPC64_ADDR16_HA, TocBiasedVal};
+ return {R_PPC64_ADDR16_HA, tocBiasedVal};
case R_PPC64_GOT16_HI:
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_DTPREL16_HI:
case R_PPC64_TOC16_HI:
- return {R_PPC64_ADDR16_HI, TocBiasedVal};
+ return {R_PPC64_ADDR16_HI, tocBiasedVal};
case R_PPC64_GOT16_LO:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TOC16_LO:
- return {R_PPC64_ADDR16_LO, TocBiasedVal};
+ return {R_PPC64_ADDR16_LO, tocBiasedVal};
case R_PPC64_GOT16_LO_DS:
case R_PPC64_TOC16_LO_DS:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_LO_DS:
- return {R_PPC64_ADDR16_LO_DS, TocBiasedVal};
+ return {R_PPC64_ADDR16_LO_DS, tocBiasedVal};
// Dynamic Thread pointer biased relocation types.
case R_PPC64_DTPREL16:
- return {R_PPC64_ADDR16, DTPBiasedVal};
+ return {R_PPC64_ADDR16, dtpBiasedVal};
case R_PPC64_DTPREL16_DS:
- return {R_PPC64_ADDR16_DS, DTPBiasedVal};
+ return {R_PPC64_ADDR16_DS, dtpBiasedVal};
case R_PPC64_DTPREL16_HA:
- return {R_PPC64_ADDR16_HA, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HA, dtpBiasedVal};
case R_PPC64_DTPREL16_HI:
- return {R_PPC64_ADDR16_HI, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HI, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHER:
- return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHERA:
- return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHEST:
- return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal};
case R_PPC64_DTPREL16_HIGHESTA:
- return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal};
+ return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal};
case R_PPC64_DTPREL16_LO:
- return {R_PPC64_ADDR16_LO, DTPBiasedVal};
+ return {R_PPC64_ADDR16_LO, dtpBiasedVal};
case R_PPC64_DTPREL16_LO_DS:
- return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal};
+ return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal};
case R_PPC64_DTPREL64:
- return {R_PPC64_ADDR64, DTPBiasedVal};
+ return {R_PPC64_ADDR64, dtpBiasedVal};
default:
- return {Type, Val};
+ return {type, val};
}
}
-static bool isTocOptType(RelType Type) {
- switch (Type) {
+static bool isTocOptType(RelType type) {
+ switch (type) {
case R_PPC64_GOT16_HA:
case R_PPC64_GOT16_LO_DS:
case R_PPC64_TOC16_HA:
@@ -610,66 +737,69 @@ static bool isTocOptType(RelType Type) {
}
}
-void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
+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;
- bool ShouldTocOptimize = isTocOptType(Type);
+ RelType originalType = 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.
- std::tie(Type, Val) = toAddr16Rel(Type, Val);
+ std::tie(type, val) = toAddr16Rel(type, val);
- switch (Type) {
+ switch (type) {
case R_PPC64_ADDR14: {
- checkAlignment(Loc, Val, 4, Type);
+ checkAlignment(loc, val, 4, type);
// Preserve the AA/LK bits in the branch instruction
- uint8_t AALK = Loc[3];
- write16(Loc + 2, (AALK & 3) | (Val & 0xfffc));
+ uint8_t aalk = loc[3];
+ write16(loc + 2, (aalk & 3) | (val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- case R_PPC64_TPREL16:
- checkInt(Loc, Val, 16, OriginalType);
- write16(Loc, Val);
+ checkIntUInt(loc, val, 16, originalType);
+ write16(loc, val);
+ break;
+ case R_PPC64_ADDR32:
+ checkIntUInt(loc, val, 32, originalType);
+ write32(loc, val);
break;
case R_PPC64_ADDR16_DS:
case R_PPC64_TPREL16_DS: {
- checkInt(Loc, Val, 16, OriginalType);
+ checkInt(loc, val, 16, originalType);
// 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(readInstrFromHalf16(Loc)) ? 0xF : 0x3;
- checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
- write16(Loc, (read16(Loc) & Mask) | lo(Val));
+ uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3;
+ checkAlignment(loc, lo(val), mask + 1, originalType);
+ write16(loc, (read16(loc) & mask) | lo(val));
} break;
case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
case R_PPC64_TPREL16_HA:
- if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0)
- writeInstrFromHalf16(Loc, 0x60000000);
+ if (config->tocOptimize && shouldTocOptimize && ha(val) == 0)
+ writeFromHalf16(loc, 0x60000000);
else
- write16(Loc, ha(Val));
+ write16(loc, ha(val));
break;
case R_PPC64_ADDR16_HI:
case R_PPC64_REL16_HI:
case R_PPC64_TPREL16_HI:
- write16(Loc, hi(Val));
+ write16(loc, hi(val));
break;
case R_PPC64_ADDR16_HIGHER:
case R_PPC64_TPREL16_HIGHER:
- write16(Loc, higher(Val));
+ write16(loc, higher(val));
break;
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_TPREL16_HIGHERA:
- write16(Loc, highera(Val));
+ write16(loc, highera(val));
break;
case R_PPC64_ADDR16_HIGHEST:
case R_PPC64_TPREL16_HIGHEST:
- write16(Loc, highest(Val));
+ write16(loc, highest(val));
break;
case R_PPC64_ADDR16_HIGHESTA:
case R_PPC64_TPREL16_HIGHESTA:
- write16(Loc, highesta(Val));
+ write16(loc, highesta(val));
break;
case R_PPC64_ADDR16_LO:
case R_PPC64_REL16_LO:
@@ -677,104 +807,119 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// When the high-adjusted part of a toc relocation evalutes to 0, it is
// changed into a nop. The lo part then needs to be updated to use the
// toc-pointer register r2, as the base register.
- if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
- uint32_t Instr = readInstrFromHalf16(Loc);
- if (isInstructionUpdateForm(Instr))
- error(getErrorLocation(Loc) +
+ if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) {
+ uint32_t insn = readFromHalf16(loc);
+ if (isInstructionUpdateForm(insn))
+ error(getErrorLocation(loc) +
"can't toc-optimize an update instruction: 0x" +
- utohexstr(Instr));
- Instr = (Instr & 0xFFE00000) | 0x00020000;
- writeInstrFromHalf16(Loc, Instr);
+ utohexstr(insn));
+ writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val));
+ } else {
+ write16(loc, lo(val));
}
- write16(Loc, lo(Val));
break;
case R_PPC64_ADDR16_LO_DS:
case R_PPC64_TPREL16_LO_DS: {
// DQ-form instructions use bits 28-31 as part of the instruction encoding
// DS-form instructions only use bits 30-31.
- uint32_t Inst = readInstrFromHalf16(Loc);
- uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3;
- checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
- if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
+ uint32_t insn = readFromHalf16(loc);
+ uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3;
+ checkAlignment(loc, lo(val), mask + 1, originalType);
+ if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) {
// When the high-adjusted part of a toc relocation evalutes to 0, it is
// changed into a nop. The lo part then needs to be updated to use the toc
// pointer register r2, as the base register.
- if (isInstructionUpdateForm(Inst))
- error(getErrorLocation(Loc) +
+ if (isInstructionUpdateForm(insn))
+ error(getErrorLocation(loc) +
"Can't toc-optimize an update instruction: 0x" +
- Twine::utohexstr(Inst));
- Inst = (Inst & 0xFFE0000F) | 0x00020000;
- writeInstrFromHalf16(Loc, Inst);
+ Twine::utohexstr(insn));
+ insn &= 0xffe00000 | mask;
+ writeFromHalf16(loc, insn | 0x00020000 | lo(val));
+ } else {
+ write16(loc, (read16(loc) & mask) | lo(val));
}
- write16(Loc, (read16(Loc) & Mask) | lo(Val));
} break;
- case R_PPC64_ADDR32:
+ case R_PPC64_TPREL16:
+ checkInt(loc, val, 16, originalType);
+ write16(loc, val);
+ break;
case R_PPC64_REL32:
- checkInt(Loc, Val, 32, Type);
- write32(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32(loc, val);
break;
case R_PPC64_ADDR64:
case R_PPC64_REL64:
case R_PPC64_TOC:
- write64(Loc, Val);
+ write64(loc, val);
break;
case R_PPC64_REL14: {
- uint32_t Mask = 0x0000FFFC;
- checkInt(Loc, Val, 16, Type);
- checkAlignment(Loc, Val, 4, Type);
- write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
+ uint32_t mask = 0x0000FFFC;
+ checkInt(loc, val, 16, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
case R_PPC64_REL24: {
- uint32_t Mask = 0x03FFFFFC;
- checkInt(Loc, Val, 26, Type);
- checkAlignment(Loc, Val, 4, Type);
- write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
+ uint32_t mask = 0x03FFFFFC;
+ checkInt(loc, val, 26, type);
+ checkAlignment(loc, val, 4, type);
+ write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
case R_PPC64_DTPREL64:
- write64(Loc, Val - DynamicThreadPointerOffset);
+ write64(loc, val - dynamicThreadPointerOffset);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
}
}
-bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
- if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24)
+bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
+ if (type != R_PPC64_REL14 && type != R_PPC64_REL24)
return false;
// If a function is in the Plt it needs to be called with a call-stub.
- if (S.isInPlt())
+ if (s.isInPlt())
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)
+ if (s.isUndefWeak() && !config->shared)
return false;
// If the offset exceeds the range of the branch type then it will need
// a range-extending thunk.
- return !inBranchRange(Type, BranchAddr, S.getVA());
+ // See the comment in getRelocTargetVA() about R_PPC64_CALL.
+ return !inBranchRange(type, branchAddr,
+ s.getVA() +
+ getPPC64GlobalEntryToLocalEntryOffset(s.stOther));
}
-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)
- return isInt<26>(Offset);
+uint32_t PPC64::getThunkSectionSpacing() const {
+ // See comment in Arch/ARM.cpp for a more detailed explanation of
+ // getThunkSectionSpacing(). For PPC64 we pick the constant here based on
+ // R_PPC64_REL24, which is used by unconditional branch instructions.
+ // 0x2000000 = (1 << 24-1) * 4
+ return 0x2000000;
+}
+
+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)
+ return isInt<26>(offset);
llvm_unreachable("unsupported relocation type used in branch");
}
-RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- if (Expr == R_RELAX_TLS_GD_TO_IE)
+RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ if (expr == R_RELAX_TLS_GD_TO_IE)
return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
- if (Expr == R_RELAX_TLS_LD_TO_LE)
+ if (expr == R_RELAX_TLS_LD_TO_LE)
return R_RELAX_TLS_LD_TO_LE_ABS;
- return Expr;
+ return expr;
}
// Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement.
@@ -794,24 +939,25 @@ 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, RelType type, uint64_t val) const {
+ switch (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);
+ relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val);
return;
+ case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO: {
// Relax from addi r3, rA, sym@got@tlsgd@l to
// ld r3, sym@got@tprel@l(rA)
- uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16));
- writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister);
- relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val);
+ uint32_t ra = (readFromHalf16(loc) & (0x1f << 16));
+ writeFromHalf16(loc, 0xe8600000 | ra);
+ relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
return;
}
case R_PPC64_TLSGD:
- write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop
- write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
+ write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop
+ write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
return;
default:
llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
@@ -846,86 +992,86 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// pair by split-stack-size-adjust.
// addis r12, r1, ha(-stack-frame size - split-stack-adjust-size)
// addi r12, r12, l(-stack-frame size - split-stack-adjust-size)
-bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const {
+bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const {
// If the caller has a global entry point adjust the buffer past it. The start
// of the split-stack prologue will be at the local entry point.
- Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther);
+ loc += getPPC64GlobalEntryToLocalEntryOffset(stOther);
// At the very least we expect to see a load of some split-stack data from the
// tcb, and 2 instructions that calculate the ending stack address this
// function will require. If there is not enough room for at least 3
// instructions it can't be a split-stack prologue.
- if (Loc + 12 >= End)
+ if (loc + 12 >= end)
return false;
// First instruction must be `ld r0, -0x7000-64(r13)`
- if (read32(Loc) != 0xe80d8fc0)
+ if (read32(loc) != 0xe80d8fc0)
return false;
- int16_t HiImm = 0;
- int16_t LoImm = 0;
+ int16_t hiImm = 0;
+ int16_t loImm = 0;
// First instruction can be either an addis if the frame size is larger then
// 32K, or an addi if the size is less then 32K.
- int32_t FirstInstr = read32(Loc + 4);
- if (getPrimaryOpCode(FirstInstr) == 15) {
- HiImm = FirstInstr & 0xFFFF;
- } else if (getPrimaryOpCode(FirstInstr) == 14) {
- LoImm = FirstInstr & 0xFFFF;
+ int32_t firstInstr = read32(loc + 4);
+ if (getPrimaryOpCode(firstInstr) == 15) {
+ hiImm = firstInstr & 0xFFFF;
+ } else if (getPrimaryOpCode(firstInstr) == 14) {
+ loImm = firstInstr & 0xFFFF;
} else {
return false;
}
// Second instruction is either an addi or a nop. If the first instruction was
// an addi then LoImm is set and the second instruction must be a nop.
- uint32_t SecondInstr = read32(Loc + 8);
- if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) {
- LoImm = SecondInstr & 0xFFFF;
- } else if (SecondInstr != 0x60000000) {
+ uint32_t secondInstr = read32(loc + 8);
+ if (!loImm && getPrimaryOpCode(secondInstr) == 14) {
+ loImm = secondInstr & 0xFFFF;
+ } else if (secondInstr != 0x60000000) {
return false;
}
// The register operands of the first instruction should be the stack-pointer
// (r1) as the input (RA) and r12 as the output (RT). If the second
// instruction is not a nop, then it should use r12 as both input and output.
- auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT,
- uint8_t ExpectedRA) {
- return ((Instr & 0x3E00000) >> 21 == ExpectedRT) &&
- ((Instr & 0x1F0000) >> 16 == ExpectedRA);
+ auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT,
+ uint8_t expectedRA) {
+ return ((instr & 0x3E00000) >> 21 == expectedRT) &&
+ ((instr & 0x1F0000) >> 16 == expectedRA);
};
- if (!CheckRegOperands(FirstInstr, 12, 1))
+ if (!checkRegOperands(firstInstr, 12, 1))
return false;
- if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12))
+ if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12))
return false;
- int32_t StackFrameSize = (HiImm * 65536) + LoImm;
+ int32_t stackFrameSize = (hiImm * 65536) + loImm;
// Check that the adjusted size doesn't overflow what we can represent with 2
// instructions.
- if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) {
- error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows");
+ if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) {
+ error(getErrorLocation(loc) + "split-stack prologue adjustment overflows");
return false;
}
- int32_t AdjustedStackFrameSize =
- StackFrameSize - Config->SplitStackAdjustSize;
+ int32_t adjustedStackFrameSize =
+ stackFrameSize - config->splitStackAdjustSize;
- LoImm = AdjustedStackFrameSize & 0xFFFF;
- HiImm = (AdjustedStackFrameSize + 0x8000) >> 16;
- if (HiImm) {
- write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm);
+ loImm = adjustedStackFrameSize & 0xFFFF;
+ hiImm = (adjustedStackFrameSize + 0x8000) >> 16;
+ if (hiImm) {
+ write32(loc + 4, 0x3D810000 | (uint16_t)hiImm);
// If the low immediate is zero the second instruction will be a nop.
- SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000;
- write32(Loc + 8, SecondInstr);
+ secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000;
+ write32(loc + 8, secondInstr);
} else {
// addi r12, r1, imm
- write32(Loc + 4, (0x39810000) | (uint16_t)LoImm);
- write32(Loc + 8, 0x60000000);
+ write32(loc + 4, (0x39810000) | (uint16_t)loImm);
+ write32(loc + 8, 0x60000000);
}
return true;
}
TargetInfo *elf::getPPC64TargetInfo() {
- static PPC64 Target;
- return &Target;
+ static PPC64 target;
+ return &target;
}
diff --git a/ELF/Arch/RISCV.cpp b/ELF/Arch/RISCV.cpp
index 461e8d35c3e6..6f16ade57177 100644
--- a/ELF/Arch/RISCV.cpp
+++ b/ELF/Arch/RISCV.cpp
@@ -1,13 +1,13 @@
//===- RISCV.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "SyntheticSections.h"
#include "Target.h"
using namespace llvm;
@@ -23,59 +23,207 @@ class RISCV final : public TargetInfo {
public:
RISCV();
uint32_t calcEFlags() 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 writeGotHeader(uint8_t *buf) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ 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;
};
} // end anonymous namespace
-RISCV::RISCV() { NoneRel = R_RISCV_NONE; }
+const uint64_t dtpOffset = 0x800;
+
+enum Op {
+ ADDI = 0x13,
+ AUIPC = 0x17,
+ JALR = 0x67,
+ LD = 0x3003,
+ LW = 0x2003,
+ SRLI = 0x5013,
+ SUB = 0x40000033,
+};
+
+enum Reg {
+ X_RA = 1,
+ X_T0 = 5,
+ X_T1 = 6,
+ X_T2 = 7,
+ X_T3 = 28,
+};
+
+static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
+static uint32_t lo12(uint32_t val) { return val & 4095; }
+
+static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) {
+ return op | (rd << 7) | (rs1 << 15) | (imm << 20);
+}
+static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) {
+ return op | (rd << 7) | (rs1 << 15) | (rs2 << 20);
+}
+static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) {
+ return op | (rd << 7) | (imm << 12);
+}
+
+RISCV::RISCV() {
+ copyRel = R_RISCV_COPY;
+ noneRel = R_RISCV_NONE;
+ pltRel = R_RISCV_JUMP_SLOT;
+ relativeRel = R_RISCV_RELATIVE;
+ if (config->is64) {
+ symbolicRel = R_RISCV_64;
+ tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64;
+ tlsOffsetRel = R_RISCV_TLS_DTPREL64;
+ tlsGotRel = R_RISCV_TLS_TPREL64;
+ } else {
+ symbolicRel = R_RISCV_32;
+ tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32;
+ tlsOffsetRel = R_RISCV_TLS_DTPREL32;
+ tlsGotRel = R_RISCV_TLS_TPREL32;
+ }
+ gotRel = symbolicRel;
+
+ // .got[0] = _DYNAMIC
+ gotBaseSymInGotPlt = false;
+ gotHeaderEntriesNum = 1;
+
+ // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map
+ gotPltHeaderEntriesNum = 2;
-static uint32_t getEFlags(InputFile *F) {
- if (Config->Is64)
- return cast<ObjFile<ELF64LE>>(F)->getObj().getHeader()->e_flags;
- return cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
+ pltEntrySize = 16;
+ pltHeaderSize = 32;
+}
+
+static uint32_t getEFlags(InputFile *f) {
+ if (config->is64)
+ return cast<ObjFile<ELF64LE>>(f)->getObj().getHeader()->e_flags;
+ return cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags;
}
uint32_t RISCV::calcEFlags() const {
- assert(!ObjectFiles.empty());
+ assert(!objectFiles.empty());
- uint32_t Target = getEFlags(ObjectFiles.front());
+ uint32_t target = getEFlags(objectFiles.front());
- for (InputFile *F : ObjectFiles) {
- uint32_t EFlags = getEFlags(F);
- if (EFlags & EF_RISCV_RVC)
- Target |= EF_RISCV_RVC;
+ for (InputFile *f : objectFiles) {
+ uint32_t eflags = getEFlags(f);
+ if (eflags & EF_RISCV_RVC)
+ target |= EF_RISCV_RVC;
- if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI))
- error(toString(F) +
+ if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI))
+ error(toString(f) +
": cannot link object files with different floating-point ABI");
- if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE))
- error(toString(F) +
+ if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE))
+ error(toString(f) +
": cannot link object files with different EF_RISCV_RVE");
}
- return Target;
+ return target;
+}
+
+void RISCV::writeGotHeader(uint8_t *buf) const {
+ if (config->is64)
+ write64le(buf, mainPart->dynamic->getVA());
+ else
+ write32le(buf, mainPart->dynamic->getVA());
+}
+
+void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ if (config->is64)
+ write64le(buf, in.plt->getVA());
+ else
+ write32le(buf, in.plt->getVA());
+}
+
+void RISCV::writePltHeader(uint8_t *buf) const {
+ // 1: auipc t2, %pcrel_hi(.got.plt)
+ // sub t1, t1, t3
+ // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
+ // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0]
+ // addi t0, t2, %pcrel_lo(1b)
+ // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
+ // l[wd] t0, Wordsize(t0); t0 = link_map
+ // jr t3
+ uint32_t offset = in.gotPlt->getVA() - in.plt->getVA();
+ uint32_t load = config->is64 ? LD : LW;
+ write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset)));
+ write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3));
+ write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset)));
+ write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12));
+ write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset)));
+ write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2));
+ write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize));
+ write32le(buf + 28, itype(JALR, 0, X_T3, 0));
}
-RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ // 1: auipc t3, %pcrel_hi(f@.got.plt)
+ // l[wd] t3, %pcrel_lo(1b)(t3)
+ // jalr t1, t3
+ // nop
+ uint32_t offset = gotPltEntryAddr - pltEntryAddr;
+ write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
+ write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset)));
+ write32le(buf + 8, itype(JALR, X_T1, X_T3, 0));
+ write32le(buf + 12, itype(ADDI, 0, 0, 0));
+}
+
+RelType RISCV::getDynRel(RelType type) const {
+ return type == target->symbolicRel ? type
+ : static_cast<RelType>(R_RISCV_NONE);
+}
+
+RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
+ case R_RISCV_ADD8:
+ case R_RISCV_ADD16:
+ case R_RISCV_ADD32:
+ case R_RISCV_ADD64:
+ case R_RISCV_SET6:
+ case R_RISCV_SET8:
+ case R_RISCV_SET16:
+ case R_RISCV_SET32:
+ case R_RISCV_SUB6:
+ case R_RISCV_SUB8:
+ case R_RISCV_SUB16:
+ case R_RISCV_SUB32:
+ case R_RISCV_SUB64:
+ return R_RISCV_ADD;
case R_RISCV_JAL:
case R_RISCV_BRANCH:
- case R_RISCV_CALL:
case R_RISCV_PCREL_HI20:
case R_RISCV_RVC_BRANCH:
case R_RISCV_RVC_JUMP:
case R_RISCV_32_PCREL:
return R_PC;
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT:
+ return R_PLT_PC;
+ case R_RISCV_GOT_HI20:
+ return R_GOT_PC;
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
return R_RISCV_PC_INDIRECT;
+ case R_RISCV_TLS_GD_HI20:
+ return R_TLSGD_PC;
+ case R_RISCV_TLS_GOT_HI20:
+ config->hasStaticTlsModel = true;
+ return R_GOT_PC;
+ case R_RISCV_TPREL_HI20:
+ case R_RISCV_TPREL_LO12_I:
+ case R_RISCV_TPREL_LO12_S:
+ return R_TLS;
case R_RISCV_RELAX:
case R_RISCV_ALIGN:
+ case R_RISCV_TPREL_ADD:
return R_HINT;
default:
return R_ABS;
@@ -83,175 +231,190 @@ RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S,
}
// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63.
-static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) {
- return (V & ((1ULL << (Begin + 1)) - 1)) >> End;
+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 {
- switch (Type) {
+void RISCV::relocateOne(uint8_t *loc, const RelType type,
+ const uint64_t val) const {
+ const unsigned bits = config->wordsize * 8;
+
+ switch (type) {
case R_RISCV_32:
- write32le(Loc, Val);
+ write32le(loc, val);
return;
case R_RISCV_64:
- write64le(Loc, Val);
+ write64le(loc, val);
return;
case R_RISCV_RVC_BRANCH: {
- checkInt(Loc, static_cast<int64_t>(Val) >> 1, 8, Type);
- checkAlignment(Loc, Val, 2, Type);
- uint16_t Insn = read16le(Loc) & 0xE383;
- uint16_t Imm8 = extractBits(Val, 8, 8) << 12;
- uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10;
- uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5;
- uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3;
- uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
- Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5;
-
- write16le(Loc, Insn);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 8, type);
+ checkAlignment(loc, val, 2, type);
+ uint16_t insn = read16le(loc) & 0xE383;
+ uint16_t imm8 = extractBits(val, 8, 8) << 12;
+ uint16_t imm4_3 = extractBits(val, 4, 3) << 10;
+ uint16_t imm7_6 = extractBits(val, 7, 6) << 5;
+ uint16_t imm2_1 = extractBits(val, 2, 1) << 3;
+ uint16_t imm5 = extractBits(val, 5, 5) << 2;
+ insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5;
+
+ write16le(loc, insn);
return;
}
case R_RISCV_RVC_JUMP: {
- checkInt(Loc, static_cast<int64_t>(Val) >> 1, 11, Type);
- checkAlignment(Loc, Val, 2, Type);
- uint16_t Insn = read16le(Loc) & 0xE003;
- uint16_t Imm11 = extractBits(Val, 11, 11) << 12;
- uint16_t Imm4 = extractBits(Val, 4, 4) << 11;
- uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9;
- uint16_t Imm10 = extractBits(Val, 10, 10) << 8;
- uint16_t Imm6 = extractBits(Val, 6, 6) << 7;
- uint16_t Imm7 = extractBits(Val, 7, 7) << 6;
- uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3;
- uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
- Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5;
-
- write16le(Loc, Insn);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 11, type);
+ checkAlignment(loc, val, 2, type);
+ uint16_t insn = read16le(loc) & 0xE003;
+ uint16_t imm11 = extractBits(val, 11, 11) << 12;
+ uint16_t imm4 = extractBits(val, 4, 4) << 11;
+ uint16_t imm9_8 = extractBits(val, 9, 8) << 9;
+ uint16_t imm10 = extractBits(val, 10, 10) << 8;
+ uint16_t imm6 = extractBits(val, 6, 6) << 7;
+ uint16_t imm7 = extractBits(val, 7, 7) << 6;
+ uint16_t imm3_1 = extractBits(val, 3, 1) << 3;
+ uint16_t imm5 = extractBits(val, 5, 5) << 2;
+ insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5;
+
+ write16le(loc, insn);
return;
}
case R_RISCV_RVC_LUI: {
- int32_t Imm = ((Val + 0x800) >> 12);
- checkUInt(Loc, Imm, 6, Type);
- if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
- write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000);
+ int64_t imm = SignExtend64(val + 0x800, bits) >> 12;
+ checkInt(loc, imm, 6, type);
+ if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
+ write16le(loc, (read16le(loc) & 0x0F83) | 0x4000);
} else {
- uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12;
- uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2;
- write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12);
+ uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12;
+ uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2;
+ write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12);
}
return;
}
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, type);
+ checkAlignment(loc, val, 2, type);
- uint32_t Insn = read32le(Loc) & 0xFFF;
- uint32_t Imm20 = extractBits(Val, 20, 20) << 31;
- uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21;
- uint32_t Imm11 = extractBits(Val, 11, 11) << 20;
- uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12;
- Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12;
+ uint32_t insn = read32le(loc) & 0xFFF;
+ uint32_t imm20 = extractBits(val, 20, 20) << 31;
+ uint32_t imm10_1 = extractBits(val, 10, 1) << 21;
+ uint32_t imm11 = extractBits(val, 11, 11) << 20;
+ uint32_t imm19_12 = extractBits(val, 19, 12) << 12;
+ insn |= imm20 | imm10_1 | imm11 | imm19_12;
- write32le(Loc, Insn);
+ write32le(loc, insn);
return;
}
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, type);
+ checkAlignment(loc, val, 2, type);
- uint32_t Insn = read32le(Loc) & 0x1FFF07F;
- uint32_t Imm12 = extractBits(Val, 12, 12) << 31;
- uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25;
- uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8;
- uint32_t Imm11 = extractBits(Val, 11, 11) << 7;
- Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11;
+ uint32_t insn = read32le(loc) & 0x1FFF07F;
+ uint32_t imm12 = extractBits(val, 12, 12) << 31;
+ uint32_t imm10_5 = extractBits(val, 10, 5) << 25;
+ uint32_t imm4_1 = extractBits(val, 4, 1) << 8;
+ uint32_t imm11 = extractBits(val, 11, 11) << 7;
+ insn |= imm12 | imm10_5 | imm4_1 | imm11;
- write32le(Loc, Insn);
+ write32le(loc, insn);
return;
}
// auipc + jalr pair
- case R_RISCV_CALL: {
- checkInt(Loc, Val, 32, Type);
- if (isInt<32>(Val)) {
- relocateOne(Loc, R_RISCV_PCREL_HI20, Val);
- relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val);
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT: {
+ int64_t hi = SignExtend64(val + 0x800, bits) >> 12;
+ checkInt(loc, hi, 20, type);
+ if (isInt<20>(hi)) {
+ relocateOne(loc, R_RISCV_PCREL_HI20, val);
+ relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val);
}
return;
}
+ case R_RISCV_GOT_HI20:
case R_RISCV_PCREL_HI20:
+ case R_RISCV_TLS_GD_HI20:
+ case R_RISCV_TLS_GOT_HI20:
+ case R_RISCV_TPREL_HI20:
case R_RISCV_HI20: {
- checkInt(Loc, Val, 32, Type);
- uint32_t Hi = Val + 0x800;
- write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000));
+ uint64_t hi = val + 0x800;
+ checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type);
+ write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
return;
}
case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_TPREL_LO12_I:
case R_RISCV_LO12_I: {
- checkInt(Loc, Val, 32, Type);
- uint32_t Hi = Val + 0x800;
- uint32_t Lo = Val - (Hi & 0xFFFFF000);
- write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20));
+ uint64_t hi = (val + 0x800) >> 12;
+ uint64_t lo = val - (hi << 12);
+ write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20));
return;
}
case R_RISCV_PCREL_LO12_S:
+ case R_RISCV_TPREL_LO12_S:
case R_RISCV_LO12_S: {
- checkInt(Loc, Val, 32, Type);
- uint32_t Hi = Val + 0x800;
- uint32_t Lo = Val - (Hi & 0xFFFFF000);
- uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25;
- uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7;
- write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0);
+ uint64_t hi = (val + 0x800) >> 12;
+ uint64_t lo = val - (hi << 12);
+ uint32_t imm11_5 = extractBits(lo, 11, 5) << 25;
+ uint32_t imm4_0 = extractBits(lo, 4, 0) << 7;
+ write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0);
return;
}
case R_RISCV_ADD8:
- *Loc += Val;
+ *loc += val;
return;
case R_RISCV_ADD16:
- write16le(Loc, read16le(Loc) + Val);
+ write16le(loc, read16le(loc) + val);
return;
case R_RISCV_ADD32:
- write32le(Loc, read32le(Loc) + Val);
+ write32le(loc, read32le(loc) + val);
return;
case R_RISCV_ADD64:
- write64le(Loc, read64le(Loc) + Val);
+ write64le(loc, read64le(loc) + val);
return;
case R_RISCV_SUB6:
- *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f);
+ *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f);
return;
case R_RISCV_SUB8:
- *Loc -= Val;
+ *loc -= val;
return;
case R_RISCV_SUB16:
- write16le(Loc, read16le(Loc) - Val);
+ write16le(loc, read16le(loc) - val);
return;
case R_RISCV_SUB32:
- write32le(Loc, read32le(Loc) - Val);
+ write32le(loc, read32le(loc) - val);
return;
case R_RISCV_SUB64:
- write64le(Loc, read64le(Loc) - Val);
+ write64le(loc, read64le(loc) - val);
return;
case R_RISCV_SET6:
- *Loc = (*Loc & 0xc0) | (Val & 0x3f);
+ *loc = (*loc & 0xc0) | (val & 0x3f);
return;
case R_RISCV_SET8:
- *Loc = Val;
+ *loc = val;
return;
case R_RISCV_SET16:
- write16le(Loc, Val);
+ write16le(loc, val);
return;
case R_RISCV_SET32:
case R_RISCV_32_PCREL:
- write32le(Loc, Val);
+ write32le(loc, val);
return;
+ case R_RISCV_TLS_DTPREL32:
+ write32le(loc, val - dtpOffset);
+ break;
+ case R_RISCV_TLS_DTPREL64:
+ write64le(loc, val - dtpOffset);
+ break;
+
case R_RISCV_ALIGN:
case R_RISCV_RELAX:
return; // Ignored (for now)
@@ -267,13 +430,13 @@ void RISCV::relocateOne(uint8_t *Loc, const RelType Type,
case R_RISCV_GPREL_I:
case R_RISCV_GPREL_S:
default:
- error(getErrorLocation(Loc) +
- "unimplemented relocation: " + toString(Type));
+ error(getErrorLocation(loc) +
+ "unimplemented relocation: " + toString(type));
return;
}
}
TargetInfo *elf::getRISCVTargetInfo() {
- static RISCV Target;
- return &Target;
+ static RISCV target;
+ return &target;
}
diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp
index 831aa2028e7f..5299206dd919 100644
--- a/ELF/Arch/SPARCV9.cpp
+++ b/ELF/Arch/SPARCV9.cpp
@@ -1,9 +1,8 @@
//===- SPARCV9.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -24,32 +23,32 @@ namespace {
class SPARCV9 final : public TargetInfo {
public:
SPARCV9();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
};
} // namespace
SPARCV9::SPARCV9() {
- CopyRel = R_SPARC_COPY;
- GotRel = R_SPARC_GLOB_DAT;
- NoneRel = R_SPARC_NONE;
- PltRel = R_SPARC_JMP_SLOT;
- RelativeRel = R_SPARC_RELATIVE;
- GotEntrySize = 8;
- PltEntrySize = 32;
- PltHeaderSize = 4 * PltEntrySize;
+ copyRel = R_SPARC_COPY;
+ gotRel = R_SPARC_GLOB_DAT;
+ noneRel = R_SPARC_NONE;
+ pltRel = R_SPARC_JMP_SLOT;
+ relativeRel = R_SPARC_RELATIVE;
+ symbolicRel = R_SPARC_64;
+ pltEntrySize = 32;
+ pltHeaderSize = 4 * pltEntrySize;
- PageSize = 8192;
- DefaultMaxPageSize = 0x100000;
- DefaultImageBase = 0x100000;
+ defaultCommonPageSize = 8192;
+ defaultMaxPageSize = 0x100000;
+ defaultImageBase = 0x100000;
}
-RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ switch (type) {
case R_SPARC_32:
case R_SPARC_UA32:
case R_SPARC_64:
@@ -69,64 +68,65 @@ RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S,
case R_SPARC_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
- checkUInt(Loc, Val, 32, Type);
- write32be(Loc, Val);
+ checkUInt(loc, val, 32, type);
+ write32be(loc, val);
break;
case R_SPARC_DISP32:
// V-disp32
- checkInt(Loc, Val, 32, Type);
- write32be(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32be(loc, val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
- checkInt(Loc, Val, 32, Type);
- write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
+ checkInt(loc, val, 32, type);
+ write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
- checkUInt(Loc, Val, 22, Type);
- write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff));
+ checkUInt(loc, val, 22, type);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
break;
case R_SPARC_GOT22:
case R_SPARC_PC22:
// T-imm22
- write32be(Loc, (read32be(Loc) & ~0x003fffff) | ((Val >> 10) & 0x003fffff));
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
break;
case R_SPARC_WDISP19:
// V-disp19
- checkInt(Loc, Val, 21, Type);
- write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff));
+ checkInt(loc, val, 21, type);
+ write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
case R_SPARC_PC10:
// T-simm10
- write32be(Loc, (read32be(Loc) & ~0x000003ff) | (Val & 0x000003ff));
+ write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
break;
case R_SPARC_64:
case R_SPARC_UA64:
- case R_SPARC_GLOB_DAT:
// V-xword64
- write64be(Loc, Val);
+ write64be(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t PltData[] = {
+void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t pltData[] = {
0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1
0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1
0x01, 0x00, 0x00, 0x00, // nop
@@ -136,14 +136,14 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
0x01, 0x00, 0x00, 0x00, // nop
0x01, 0x00, 0x00, 0x00 // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
+ memcpy(buf, pltData, sizeof(pltData));
- uint64_t Off = getPltEntryOffset(Index);
- relocateOne(Buf, R_SPARC_22, Off);
- relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
+ uint64_t off = pltHeaderSize + pltEntrySize * index;
+ relocateOne(buf, R_SPARC_22, off);
+ relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
}
TargetInfo *elf::getSPARCV9TargetInfo() {
- static SPARCV9 Target;
- return &Target;
+ static SPARCV9 target;
+ return &target;
}
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index e910375d2fc7..e1dd231e8e8d 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -1,9 +1,8 @@
//===- X86.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -24,63 +23,73 @@ namespace {
class X86 : public TargetInfo {
public:
X86();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- void writeGotPltHeader(uint8_t *Buf) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, 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;
+ int getTlsGdRelaxSkip(RelType type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ void writeGotPltHeader(uint8_t *buf) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, 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;
};
} // namespace
X86::X86() {
- CopyRel = R_386_COPY;
- GotRel = R_386_GLOB_DAT;
- NoneRel = R_386_NONE;
- PltRel = R_386_JUMP_SLOT;
- IRelativeRel = R_386_IRELATIVE;
- RelativeRel = R_386_RELATIVE;
- TlsGotRel = R_386_TLS_TPOFF;
- TlsModuleIndexRel = R_386_TLS_DTPMOD32;
- TlsOffsetRel = R_386_TLS_DTPOFF32;
- GotEntrySize = 4;
- GotPltEntrySize = 4;
- PltEntrySize = 16;
- PltHeaderSize = 16;
- TlsGdRelaxSkip = 2;
- TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+ copyRel = R_386_COPY;
+ gotRel = R_386_GLOB_DAT;
+ noneRel = R_386_NONE;
+ pltRel = R_386_JUMP_SLOT;
+ iRelativeRel = R_386_IRELATIVE;
+ relativeRel = R_386_RELATIVE;
+ symbolicRel = R_386_32;
+ tlsGotRel = R_386_TLS_TPOFF;
+ tlsModuleIndexRel = R_386_TLS_DTPMOD32;
+ tlsOffsetRel = R_386_TLS_DTPOFF32;
+ pltEntrySize = 16;
+ pltHeaderSize = 16;
+ trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
// Align to the non-PAE large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
- DefaultImageBase = 0x400000;
+ defaultImageBase = 0x400000;
}
-static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; }
+int X86::getTlsGdRelaxSkip(RelType type) const {
+ return 2;
+}
-RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+RelExpr X86::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ // There are 4 different TLS variable models with varying degrees of
+ // flexibility and performance. LocalExec and InitialExec models are fast but
+ // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the
+ // dynamic section to let runtime know about that.
+ if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE ||
+ type == R_386_TLS_GOTIE)
+ config->hasStaticTlsModel = true;
+
+ switch (type) {
case R_386_8:
case R_386_16:
case R_386_32:
- case R_386_TLS_LDO_32:
return R_ABS;
+ case R_386_TLS_LDO_32:
+ return R_DTPREL;
case R_386_TLS_GD:
- return R_TLSGD_GOT_FROM_END;
+ return R_TLSGD_GOTPLT;
case R_386_TLS_LDM:
- return R_TLSLD_GOT_FROM_END;
+ return R_TLSLD_GOTPLT;
case R_386_PLT32:
return R_PLT_PC;
case R_386_PC8:
@@ -88,7 +97,7 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
case R_386_PC32:
return R_PC;
case R_386_GOTPC:
- return R_GOTONLY_PC_FROM_END;
+ return R_GOTPLTONLY_PC;
case R_386_TLS_IE:
return R_GOT;
case R_386_GOT32:
@@ -108,14 +117,14 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
// load an GOT address to a register, which is usually %ebx.
//
// So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or
- // foo@GOT(%reg).
+ // foo@GOT(%ebx).
//
// foo@GOT is not usable in PIC. If we are creating a PIC output and if we
// find such relocation, we should report an error. foo@GOT is resolved to
// an *absolute* address of foo's GOT entry, because both GOT address and
// foo's offset are known. In other words, it's G + A.
//
- // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to
+ // foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to
// foo's GOT entry in the table, because GOT address is not known but foo's
// offset in the table is known. It's G + A - GOT.
//
@@ -123,16 +132,16 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
// different use cases. In order to distinguish them, we have to read a
// machine instruction.
//
- // The following code implements it. We assume that Loc[0] is the first
- // byte of a displacement or an immediate field of a valid machine
+ // The following code implements it. We assume that Loc[0] is the first byte
+ // of a displacement or an immediate field of a valid machine
// instruction. That means a ModRM byte is at Loc[-1]. By taking a look at
- // the byte, we can determine whether the instruction is register-relative
- // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT).
- return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT;
+ // the byte, we can determine whether the instruction uses the operand as an
+ // absolute address (R_GOT) or a register-relative address (R_GOTPLT).
+ return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT;
case R_386_TLS_GOTIE:
- return R_GOT_FROM_END;
+ return R_GOTPLT;
case R_386_GOTOFF:
- return R_GOTREL_FROM_END;
+ return R_GOTPLTREL;
case R_386_TLS_LE:
return R_TLS;
case R_386_TLS_LE_32:
@@ -140,105 +149,102 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
case R_386_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- switch (Expr) {
+RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ switch (expr) {
default:
- return Expr;
+ return expr;
case R_RELAX_TLS_GD_TO_IE:
- return R_RELAX_TLS_GD_TO_IE_END;
+ return R_RELAX_TLS_GD_TO_IE_GOTPLT;
case R_RELAX_TLS_GD_TO_LE:
return R_RELAX_TLS_GD_TO_LE_NEG;
}
}
-void X86::writeGotPltHeader(uint8_t *Buf) const {
- write32le(Buf, In.Dynamic->getVA());
+void X86::writeGotPltHeader(uint8_t *buf) const {
+ write32le(buf, mainPart->dynamic->getVA());
}
-void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
+void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const {
// Entries in .got.plt initially points back to the corresponding
// PLT entries with a fixed offset to skip the first instruction.
- write32le(Buf, S.getPltVA() + 6);
+ write32le(buf, s.getPltVA() + 6);
}
-void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
+void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
// An x86 entry is the address of the ifunc resolver function.
- write32le(Buf, S.getVA());
+ write32le(buf, s.getVA());
}
-RelType X86::getDynRel(RelType Type) const {
- if (Type == R_386_TLS_LE)
+RelType X86::getDynRel(RelType type) const {
+ if (type == R_386_TLS_LE)
return R_386_TLS_TPOFF;
- if (Type == R_386_TLS_LE_32)
+ if (type == R_386_TLS_LE_32)
return R_386_TLS_TPOFF32;
- return Type;
+ return type;
}
-void X86::writePltHeader(uint8_t *Buf) const {
- if (Config->Pic) {
- const uint8_t V[] = {
- 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx)
- 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx)
+void X86::writePltHeader(uint8_t *buf) const {
+ if (config->isPic) {
+ const uint8_t v[] = {
+ 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx)
+ 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx)
0x90, 0x90, 0x90, 0x90 // nop
};
- memcpy(Buf, V, sizeof(V));
-
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 8, GotPlt + 8);
+ memcpy(buf, v, sizeof(v));
return;
}
- const uint8_t PltData[] = {
+ const uint8_t pltData[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
0x90, 0x90, 0x90, 0x90, // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
- uint32_t GotPlt = In.GotPlt->getVA();
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 8, GotPlt + 8);
+ memcpy(buf, pltData, sizeof(pltData));
+ uint32_t gotPlt = in.gotPlt->getVA();
+ write32le(buf + 2, gotPlt + 4);
+ write32le(buf + 8, gotPlt + 8);
}
-void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
- 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx)
- 0x68, 0, 0, 0, 0, // pushl $reloc_offset
- 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
- };
- memcpy(Buf, Inst, sizeof(Inst));
-
- if (Config->Pic) {
- // jmp *foo@GOT(%ebx)
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- Buf[1] = 0xa3;
- write32le(Buf + 2, GotPltEntryAddr - Ebx);
+void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ if (config->isPic) {
+ const uint8_t inst[] = {
+ 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
+ 0x68, 0, 0, 0, 0, // pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
+ };
+ memcpy(buf, inst, sizeof(inst));
+ write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA());
} else {
- // jmp *foo_in_GOT
- Buf[1] = 0x25;
- write32le(Buf + 2, GotPltEntryAddr);
+ const uint8_t inst[] = {
+ 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
+ 0x68, 0, 0, 0, 0, // pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
+ };
+ memcpy(buf, inst, sizeof(inst));
+ write32le(buf + 2, gotPltEntryAddr);
}
- write32le(Buf + 7, RelOff);
- write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
+ write32le(buf + 7, relOff);
+ write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16);
}
-int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
- switch (Type) {
+int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ switch (type) {
case R_386_8:
case R_386_PC8:
- return SignExtend64<8>(*Buf);
+ return SignExtend64<8>(*buf);
case R_386_16:
case R_386_PC16:
- return SignExtend64<16>(read16le(Buf));
+ return SignExtend64<16>(read16le(buf));
case R_386_32:
case R_386_GOT32:
case R_386_GOT32X:
@@ -248,28 +254,28 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
case R_386_PLT32:
case R_386_TLS_LDO_32:
case R_386_TLS_LE:
- return SignExtend64<32>(read32le(Buf));
+ return SignExtend64<32>(read32le(buf));
default:
return 0;
}
}
-void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (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);
- *Loc = Val;
+ checkIntUInt(loc, val, 8, type);
+ *loc = val;
break;
case R_386_PC8:
- checkInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkInt(loc, val, 8, type);
+ *loc = val;
break;
case R_386_16:
- checkIntUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_386_PC16:
// R_386_PC16 is normally used with 16 bit code. In that situation
@@ -282,11 +288,10 @@ 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);
- write16le(Loc, Val);
+ checkInt(loc, val, 17, type);
+ write16le(loc, val);
break;
case R_386_32:
- case R_386_GLOB_DAT:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTOFF:
@@ -305,86 +310,86 @@ 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);
- write32le(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32le(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// to
// movl %gs:0,%eax
// subl $x@ntpoff,%eax
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax
};
- memcpy(Loc - 3, Inst, sizeof(Inst));
- write32le(Loc + 5, Val);
+ memcpy(loc - 3, inst, sizeof(inst));
+ write32le(loc + 5, val);
}
-void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// to
// movl %gs:0, %eax
// addl x@gotntpoff(%ebx), %eax
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax
};
- memcpy(Loc - 3, Inst, sizeof(Inst));
- write32le(Loc + 5, Val);
+ memcpy(loc - 3, inst, sizeof(inst));
+ write32le(loc + 5, val);
}
// 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, RelType type, 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;
+ uint8_t reg = (loc[-1] >> 3) & 7;
- if (Type == R_386_TLS_IE) {
- if (Loc[-1] == 0xa1) {
+ if (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
// this is a 5 byte instruction while below is 6 bytes.
- Loc[-1] = 0xb8;
- } else if (Loc[-2] == 0x8b) {
+ loc[-1] = 0xb8;
+ } else if (loc[-2] == 0x8b) {
// "movl foo@indntpoff,%reg" -> "movl $foo,%reg"
- Loc[-2] = 0xc7;
- Loc[-1] = 0xc0 | Reg;
+ loc[-2] = 0xc7;
+ loc[-1] = 0xc0 | reg;
} else {
// "addl foo@indntpoff,%reg" -> "addl $foo,%reg"
- Loc[-2] = 0x81;
- Loc[-1] = 0xc0 | Reg;
+ loc[-2] = 0x81;
+ loc[-1] = 0xc0 | reg;
}
} else {
- assert(Type == R_386_TLS_GOTIE);
- if (Loc[-2] == 0x8b) {
+ assert(type == R_386_TLS_GOTIE);
+ if (loc[-2] == 0x8b) {
// "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg"
- Loc[-2] = 0xc7;
- Loc[-1] = 0xc0 | Reg;
+ loc[-2] = 0xc7;
+ loc[-1] = 0xc0 | reg;
} else {
// "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg"
- Loc[-2] = 0x8d;
- Loc[-1] = 0x80 | (Reg << 3) | Reg;
+ loc[-2] = 0x8d;
+ loc[-1] = 0x80 | (reg << 3) | reg;
}
}
- write32le(Loc, Val);
+ write32le(loc, val);
}
-void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
- if (Type == R_386_TLS_LDO_32) {
- write32le(Loc, Val);
+void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_386_TLS_LDO_32) {
+ write32le(loc, val);
return;
}
@@ -395,48 +400,48 @@ void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// movl %gs:0,%eax
// nop
// leal 0(%esi,1),%esi
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
0x90, // nop
0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
};
- memcpy(Loc - 2, Inst, sizeof(Inst));
+ memcpy(loc - 2, inst, sizeof(inst));
}
namespace {
class RetpolinePic : public X86 {
public:
RetpolinePic();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
class RetpolineNoPic : public X86 {
public:
RetpolineNoPic();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
} // namespace
RetpolinePic::RetpolinePic() {
- PltHeaderSize = 48;
- PltEntrySize = 32;
+ pltHeaderSize = 48;
+ pltEntrySize = 32;
}
-void RetpolinePic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
- write32le(Buf, S.getPltVA() + 17);
+void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ write32le(buf, s.getPltVA() + 17);
}
-void RetpolinePic::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
- 0xff, 0xb3, 0, 0, 0, 0, // 0: pushl GOTPLT+4(%ebx)
+void RetpolinePic::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
+ 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx)
0x50, // 6: pushl %eax
- 0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax
+ 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next
0xf3, 0x90, // 12: loop: pause
0x0f, 0xae, 0xe8, // 14: lfence
@@ -450,18 +455,13 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const {
0xc3, // 2e: ret
0xcc, // 2f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
-
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 9, GotPlt + 8);
+ memcpy(buf, insn, sizeof(insn));
}
-void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x50, // pushl %eax
0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
0xe8, 0, 0, 0, 0, // call plt+0x20
@@ -470,28 +470,28 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
0xe9, 0, 0, 0, 0, // jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
-
- uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
- unsigned Off = getPltEntryOffset(Index);
- write32le(Buf + 3, GotPltEntryAddr - Ebx);
- write32le(Buf + 8, -Off - 12 + 32);
- write32le(Buf + 13, -Off - 17 + 18);
- write32le(Buf + 18, RelOff);
- write32le(Buf + 23, -Off - 27);
+ memcpy(buf, insn, sizeof(insn));
+
+ uint32_t ebx = in.gotPlt->getVA();
+ unsigned off = pltHeaderSize + pltEntrySize * index;
+ write32le(buf + 3, gotPltEntryAddr - ebx);
+ write32le(buf + 8, -off - 12 + 32);
+ write32le(buf + 13, -off - 17 + 18);
+ write32le(buf + 18, relOff);
+ write32le(buf + 23, -off - 27);
}
RetpolineNoPic::RetpolineNoPic() {
- PltHeaderSize = 48;
- PltEntrySize = 32;
+ pltHeaderSize = 48;
+ pltEntrySize = 32;
}
-void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
- write32le(Buf, S.getPltVA() + 16);
+void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ write32le(buf, s.getPltVA() + 16);
}
-void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
+void RetpolineNoPic::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
0x50, // 6: pushl %eax
0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
@@ -509,17 +509,17 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
0xc3, // 2e: ret
0xcc, // 2f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- uint32_t GotPlt = In.GotPlt->getVA();
- write32le(Buf + 2, GotPlt + 4);
- write32le(Buf + 8, GotPlt + 8);
+ uint32_t gotPlt = in.gotPlt->getVA();
+ write32le(buf + 2, gotPlt + 4);
+ write32le(buf + 8, gotPlt + 8);
}
-void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x50, // 0: pushl %eax
0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
0xe8, 0, 0, 0, 0, // 6: call plt+0x20
@@ -529,26 +529,26 @@ void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
0xcc, // 1f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
-
- unsigned Off = getPltEntryOffset(Index);
- write32le(Buf + 2, GotPltEntryAddr);
- write32le(Buf + 7, -Off - 11 + 32);
- write32le(Buf + 12, -Off - 16 + 17);
- write32le(Buf + 17, RelOff);
- write32le(Buf + 22, -Off - 26);
+ memcpy(buf, insn, sizeof(insn));
+
+ unsigned off = pltHeaderSize + pltEntrySize * index;
+ write32le(buf + 2, gotPltEntryAddr);
+ write32le(buf + 7, -off - 11 + 32);
+ write32le(buf + 12, -off - 16 + 17);
+ write32le(buf + 17, relOff);
+ write32le(buf + 22, -off - 26);
}
TargetInfo *elf::getX86TargetInfo() {
- if (Config->ZRetpolineplt) {
- if (Config->Pic) {
- static RetpolinePic T;
- return &T;
+ if (config->zRetpolineplt) {
+ if (config->isPic) {
+ static RetpolinePic t;
+ return &t;
}
- static RetpolineNoPic T;
- return &T;
+ static RetpolineNoPic t;
+ return &t;
}
- static X86 T;
- return &T;
+ static X86 t;
+ return &t;
}
diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp
index 06314155dcc9..de67aa5c33dc 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -1,9 +1,8 @@
//===- X86_64.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -23,71 +22,74 @@ using namespace lld;
using namespace lld::elf;
namespace {
-template <class ELFT> class X86_64 : public TargetInfo {
+class X86_64 : public TargetInfo {
public:
X86_64();
- RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const override;
- RelType getDynRel(RelType Type) const override;
- void writeGotPltHeader(uint8_t *Buf) const override;
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
-
- RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const override;
- void relaxGot(uint8_t *Loc, 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;
- bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const override;
-
-private:
- void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
- uint8_t ModRm) const;
+ int getTlsGdRelaxSkip(RelType type) const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ void writeGotPltHeader(uint8_t *buf) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
+ void relocateOne(uint8_t *loc, RelType type, uint64_t val) 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;
+ bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const override;
};
} // namespace
-template <class ELFT> X86_64<ELFT>::X86_64() {
- CopyRel = R_X86_64_COPY;
- GotRel = R_X86_64_GLOB_DAT;
- NoneRel = R_X86_64_NONE;
- PltRel = R_X86_64_JUMP_SLOT;
- RelativeRel = R_X86_64_RELATIVE;
- IRelativeRel = R_X86_64_IRELATIVE;
- TlsGotRel = R_X86_64_TPOFF64;
- TlsModuleIndexRel = R_X86_64_DTPMOD64;
- TlsOffsetRel = R_X86_64_DTPOFF64;
- GotEntrySize = 8;
- GotPltEntrySize = 8;
- PltEntrySize = 16;
- PltHeaderSize = 16;
- TlsGdRelaxSkip = 2;
- TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+X86_64::X86_64() {
+ copyRel = R_X86_64_COPY;
+ gotRel = R_X86_64_GLOB_DAT;
+ noneRel = R_X86_64_NONE;
+ pltRel = R_X86_64_JUMP_SLOT;
+ relativeRel = R_X86_64_RELATIVE;
+ iRelativeRel = R_X86_64_IRELATIVE;
+ symbolicRel = R_X86_64_64;
+ tlsDescRel = R_X86_64_TLSDESC;
+ tlsGotRel = R_X86_64_TPOFF64;
+ tlsModuleIndexRel = R_X86_64_DTPMOD64;
+ tlsOffsetRel = R_X86_64_DTPOFF64;
+ pltEntrySize = 16;
+ pltHeaderSize = 16;
+ trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
// Align to the large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
- DefaultImageBase = 0x200000;
+ defaultImageBase = 0x200000;
}
-template <class ELFT>
-RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const {
- switch (Type) {
+int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; }
+
+RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const {
+ if (type == R_X86_64_GOTTPOFF)
+ config->hasStaticTlsModel = true;
+
+ switch (type) {
case R_X86_64_8:
case R_X86_64_16:
case R_X86_64_32:
case R_X86_64_32S:
case R_X86_64_64:
+ return R_ABS;
case R_X86_64_DTPOFF32:
case R_X86_64_DTPOFF64:
- return R_ABS;
+ return R_DTPREL;
case R_X86_64_TPOFF32:
return R_TLS;
+ case R_X86_64_TLSDESC_CALL:
+ return R_TLSDESC_CALL;
case R_X86_64_TLSLD:
return R_TLSLD_PC;
case R_X86_64_TLSGD:
@@ -97,218 +99,280 @@ RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
return R_SIZE;
case R_X86_64_PLT32:
return R_PLT_PC;
+ case R_X86_64_PC8:
+ case R_X86_64_PC16:
case R_X86_64_PC32:
case R_X86_64_PC64:
return R_PC;
case R_X86_64_GOT32:
case R_X86_64_GOT64:
- return R_GOT_FROM_END;
+ return R_GOTPLT;
+ case R_X86_64_GOTPC32_TLSDESC:
+ return R_TLSDESC_PC;
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOTTPOFF:
return R_GOT_PC;
case R_X86_64_GOTOFF64:
- return R_GOTREL_FROM_END;
+ return R_GOTPLTREL;
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
- return R_GOTONLY_PC_FROM_END;
+ return R_GOTPLTONLY_PC;
case R_X86_64_NONE:
return R_NONE;
default:
- return R_INVALID;
+ error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+ ") against symbol " + toString(s));
+ return R_NONE;
}
}
-template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
+void X86_64::writeGotPltHeader(uint8_t *buf) const {
// The first entry holds the value of _DYNAMIC. It is not clear why that is
// required, but it is documented in the psabi and the glibc dynamic linker
// seems to use it (note that this is relevant for linking ld.so, not any
// other program).
- write64le(Buf, In.Dynamic->getVA());
+ write64le(buf, mainPart->dynamic->getVA());
}
-template <class ELFT>
-void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
+void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const {
// See comments in X86::writeGotPlt.
- write64le(Buf, S.getPltVA() + 6);
+ write64le(buf, s.getPltVA() + 6);
}
-template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+void X86_64::writePltHeader(uint8_t *buf) const {
+ const uint8_t pltData[] = {
0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
0x0f, 0x1f, 0x40, 0x00, // nop
};
- memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
- write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8
- write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16
+ memcpy(buf, pltData, sizeof(pltData));
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+ write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8
+ write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16
}
-template <class ELFT>
-void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Inst[] = {
+void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t inst[] = {
0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip)
0x68, 0, 0, 0, 0, // pushq <relocation index>
0xe9, 0, 0, 0, 0, // jmpq plt[0]
};
- memcpy(Buf, Inst, sizeof(Inst));
+ memcpy(buf, inst, sizeof(inst));
- write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
- write32le(Buf + 7, Index);
- write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
+ write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6);
+ write32le(buf + 7, index);
+ write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16);
}
-template <class ELFT> RelType X86_64<ELFT>::getDynRel(RelType Type) const {
- if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 ||
- Type == R_X86_64_SIZE64)
- return Type;
+RelType X86_64::getDynRel(RelType type) const {
+ if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 ||
+ type == R_X86_64_SIZE64)
+ return type;
return R_X86_64_NONE;
}
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- // Convert
- // .byte 0x66
- // leaq x@tlsgd(%rip), %rdi
- // .word 0x6666
- // rex64
- // call __tls_get_addr@plt
- // to
- // mov %fs:0x0,%rax
- // lea x@tpoff,%rax
- const uint8_t Inst[] = {
- 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax
- 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax
- };
- memcpy(Loc - 4, Inst, sizeof(Inst));
-
- // The original code used a pc relative relocation and so we have to
- // compensate for the -4 in had in the addend.
- write32le(Loc + 8, Val + 4);
+void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_X86_64_TLSGD) {
+ // Convert
+ // .byte 0x66
+ // leaq x@tlsgd(%rip), %rdi
+ // .word 0x6666
+ // rex64
+ // call __tls_get_addr@plt
+ // to the following two instructions.
+ const uint8_t inst[] = {
+ 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00,
+ 0x00, 0x00, // mov %fs:0x0,%rax
+ 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax
+ };
+ memcpy(loc - 4, inst, sizeof(inst));
+
+ // The original code used a pc relative relocation and so we have to
+ // compensate for the -4 in had in the addend.
+ write32le(loc + 8, val + 4);
+ } else {
+ // Convert
+ // lea x@tlsgd(%rip), %rax
+ // call *(%rax)
+ // to the following two instructions.
+ assert(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");
+ return;
+ }
+ // movq $x@tpoff(%rip),%rax
+ loc[-2] = 0xc7;
+ loc[-1] = 0xc0;
+ write32le(loc, val + 4);
+ // xchg ax,ax
+ loc[4] = 0x66;
+ loc[5] = 0x90;
+ }
}
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- // Convert
- // .byte 0x66
- // leaq x@tlsgd(%rip), %rdi
- // .word 0x6666
- // rex64
- // call __tls_get_addr@plt
- // to
- // mov %fs:0x0,%rax
- // addq x@tpoff,%rax
- const uint8_t Inst[] = {
- 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax
- 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax
- };
- memcpy(Loc - 4, Inst, sizeof(Inst));
-
- // Both code sequences are PC relatives, but since we are moving the constant
- // forward by 8 bytes we have to subtract the value by 8.
- write32le(Loc + 8, Val - 8);
+void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_X86_64_TLSGD) {
+ // Convert
+ // .byte 0x66
+ // leaq x@tlsgd(%rip), %rdi
+ // .word 0x6666
+ // rex64
+ // call __tls_get_addr@plt
+ // to the following two instructions.
+ const uint8_t inst[] = {
+ 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00,
+ 0x00, 0x00, // mov %fs:0x0,%rax
+ 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax
+ };
+ memcpy(loc - 4, inst, sizeof(inst));
+
+ // Both code sequences are PC relatives, but since we are moving the
+ // constant forward by 8 bytes we have to subtract the value by 8.
+ write32le(loc + 8, val - 8);
+ } else {
+ // Convert
+ // lea x@tlsgd(%rip), %rax
+ // call *(%rax)
+ // to the following two instructions.
+ assert(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");
+ return;
+ }
+ // movq x@gottpoff(%rip),%rax
+ loc[-2] = 0x8b;
+ write32le(loc, val);
+ // xchg ax,ax
+ loc[4] = 0x66;
+ loc[5] = 0x90;
+ }
}
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- uint8_t *Inst = Loc - 3;
- uint8_t Reg = Loc[-1] >> 3;
- uint8_t *RegSlot = Loc - 1;
+void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ uint8_t *inst = loc - 3;
+ uint8_t reg = loc[-1] >> 3;
+ uint8_t *regSlot = loc - 1;
// Note that ADD with RSP or R12 is converted to ADD instead of LEA
// because LEA with these registers needs 4 bytes to encode and thus
// wouldn't fit the space.
- if (memcmp(Inst, "\x48\x03\x25", 3) == 0) {
+ if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
// "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp"
- memcpy(Inst, "\x48\x81\xc4", 3);
- } else if (memcmp(Inst, "\x4c\x03\x25", 3) == 0) {
+ memcpy(inst, "\x48\x81\xc4", 3);
+ } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) {
// "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12"
- memcpy(Inst, "\x49\x81\xc4", 3);
- } else if (memcmp(Inst, "\x4c\x03", 2) == 0) {
+ memcpy(inst, "\x49\x81\xc4", 3);
+ } else if (memcmp(inst, "\x4c\x03", 2) == 0) {
// "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]"
- memcpy(Inst, "\x4d\x8d", 2);
- *RegSlot = 0x80 | (Reg << 3) | Reg;
- } else if (memcmp(Inst, "\x48\x03", 2) == 0) {
+ memcpy(inst, "\x4d\x8d", 2);
+ *regSlot = 0x80 | (reg << 3) | reg;
+ } else if (memcmp(inst, "\x48\x03", 2) == 0) {
// "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg"
- memcpy(Inst, "\x48\x8d", 2);
- *RegSlot = 0x80 | (Reg << 3) | Reg;
- } else if (memcmp(Inst, "\x4c\x8b", 2) == 0) {
+ memcpy(inst, "\x48\x8d", 2);
+ *regSlot = 0x80 | (reg << 3) | reg;
+ } else if (memcmp(inst, "\x4c\x8b", 2) == 0) {
// "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]"
- memcpy(Inst, "\x49\xc7", 2);
- *RegSlot = 0xc0 | Reg;
- } else if (memcmp(Inst, "\x48\x8b", 2) == 0) {
+ memcpy(inst, "\x49\xc7", 2);
+ *regSlot = 0xc0 | reg;
+ } else if (memcmp(inst, "\x48\x8b", 2) == 0) {
// "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg"
- memcpy(Inst, "\x48\xc7", 2);
- *RegSlot = 0xc0 | Reg;
+ memcpy(inst, "\x48\xc7", 2);
+ *regSlot = 0xc0 | reg;
} else {
- error(getErrorLocation(Loc - 3) +
+ error(getErrorLocation(loc - 3) +
"R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only");
}
// The original code used a PC relative relocation.
// Need to compensate for the -4 it had in the addend.
- write32le(Loc, Val + 4);
+ write32le(loc, val + 4);
}
-template <class ELFT>
-void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
- // Convert
- // leaq bar@tlsld(%rip), %rdi
- // callq __tls_get_addr@PLT
- // leaq bar@dtpoff(%rax), %rcx
- // to
- // .word 0x6666
- // .byte 0x66
- // mov %fs:0,%rax
- // leaq bar@tpoff(%rax), %rcx
- if (Type == R_X86_64_DTPOFF64) {
- write64le(Loc, Val);
+void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+ if (type == R_X86_64_DTPOFF64) {
+ write64le(loc, val);
return;
}
- if (Type == R_X86_64_DTPOFF32) {
- write32le(Loc, Val);
+ if (type == R_X86_64_DTPOFF32) {
+ write32le(loc, val);
return;
}
- const uint8_t Inst[] = {
+ const uint8_t inst[] = {
0x66, 0x66, // .word 0x6666
0x66, // .byte 0x66
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax
};
- memcpy(Loc - 3, Inst, sizeof(Inst));
+
+ if (loc[4] == 0xe8) {
+ // Convert
+ // leaq bar@tlsld(%rip), %rdi # 48 8d 3d <Loc>
+ // callq __tls_get_addr@PLT # e8 <disp32>
+ // leaq bar@dtpoff(%rax), %rcx
+ // to
+ // .word 0x6666
+ // .byte 0x66
+ // mov %fs:0,%rax
+ // leaq bar@tpoff(%rax), %rcx
+ memcpy(loc - 3, inst, sizeof(inst));
+ return;
+ }
+
+ if (loc[4] == 0xff && loc[5] == 0x15) {
+ // Convert
+ // leaq x@tlsld(%rip),%rdi # 48 8d 3d <Loc>
+ // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 <disp32>
+ // to
+ // .long 0x66666666
+ // movq %fs:0,%rax
+ // See "Table 11.9: LD -> LE Code Transition (LP64)" in
+ // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf
+ loc[-3] = 0x66;
+ memcpy(loc - 2, inst, sizeof(inst));
+ return;
+ }
+
+ error(getErrorLocation(loc - 3) +
+ "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD");
}
-template <class ELFT>
-void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- switch (Type) {
+void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+ switch (type) {
case R_X86_64_8:
- checkUInt(Loc, Val, 8, Type);
- *Loc = Val;
+ checkIntUInt(loc, val, 8, type);
+ *loc = val;
+ break;
+ case R_X86_64_PC8:
+ checkInt(loc, val, 8, type);
+ *loc = val;
break;
case R_X86_64_16:
- checkUInt(Loc, Val, 16, Type);
- write16le(Loc, Val);
+ checkIntUInt(loc, val, 16, type);
+ write16le(loc, val);
+ break;
+ case R_X86_64_PC16:
+ checkInt(loc, val, 16, type);
+ write16le(loc, val);
break;
case R_X86_64_32:
- checkUInt(Loc, Val, 32, Type);
- write32le(Loc, Val);
+ checkUInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_X86_64_32S:
case R_X86_64_TPOFF32:
case R_X86_64_GOT32:
case R_X86_64_GOTPC32:
+ case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
@@ -319,49 +383,47 @@ void X86_64<ELFT>::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);
- write32le(Loc, Val);
+ checkInt(loc, val, 32, type);
+ write32le(loc, val);
break;
case R_X86_64_64:
case R_X86_64_DTPOFF64:
- case R_X86_64_GLOB_DAT:
case R_X86_64_PC64:
case R_X86_64_SIZE64:
case R_X86_64_GOT64:
case R_X86_64_GOTOFF64:
case R_X86_64_GOTPC64:
- write64le(Loc, Val);
+ write64le(loc, val);
break;
default:
- error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ llvm_unreachable("unknown relocation");
}
}
-template <class ELFT>
-RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr RelExpr) const {
- if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX)
- return RelExpr;
- const uint8_t Op = Data[-2];
- const uint8_t ModRm = Data[-1];
+RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr relExpr) const {
+ if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX)
+ return relExpr;
+ const uint8_t op = data[-2];
+ const uint8_t modRm = data[-1];
// FIXME: When PIC is disabled and foo is defined locally in the
// lower 32 bit address space, memory operand in mov can be converted into
// immediate operand. Otherwise, mov must be changed to lea. We support only
// latter relaxation at this moment.
- if (Op == 0x8b)
+ if (op == 0x8b)
return R_RELAX_GOT_PC;
// Relax call and jmp.
- if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25))
+ if (op == 0xff && (modRm == 0x15 || modRm == 0x25))
return R_RELAX_GOT_PC;
// Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor.
// If PIC then no relaxation is available.
// We also don't relax test/binop instructions without REX byte,
// they are 32bit operations and not common to have.
- assert(Type == R_X86_64_REX_GOTPCRELX);
- return Config->Pic ? RelExpr : R_RELAX_GOT_PC_NOPIC;
+ assert(type == R_X86_64_REX_GOTPCRELX);
+ return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC;
}
// A subset of relaxations can only be applied for no-PIC. This method
@@ -369,12 +431,11 @@ RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data,
// "Intel 64 and IA-32 Architectures Software Developer's Manual V2"
// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
-template <class ELFT>
-void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
- uint8_t ModRm) const {
- const uint8_t Rex = Loc[-3];
+static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op,
+ uint8_t modRm) {
+ const uint8_t rex = loc[-3];
// Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg".
- if (Op == 0x85) {
+ if (op == 0x85) {
// See "TEST-Logical Compare" (4-428 Vol. 2B),
// TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension).
@@ -391,11 +452,11 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// 0x38 == 00 111 000 binary.
// We transfer reg2 to reg1 here as operand.
// See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3).
- Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3; // ModR/M byte.
+ loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte.
// Change opcode from TEST r/m64, r64 to TEST r/m64, imm32
// See "TEST-Logical Compare" (4-428 Vol. 2B).
- Loc[-2] = 0xf7;
+ loc[-2] = 0xf7;
// Move R bit to the B bit in REX byte.
// REX byte is encoded as 0100WRXB, where
@@ -408,8 +469,8 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// REX.B This 1-bit value is an extension to the MODRM.rm field or the
// SIB.base field.
// See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A).
- Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2;
- write32le(Loc, Val);
+ loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2;
+ write32le(loc, val);
return;
}
@@ -419,7 +480,7 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg".
// Logic is close to one for test instruction above, but we also
// write opcode extension here, see below for details.
- Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3 | (Op & 0x3c); // ModR/M byte.
+ loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte.
// Primary opcode is 0x81, opcode extension is one of:
// 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB,
@@ -428,69 +489,67 @@ void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
// See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15),
// "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for
// descriptions about each operation.
- Loc[-2] = 0x81;
- Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2;
- write32le(Loc, Val);
+ loc[-2] = 0x81;
+ loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2;
+ write32le(loc, val);
}
-template <class ELFT>
-void X86_64<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
- const uint8_t Op = Loc[-2];
- const uint8_t ModRm = Loc[-1];
+void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+ const uint8_t op = loc[-2];
+ const uint8_t modRm = loc[-1];
// Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
- if (Op == 0x8b) {
- Loc[-2] = 0x8d;
- write32le(Loc, Val);
+ if (op == 0x8b) {
+ loc[-2] = 0x8d;
+ write32le(loc, val);
return;
}
- if (Op != 0xff) {
+ if (op != 0xff) {
// We are relaxing a rip relative to an absolute, so compensate
// for the old -4 addend.
- assert(!Config->Pic);
- relaxGotNoPic(Loc, Val + 4, Op, ModRm);
+ assert(!config->isPic);
+ relaxGotNoPic(loc, val + 4, op, modRm);
return;
}
// Convert call/jmp instructions.
- if (ModRm == 0x15) {
+ if (modRm == 0x15) {
// ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo".
// Instead we convert to "addr32 call foo" where addr32 is an instruction
// prefix. That makes result expression to be a single instruction.
- Loc[-2] = 0x67; // addr32 prefix
- Loc[-1] = 0xe8; // call
- write32le(Loc, Val);
+ loc[-2] = 0x67; // addr32 prefix
+ loc[-1] = 0xe8; // call
+ write32le(loc, val);
return;
}
// Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop".
// jmp doesn't return, so it is fine to use nop here, it is just a stub.
- assert(ModRm == 0x25);
- Loc[-2] = 0xe9; // jmp
- Loc[3] = 0x90; // nop
- write32le(Loc - 1, Val + 1);
+ assert(modRm == 0x25);
+ loc[-2] = 0xe9; // jmp
+ loc[3] = 0x90; // nop
+ write32le(loc - 1, val + 1);
}
-// This anonymous namespace works around a warning bug in
-// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
-namespace {
-
// A split-stack prologue starts by checking the amount of stack remaining
// in one of two ways:
// A) Comparing of the stack pointer to a field in the tcb.
// B) Or a load of a stack pointer offset with an lea to r10 or r11.
-template <>
-bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End,
- uint8_t StOther) const {
- if (Loc + 8 >= End)
+bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const {
+ if (!config->is64) {
+ error("Target doesn't support split stacks.");
+ return false;
+ }
+
+ if (loc + 8 >= end)
return false;
// Replace "cmp %fs:0x70,%rsp" and subsequent branch
// with "stc, nopl 0x0(%rax,%rax,1)"
- if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 0) {
- memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
+ if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) {
+ memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
return true;
}
@@ -498,25 +557,16 @@ bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
// be r10 or r11. The lea instruction feeds a subsequent compare which checks
// if there is X available stack space. Making X larger effectively reserves
// that much additional space. The stack grows downward so subtract the value.
- if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 ||
- memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) {
+ if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 ||
+ memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) {
// The offset bytes are encoded four bytes after the start of the
// instruction.
- write32le(Loc + 4, read32le(Loc + 4) - 0x4000);
+ write32le(loc + 4, read32le(loc + 4) - 0x4000);
return true;
}
return false;
}
-template <>
-bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End,
- uint8_t StOther) const {
- llvm_unreachable("Target doesn't support split stacks.");
-}
-
-} // namespace
-
// These nonstandard PLT entries are to migtigate Spectre v2 security
// vulnerability. In order to mitigate Spectre v2, we want to avoid indirect
// branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT
@@ -527,37 +577,36 @@ bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
// is specified, all dynamic symbols are resolved at load-time. Thus, when
// that option is given, we can omit code for symbol lazy resolution.
namespace {
-template <class ELFT> class Retpoline : public X86_64<ELFT> {
+class Retpoline : public X86_64 {
public:
Retpoline();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
-template <class ELFT> class RetpolineZNow : public X86_64<ELFT> {
+class RetpolineZNow : public X86_64 {
public:
RetpolineZNow();
- void writeGotPlt(uint8_t *Buf, const Symbol &S) const override {}
- void writePltHeader(uint8_t *Buf) const override;
- void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
- int32_t Index, unsigned RelOff) const override;
+ void writeGotPlt(uint8_t *buf, const Symbol &s) const override {}
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
+ int32_t index, unsigned relOff) const override;
};
} // namespace
-template <class ELFT> Retpoline<ELFT>::Retpoline() {
- TargetInfo::PltHeaderSize = 48;
- TargetInfo::PltEntrySize = 32;
+Retpoline::Retpoline() {
+ pltHeaderSize = 48;
+ pltEntrySize = 32;
}
-template <class ELFT>
-void Retpoline<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
- write64le(Buf, S.getPltVA() + 17);
+void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const {
+ write64le(buf, s.getPltVA() + 17);
}
-template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
+void Retpoline::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip)
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next
@@ -570,19 +619,18 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding
0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- uint64_t GotPlt = In.GotPlt->getVA();
- uint64_t Plt = In.Plt->getVA();
- write32le(Buf + 2, GotPlt - Plt - 6 + 8);
- write32le(Buf + 9, GotPlt - Plt - 13 + 16);
+ uint64_t gotPlt = in.gotPlt->getVA();
+ uint64_t plt = in.plt->getVA();
+ write32le(buf + 2, gotPlt - plt - 6 + 8);
+ write32le(buf + 9, gotPlt - plt - 13 + 16);
}
-template <class ELFT>
-void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11
0xe8, 0, 0, 0, 0, // 7: callq plt+0x20
0xe9, 0, 0, 0, 0, // c: jmp plt+0x12
@@ -590,25 +638,24 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
0xe9, 0, 0, 0, 0, // 16: jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- uint64_t Off = getPltEntryOffset(Index);
+ uint64_t off = pltHeaderSize + pltEntrySize * index;
- write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
- write32le(Buf + 8, -Off - 12 + 32);
- write32le(Buf + 13, -Off - 17 + 18);
- write32le(Buf + 18, Index);
- write32le(Buf + 23, -Off - 27);
+ write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7);
+ write32le(buf + 8, -off - 12 + 32);
+ write32le(buf + 13, -off - 17 + 18);
+ write32le(buf + 18, index);
+ write32le(buf + 23, -off - 27);
}
-template <class ELFT> RetpolineZNow<ELFT>::RetpolineZNow() {
- TargetInfo::PltHeaderSize = 32;
- TargetInfo::PltEntrySize = 16;
+RetpolineZNow::RetpolineZNow() {
+ pltHeaderSize = 32;
+ pltEntrySize = 16;
}
-template <class ELFT>
-void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const {
- const uint8_t Insn[] = {
+void RetpolineZNow::writePltHeader(uint8_t *buf) const {
+ const uint8_t insn[] = {
0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next
0xf3, 0x90, // 5: loop: pause
0x0f, 0xae, 0xe8, // 7: lfence
@@ -620,37 +667,35 @@ void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const {
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
0xcc, // 1f: int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
}
-template <class ELFT>
-void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- const uint8_t Insn[] = {
+void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {
+ const uint8_t insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
0xe9, 0, 0, 0, 0, // jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
- memcpy(Buf, Insn, sizeof(Insn));
+ memcpy(buf, insn, sizeof(insn));
- write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
- write32le(Buf + 8, -getPltEntryOffset(Index) - 12);
+ write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7);
+ write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12);
}
-template <class ELFT> static TargetInfo *getTargetInfo() {
- if (Config->ZRetpolineplt) {
- if (Config->ZNow) {
- static RetpolineZNow<ELFT> T;
- return &T;
+static TargetInfo *getTargetInfo() {
+ if (config->zRetpolineplt) {
+ if (config->zNow) {
+ static RetpolineZNow t;
+ return &t;
}
- static Retpoline<ELFT> T;
- return &T;
+ static Retpoline t;
+ return &t;
}
- static X86_64<ELFT> T;
- return &T;
+ static X86_64 t;
+ return &t;
}
-TargetInfo *elf::getX32TargetInfo() { return getTargetInfo<ELF32LE>(); }
-TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo<ELF64LE>(); }
+TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
diff --git a/ELF/Bits.h b/ELF/Bits.h
deleted file mode 100644
index 13d40322265e..000000000000
--- a/ELF/Bits.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//===- Bits.h ---------------------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_ELF_BITS_H
-#define LLD_ELF_BITS_H
-
-#include "Config.h"
-#include "llvm/Support/Endian.h"
-
-namespace lld {
-namespace elf {
-
-inline uint64_t readUint(uint8_t *Buf) {
- if (Config->Is64)
- return llvm::support::endian::read64(Buf, Config->Endianness);
- return llvm::support::endian::read32(Buf, Config->Endianness);
-}
-
-inline void writeUint(uint8_t *Buf, uint64_t Val) {
- if (Config->Is64)
- llvm::support::endian::write64(Buf, Val, Config->Endianness);
- else
- llvm::support::endian::write32(Buf, Val, Config->Endianness);
-}
-
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index a1c23b0d49ac..70578746483d 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -27,7 +27,6 @@ add_lld_library(lldELF
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
- Filesystem.cpp
ICF.cpp
InputFiles.cpp
InputSection.cpp
diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp
index 2a7d78664b8e..9aaadd481833 100644
--- a/ELF/CallGraphSort.cpp
+++ b/ELF/CallGraphSort.cpp
@@ -1,9 +1,8 @@
//===- CallGraphSort.cpp --------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
@@ -52,24 +51,24 @@ using namespace lld::elf;
namespace {
struct Edge {
- int From;
- uint64_t Weight;
+ int from;
+ uint64_t weight;
};
struct Cluster {
- Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {}
+ Cluster(int sec, size_t s) : sections{sec}, size(s) {}
double getDensity() const {
- if (Size == 0)
+ if (size == 0)
return 0;
- return double(Weight) / double(Size);
+ return double(weight) / double(size);
}
- std::vector<int> Sections;
- size_t Size = 0;
- uint64_t Weight = 0;
- uint64_t InitialWeight = 0;
- Edge BestPred = {-1, 0};
+ std::vector<int> sections;
+ size_t size = 0;
+ uint64_t weight = 0;
+ uint64_t initialWeight = 0;
+ Edge bestPred = {-1, 0};
};
class CallGraphSort {
@@ -79,8 +78,8 @@ public:
DenseMap<const InputSectionBase *, int> run();
private:
- std::vector<Cluster> Clusters;
- std::vector<const InputSectionBase *> Sections;
+ std::vector<Cluster> clusters;
+ std::vector<const InputSectionBase *> sections;
void groupClusters();
};
@@ -93,30 +92,30 @@ constexpr int MAX_DENSITY_DEGRADATION = 8;
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
} // end anonymous namespace
-typedef std::pair<const InputSectionBase *, const InputSectionBase *>
- SectionPair;
+using SectionPair =
+ std::pair<const InputSectionBase *, const InputSectionBase *>;
// Take the edge list in Config->CallGraphProfile, resolve symbol names to
// Symbols, and generate a graph between InputSections with the provided
// weights.
CallGraphSort::CallGraphSort() {
- MapVector<SectionPair, uint64_t> &Profile = Config->CallGraphProfile;
- DenseMap<const InputSectionBase *, int> SecToCluster;
-
- auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int {
- auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size()));
- if (Res.second) {
- Sections.push_back(IS);
- Clusters.emplace_back(Clusters.size(), IS->getSize());
+ MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
+ DenseMap<const InputSectionBase *, int> secToCluster;
+
+ auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
+ auto res = secToCluster.insert(std::make_pair(isec, clusters.size()));
+ if (res.second) {
+ sections.push_back(isec);
+ clusters.emplace_back(clusters.size(), isec->getSize());
}
- return Res.first->second;
+ return res.first->second;
};
// Create the graph.
- for (std::pair<SectionPair, uint64_t> &C : Profile) {
- const auto *FromSB = cast<InputSectionBase>(C.first.first->Repl);
- const auto *ToSB = cast<InputSectionBase>(C.first.second->Repl);
- uint64_t Weight = C.second;
+ for (std::pair<SectionPair, uint64_t> &c : profile) {
+ const auto *fromSB = cast<InputSectionBase>(c.first.first->repl);
+ const auto *toSB = cast<InputSectionBase>(c.first.second->repl);
+ uint64_t weight = c.second;
// Ignore edges between input sections belonging to different output
// sections. This is done because otherwise we would end up with clusters
@@ -124,110 +123,130 @@ CallGraphSort::CallGraphSort() {
// output. This messes with the cluster size and density calculations. We
// would also end up moving input sections in other output sections without
// moving them closer to what calls them.
- if (FromSB->getOutputSection() != ToSB->getOutputSection())
+ if (fromSB->getOutputSection() != toSB->getOutputSection())
continue;
- int From = GetOrCreateNode(FromSB);
- int To = GetOrCreateNode(ToSB);
+ int from = getOrCreateNode(fromSB);
+ int to = getOrCreateNode(toSB);
- Clusters[To].Weight += Weight;
+ clusters[to].weight += weight;
- if (From == To)
+ if (from == to)
continue;
// Remember the best edge.
- Cluster &ToC = Clusters[To];
- if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) {
- ToC.BestPred.From = From;
- ToC.BestPred.Weight = Weight;
+ Cluster &toC = clusters[to];
+ if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) {
+ toC.bestPred.from = from;
+ toC.bestPred.weight = weight;
}
}
- for (Cluster &C : Clusters)
- C.InitialWeight = C.Weight;
+ for (Cluster &c : clusters)
+ c.initialWeight = c.weight;
}
// It's bad to merge clusters which would degrade the density too much.
-static bool isNewDensityBad(Cluster &A, Cluster &B) {
- double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size);
- return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION;
+static bool isNewDensityBad(Cluster &a, Cluster &b) {
+ double newDensity = double(a.weight + b.weight) / double(a.size + b.size);
+ return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
}
-static void mergeClusters(Cluster &Into, Cluster &From) {
- Into.Sections.insert(Into.Sections.end(), From.Sections.begin(),
- From.Sections.end());
- Into.Size += From.Size;
- Into.Weight += From.Weight;
- From.Sections.clear();
- From.Size = 0;
- From.Weight = 0;
+static void mergeClusters(Cluster &into, Cluster &from) {
+ into.sections.insert(into.sections.end(), from.sections.begin(),
+ from.sections.end());
+ into.size += from.size;
+ into.weight += from.weight;
+ from.sections.clear();
+ from.size = 0;
+ from.weight = 0;
}
// Group InputSections into clusters using the Call-Chain Clustering heuristic
// then sort the clusters by density.
void CallGraphSort::groupClusters() {
- std::vector<int> SortedSecs(Clusters.size());
- std::vector<Cluster *> SecToCluster(Clusters.size());
+ std::vector<int> sortedSecs(clusters.size());
+ std::vector<Cluster *> secToCluster(clusters.size());
- for (size_t I = 0; I < Clusters.size(); ++I) {
- SortedSecs[I] = I;
- SecToCluster[I] = &Clusters[I];
+ for (size_t i = 0; i < clusters.size(); ++i) {
+ sortedSecs[i] = i;
+ secToCluster[i] = &clusters[i];
}
- std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) {
- return Clusters[B].getDensity() < Clusters[A].getDensity();
+ llvm::stable_sort(sortedSecs, [&](int a, int b) {
+ return clusters[a].getDensity() > clusters[b].getDensity();
});
- for (int SI : SortedSecs) {
- // Clusters[SI] is the same as SecToClusters[SI] here because it has not
+ for (int si : sortedSecs) {
+ // clusters[si] is the same as secToClusters[si] here because it has not
// been merged into another cluster yet.
- Cluster &C = Clusters[SI];
+ Cluster &c = clusters[si];
// Don't consider merging if the edge is unlikely.
- if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight)
+ if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
continue;
- Cluster *PredC = SecToCluster[C.BestPred.From];
- if (PredC == &C)
+ Cluster *predC = secToCluster[c.bestPred.from];
+ if (predC == &c)
continue;
- if (C.Size + PredC->Size > MAX_CLUSTER_SIZE)
+ if (c.size + predC->size > MAX_CLUSTER_SIZE)
continue;
- if (isNewDensityBad(*PredC, C))
+ if (isNewDensityBad(*predC, c))
continue;
// NOTE: Consider using a disjoint-set to track section -> cluster mapping
// if this is ever slow.
- for (int SI : C.Sections)
- SecToCluster[SI] = PredC;
+ for (int si : c.sections)
+ secToCluster[si] = predC;
- mergeClusters(*PredC, C);
+ mergeClusters(*predC, c);
}
// Remove empty or dead nodes. Invalidates all cluster indices.
- llvm::erase_if(Clusters, [](const Cluster &C) {
- return C.Size == 0 || C.Sections.empty();
+ llvm::erase_if(clusters, [](const Cluster &c) {
+ return c.size == 0 || c.sections.empty();
});
// Sort by density.
- std::stable_sort(Clusters.begin(), Clusters.end(),
- [](const Cluster &A, const Cluster &B) {
- return A.getDensity() > B.getDensity();
- });
+ llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) {
+ return a.getDensity() > b.getDensity();
+ });
}
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
groupClusters();
// Generate order.
- DenseMap<const InputSectionBase *, int> OrderMap;
- ssize_t CurOrder = 1;
+ DenseMap<const InputSectionBase *, int> orderMap;
+ ssize_t curOrder = 1;
+
+ for (const Cluster &c : clusters)
+ for (int secIndex : c.sections)
+ orderMap[sections[secIndex]] = curOrder++;
+
+ if (!config->printSymbolOrder.empty()) {
+ std::error_code ec;
+ raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None);
+ if (ec) {
+ error("cannot open " + config->printSymbolOrder + ": " + ec.message());
+ return orderMap;
+ }
- for (const Cluster &C : Clusters)
- for (int SecIndex : C.Sections)
- OrderMap[Sections[SecIndex]] = CurOrder++;
+ // Print the symbols ordered by C3, in the order of increasing curOrder
+ // Instead of sorting all the orderMap, just repeat the loops above.
+ for (const Cluster &c : clusters)
+ for (int secIndex : c.sections)
+ // Search all the symbols in the file of the section
+ // and find out a Defined symbol with name that is within the section.
+ for (Symbol *sym: sections[secIndex]->file->getSymbols())
+ if (!sym->isSection()) // Filter out section-type symbols here.
+ if (auto *d = dyn_cast<Defined>(sym))
+ if (sections[secIndex] == d->section)
+ os << sym->getName() << "\n";
+ }
- return OrderMap;
+ return orderMap;
}
// Sort sections by the profile data provided by -callgraph-profile-file
diff --git a/ELF/CallGraphSort.h b/ELF/CallGraphSort.h
index 3f96dc88f435..5a092278a416 100644
--- a/ELF/CallGraphSort.h
+++ b/ELF/CallGraphSort.h
@@ -1,9 +1,8 @@
//===- CallGraphSort.h ------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/Config.h b/ELF/Config.h
index 8fb760e592eb..ff9d3dc0933c 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -1,9 +1,8 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -18,6 +17,7 @@
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
+#include <atomic>
#include <vector>
namespace lld {
@@ -62,18 +62,17 @@ enum class Target2Policy { Abs, Rel, GotRel };
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
struct SymbolVersion {
- llvm::StringRef Name;
- bool IsExternCpp;
- bool HasWildcard;
+ llvm::StringRef name;
+ bool isExternCpp;
+ bool hasWildcard;
};
// This struct contains symbols version definition that
// can be found in version script if it is used for link.
struct VersionDefinition {
- llvm::StringRef Name;
- uint16_t Id = 0;
- std::vector<SymbolVersion> Globals;
- size_t NameOff = 0; // Offset in the string table
+ llvm::StringRef name;
+ uint16_t id = 0;
+ std::vector<SymbolVersion> globals;
};
// This struct contains the global configuration for the linker.
@@ -81,162 +80,177 @@ struct VersionDefinition {
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Configuration {
- uint8_t OSABI = 0;
- llvm::CachePruningPolicy ThinLTOCachePolicy;
- llvm::StringMap<uint64_t> SectionStartMap;
- llvm::StringRef Chroot;
- llvm::StringRef DynamicLinker;
- llvm::StringRef DwoDir;
- llvm::StringRef Entry;
- llvm::StringRef Emulation;
- llvm::StringRef Fini;
- llvm::StringRef Init;
- llvm::StringRef LTOAAPipeline;
- llvm::StringRef LTONewPmPasses;
- llvm::StringRef LTOObjPath;
- llvm::StringRef LTOSampleProfile;
- llvm::StringRef MapFile;
- llvm::StringRef OutputFile;
- llvm::StringRef OptRemarksFilename;
- llvm::StringRef ProgName;
- llvm::StringRef SoName;
- llvm::StringRef Sysroot;
- llvm::StringRef ThinLTOCacheDir;
- llvm::StringRef ThinLTOIndexOnlyArg;
- std::pair<llvm::StringRef, llvm::StringRef> ThinLTOObjectSuffixReplace;
- std::pair<llvm::StringRef, llvm::StringRef> ThinLTOPrefixReplace;
- std::string Rpath;
- std::vector<VersionDefinition> VersionDefinitions;
- std::vector<llvm::StringRef> AuxiliaryList;
- std::vector<llvm::StringRef> FilterList;
- std::vector<llvm::StringRef> SearchPaths;
- std::vector<llvm::StringRef> SymbolOrderingFile;
- std::vector<llvm::StringRef> Undefined;
- std::vector<SymbolVersion> DynamicList;
- std::vector<SymbolVersion> VersionScriptGlobals;
- std::vector<SymbolVersion> VersionScriptLocals;
- std::vector<uint8_t> BuildIdVector;
+ uint8_t osabi = 0;
+ uint32_t andFeatures = 0;
+ llvm::CachePruningPolicy thinLTOCachePolicy;
+ llvm::StringMap<uint64_t> sectionStartMap;
+ llvm::StringRef chroot;
+ llvm::StringRef dynamicLinker;
+ llvm::StringRef dwoDir;
+ llvm::StringRef entry;
+ llvm::StringRef emulation;
+ llvm::StringRef fini;
+ llvm::StringRef init;
+ llvm::StringRef ltoAAPipeline;
+ llvm::StringRef ltoCSProfileFile;
+ llvm::StringRef ltoNewPmPasses;
+ llvm::StringRef ltoObjPath;
+ llvm::StringRef ltoSampleProfile;
+ llvm::StringRef mapFile;
+ llvm::StringRef outputFile;
+ llvm::StringRef optRemarksFilename;
+ llvm::StringRef optRemarksPasses;
+ llvm::StringRef optRemarksFormat;
+ llvm::StringRef progName;
+ llvm::StringRef printSymbolOrder;
+ llvm::StringRef soName;
+ llvm::StringRef sysroot;
+ llvm::StringRef thinLTOCacheDir;
+ llvm::StringRef thinLTOIndexOnlyArg;
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+ std::string rpath;
+ std::vector<VersionDefinition> versionDefinitions;
+ std::vector<llvm::StringRef> auxiliaryList;
+ std::vector<llvm::StringRef> filterList;
+ std::vector<llvm::StringRef> searchPaths;
+ std::vector<llvm::StringRef> symbolOrderingFile;
+ std::vector<llvm::StringRef> undefined;
+ std::vector<SymbolVersion> dynamicList;
+ std::vector<SymbolVersion> versionScriptGlobals;
+ std::vector<SymbolVersion> versionScriptLocals;
+ std::vector<uint8_t> buildIdVector;
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
uint64_t>
- CallGraphProfile;
- bool AllowMultipleDefinition;
- bool AndroidPackDynRelocs;
- bool ARMHasBlx = false;
- bool ARMHasMovtMovw = false;
- bool ARMJ1J2BranchEncoding = false;
- bool AsNeeded = false;
- bool Bsymbolic;
- bool BsymbolicFunctions;
- bool CallGraphProfileSort;
- bool CheckSections;
- bool CompressDebugSections;
- bool Cref;
- bool DefineCommon;
- bool Demangle = true;
- bool DisableVerify;
- bool EhFrameHdr;
- bool EmitLLVM;
- bool EmitRelocs;
- bool EnableNewDtags;
- bool ExecuteOnly;
- bool ExportDynamic;
- bool FixCortexA53Errata843419;
- bool FormatBinary = false;
- bool GcSections;
- bool GdbIndex;
- bool GnuHash = false;
- bool GnuUnique;
- bool HasDynamicList = false;
- bool HasDynSymTab;
- bool IgnoreDataAddressEquality;
- bool IgnoreFunctionAddressEquality;
- bool LTODebugPassManager;
- bool LTONewPassManager;
- bool MergeArmExidx;
- bool MipsN32Abi = false;
- bool NoinhibitExec;
- bool Nostdlib;
- bool OFormatBinary;
- bool Omagic;
- bool OptRemarksWithHotness;
- bool Pie;
- bool PrintGcSections;
- bool PrintIcfSections;
- bool Relocatable;
- bool RelrPackDynRelocs;
- bool SaveTemps;
- bool SingleRoRx;
- bool Shared;
- bool Static = false;
- bool SysvHash = false;
- bool Target1Rel;
- bool Trace;
- bool ThinLTOEmitImportsFiles;
- bool ThinLTOIndexOnly;
- bool TocOptimize;
- bool UndefinedVersion;
- bool UseAndroidRelrTags = false;
- bool WarnBackrefs;
- bool WarnCommon;
- bool WarnIfuncTextrel;
- bool WarnMissingEntry;
- bool WarnSymbolOrdering;
- bool WriteAddends;
- bool ZCombreloc;
- bool ZCopyreloc;
- bool ZExecstack;
- bool ZGlobal;
- bool ZHazardplt;
- bool ZInitfirst;
- bool ZInterpose;
- bool ZKeepTextSectionPrefix;
- bool ZNodefaultlib;
- bool ZNodelete;
- bool ZNodlopen;
- bool ZNow;
- bool ZOrigin;
- bool ZRelro;
- bool ZRodynamic;
- bool ZText;
- bool ZRetpolineplt;
- bool ZWxneeded;
- DiscardPolicy Discard;
- ICFLevel ICF;
- OrphanHandlingPolicy OrphanHandling;
- SortSectionPolicy SortSection;
- StripPolicy Strip;
- UnresolvedPolicy UnresolvedSymbols;
- Target2Policy Target2;
- ARMVFPArgKind ARMVFPArgs = ARMVFPArgKind::Default;
- BuildIdKind BuildId = BuildIdKind::None;
- ELFKind EKind = ELFNoneKind;
- uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
- uint16_t EMachine = llvm::ELF::EM_NONE;
- llvm::Optional<uint64_t> ImageBase;
- uint64_t MaxPageSize;
- uint64_t MipsGotSize;
- uint64_t ZStackSize;
- unsigned LTOPartitions;
- unsigned LTOO;
- unsigned Optimize;
- unsigned ThinLTOJobs;
- int32_t SplitStackAdjustSize;
+ callGraphProfile;
+ bool allowMultipleDefinition;
+ bool allowShlibUndefined;
+ bool androidPackDynRelocs;
+ bool armHasBlx = false;
+ bool armHasMovtMovw = false;
+ bool armJ1J2BranchEncoding = false;
+ bool asNeeded = false;
+ bool bsymbolic;
+ bool bsymbolicFunctions;
+ bool callGraphProfileSort;
+ bool checkSections;
+ bool compressDebugSections;
+ bool cref;
+ bool defineCommon;
+ bool demangle = true;
+ bool dependentLibraries;
+ bool disableVerify;
+ bool ehFrameHdr;
+ bool emitLLVM;
+ bool emitRelocs;
+ bool enableNewDtags;
+ bool executeOnly;
+ bool exportDynamic;
+ bool fixCortexA53Errata843419;
+ bool forceBTI;
+ bool formatBinary = false;
+ bool requireCET;
+ bool gcSections;
+ bool gdbIndex;
+ bool gnuHash = false;
+ bool gnuUnique;
+ bool hasDynamicList = false;
+ bool hasDynSymTab;
+ bool ignoreDataAddressEquality;
+ bool ignoreFunctionAddressEquality;
+ bool ltoCSProfileGenerate;
+ bool ltoDebugPassManager;
+ bool ltoNewPassManager;
+ bool mergeArmExidx;
+ bool mipsN32Abi = false;
+ bool nmagic;
+ bool noinhibitExec;
+ bool nostdlib;
+ bool oFormatBinary;
+ bool omagic;
+ bool optRemarksWithHotness;
+ bool pacPlt;
+ bool picThunk;
+ bool pie;
+ bool printGcSections;
+ bool printIcfSections;
+ bool relocatable;
+ bool relrPackDynRelocs;
+ bool saveTemps;
+ bool singleRoRx;
+ bool shared;
+ bool isStatic = false;
+ bool sysvHash = false;
+ bool target1Rel;
+ bool trace;
+ bool thinLTOEmitImportsFiles;
+ bool thinLTOIndexOnly;
+ bool tocOptimize;
+ bool undefinedVersion;
+ bool useAndroidRelrTags = false;
+ bool warnBackrefs;
+ bool warnCommon;
+ bool warnIfuncTextrel;
+ bool warnMissingEntry;
+ bool warnSymbolOrdering;
+ bool writeAddends;
+ bool zCombreloc;
+ bool zCopyreloc;
+ bool zExecstack;
+ bool zGlobal;
+ bool zHazardplt;
+ bool zIfuncNoplt;
+ bool zInitfirst;
+ bool zInterpose;
+ bool zKeepTextSectionPrefix;
+ bool zNodefaultlib;
+ bool zNodelete;
+ bool zNodlopen;
+ bool zNow;
+ bool zOrigin;
+ bool zRelro;
+ bool zRodynamic;
+ bool zText;
+ bool zRetpolineplt;
+ bool zWxneeded;
+ DiscardPolicy discard;
+ ICFLevel icf;
+ OrphanHandlingPolicy orphanHandling;
+ SortSectionPolicy sortSection;
+ StripPolicy strip;
+ UnresolvedPolicy unresolvedSymbols;
+ Target2Policy target2;
+ ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
+ BuildIdKind buildId = BuildIdKind::None;
+ ELFKind ekind = ELFNoneKind;
+ uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
+ uint16_t emachine = llvm::ELF::EM_NONE;
+ llvm::Optional<uint64_t> imageBase;
+ uint64_t commonPageSize;
+ uint64_t maxPageSize;
+ uint64_t mipsGotSize;
+ uint64_t zStackSize;
+ unsigned ltoPartitions;
+ unsigned ltoo;
+ unsigned optimize;
+ unsigned thinLTOJobs;
+ int32_t splitStackAdjustSize;
// The following config options do not directly correspond to any
// particualr command line options.
// True if we need to pass through relocations in input files to the
// output file. Usually false because we consume relocations.
- bool CopyRelocs;
+ bool copyRelocs;
// True if the target is ELF64. False if ELF32.
- bool Is64;
+ bool is64;
// True if the target is little-endian. False if big-endian.
- bool IsLE;
+ bool isLE;
- // endianness::little if IsLE is true. endianness::big otherwise.
- llvm::support::endianness Endianness;
+ // endianness::little if isLE is true. endianness::big otherwise.
+ llvm::support::endianness endianness;
// True if the target is the little-endian MIPS64.
//
@@ -250,10 +264,24 @@ struct Configuration {
// name whatever that means. A fun hypothesis is that "EL" is short for
// little-endian written in the little-endian order, but I don't know
// if that's true.)
- bool IsMips64EL;
+ bool isMips64EL;
+
+ // True if we need to set the DF_STATIC_TLS flag to an output file,
+ // which works as a hint to the dynamic loader that the file contains
+ // code compiled with the static TLS model. The thread-local variable
+ // compiled with the static TLS model is faster but less flexible, and
+ // it may not be loaded using dlopen().
+ //
+ // We set this flag to true when we see a relocation for the static TLS
+ // model. Once this becomes true, it will never become false.
+ //
+ // Since the flag is updated by multi-threaded code, we use std::atomic.
+ // (Writing to a variable is not considered thread-safe even if the
+ // variable is boolean and we always set the same value from all threads.)
+ std::atomic<bool> hasStaticTlsModel{false};
// Holds set of ELF header flags for the target.
- uint32_t EFlags = 0;
+ uint32_t eflags = 0;
// The ELF spec defines two types of relocation table entries, RELA and
// REL. RELA is a triplet of (offset, info, addend) while REL is a
@@ -269,23 +297,23 @@ struct Configuration {
// Each ABI defines its relocation type. IsRela is true if target
// uses RELA. As far as we know, all 64-bit ABIs are using RELA. A
// few 32-bit ABIs are using RELA too.
- bool IsRela;
+ bool isRela;
// True if we are creating position-independent code.
- bool Pic;
+ bool isPic;
// 4 for ELF32, 8 for ELF64.
- int Wordsize;
+ int wordsize;
};
// The only instance of Configuration struct.
-extern Configuration *Config;
+extern Configuration *config;
-static inline void errorOrWarn(const Twine &Msg) {
- if (!Config->NoinhibitExec)
- error(Msg);
+static inline void errorOrWarn(const Twine &msg) {
+ if (!config->noinhibitExec)
+ error(msg);
else
- warn(Msg);
+ warn(msg);
}
} // namespace elf
} // namespace lld
diff --git a/ELF/DWARF.cpp b/ELF/DWARF.cpp
index 17e1a4d600eb..1e4b36f71b54 100644
--- a/ELF/DWARF.cpp
+++ b/ELF/DWARF.cpp
@@ -1,9 +1,8 @@
//===- DWARF.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -26,81 +25,102 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
- for (InputSectionBase *Sec : Obj->getSections()) {
- if (!Sec)
+template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
+ for (InputSectionBase *sec : obj->getSections()) {
+ if (!sec)
continue;
- if (LLDDWARFSection *M =
- StringSwitch<LLDDWARFSection *>(Sec->Name)
- .Case(".debug_addr", &AddrSection)
- .Case(".debug_gnu_pubnames", &GnuPubNamesSection)
- .Case(".debug_gnu_pubtypes", &GnuPubTypesSection)
- .Case(".debug_info", &InfoSection)
- .Case(".debug_ranges", &RangeSection)
- .Case(".debug_rnglists", &RngListsSection)
- .Case(".debug_line", &LineSection)
+ if (LLDDWARFSection *m =
+ StringSwitch<LLDDWARFSection *>(sec->name)
+ .Case(".debug_addr", &addrSection)
+ .Case(".debug_gnu_pubnames", &gnuPubNamesSection)
+ .Case(".debug_gnu_pubtypes", &gnuPubTypesSection)
+ .Case(".debug_info", &infoSection)
+ .Case(".debug_ranges", &rangeSection)
+ .Case(".debug_rnglists", &rngListsSection)
+ .Case(".debug_line", &lineSection)
.Default(nullptr)) {
- M->Data = toStringRef(Sec->data());
- M->Sec = Sec;
+ m->Data = toStringRef(sec->data());
+ m->sec = sec;
continue;
}
- if (Sec->Name == ".debug_abbrev")
- AbbrevSection = toStringRef(Sec->data());
- else if (Sec->Name == ".debug_str")
- StrSection = toStringRef(Sec->data());
- else if (Sec->Name == ".debug_line_str")
- LineStringSection = toStringRef(Sec->data());
+ if (sec->name == ".debug_abbrev")
+ abbrevSection = toStringRef(sec->data());
+ else if (sec->name == ".debug_str")
+ strSection = toStringRef(sec->data());
+ else if (sec->name == ".debug_line_str")
+ lineStringSection = toStringRef(sec->data());
}
}
+namespace {
+template <class RelTy> struct LLDRelocationResolver {
+ // In the ELF ABIs, S sepresents the value of the symbol in the relocation
+ // entry. For Rela, the addend is stored as part of the relocation entry.
+ static uint64_t resolve(object::RelocationRef ref, uint64_t s,
+ uint64_t /* A */) {
+ return s + ref.getRawDataRefImpl().p;
+ }
+};
+
+template <class ELFT> struct LLDRelocationResolver<Elf_Rel_Impl<ELFT, false>> {
+ // For Rel, the addend A is supplied by the caller.
+ static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s,
+ uint64_t a) {
+ return s + a;
+ }
+};
+} // namespace
+
// Find if there is a relocation at Pos in Sec. The code is a bit
// more complicated than usual because we need to pass a section index
// to llvm since it has no idea about InputSection.
template <class ELFT>
template <class RelTy>
Optional<RelocAddrEntry>
-LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos,
- ArrayRef<RelTy> Rels) const {
- auto It = std::lower_bound(
- Rels.begin(), Rels.end(), Pos,
- [](const RelTy &A, uint64_t B) { return A.r_offset < B; });
- if (It == Rels.end() || It->r_offset != Pos)
+LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos,
+ ArrayRef<RelTy> rels) const {
+ auto it =
+ partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; });
+ if (it == rels.end() || it->r_offset != pos)
return None;
- const RelTy &Rel = *It;
+ const RelTy &rel = *it;
- const ObjFile<ELFT> *File = Sec.getFile<ELFT>();
- uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
- const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex];
- uint32_t SecIndex = File->getSectionIndex(Sym);
+ const ObjFile<ELFT> *file = sec.getFile<ELFT>();
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
+ const typename ELFT::Sym &sym = file->template getELFSyms<ELFT>()[symIndex];
+ uint32_t secIndex = file->getSectionIndex(sym);
- // Broken debug info can point to a non-Defined symbol.
- auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel));
- if (!DR) {
- RelType Type = Rel.getType(Config->IsMips64EL);
- if (Type != Target->NoneRel)
- error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" +
- llvm::utohexstr(Rel.r_offset) + " has unsupported target");
- return None;
- }
- uint64_t Val = DR->Value + getAddend<ELFT>(Rel);
+ // An undefined symbol may be a symbol defined in a discarded section. We
+ // shall still resolve it. This is important for --gdb-index: the end address
+ // offset of an entry in .debug_ranges is relocated. If it is not resolved,
+ // 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)) {
+ 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();
+ // 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();
+ }
- return RelocAddrEntry{SecIndex, Val};
+ DataRefImpl d;
+ d.p = getAddend<ELFT>(rel);
+ return RelocAddrEntry{secIndex, RelocationRef(d, nullptr),
+ val, Optional<object::RelocationRef>(),
+ 0, LLDRelocationResolver<RelTy>::resolve};
}
template <class ELFT>
-Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &S,
- uint64_t Pos) const {
- auto &Sec = static_cast<const LLDDWARFSection &>(S);
- if (Sec.Sec->AreRelocsRela)
- return findAux(*Sec.Sec, Pos, Sec.Sec->template relas<ELFT>());
- return findAux(*Sec.Sec, Pos, Sec.Sec->template rels<ELFT>());
+Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s,
+ uint64_t pos) const {
+ auto &sec = static_cast<const LLDDWARFSection &>(s);
+ if (sec.sec->areRelocsRela)
+ return findAux(*sec.sec, pos, sec.sec->template relas<ELFT>());
+ return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
}
template class elf::LLDDwarfObj<ELF32LE>;
diff --git a/ELF/DWARF.h b/ELF/DWARF.h
index 8ecf02c77fb4..426022945007 100644
--- a/ELF/DWARF.h
+++ b/ELF/DWARF.h
@@ -1,9 +1,8 @@
//===- DWARF.h -----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===-------------------------------------------------------------------===//
@@ -21,70 +20,70 @@ namespace elf {
class InputSection;
struct LLDDWARFSection final : public llvm::DWARFSection {
- InputSectionBase *Sec = nullptr;
+ InputSectionBase *sec = nullptr;
};
template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
public:
- explicit LLDDwarfObj(ObjFile<ELFT> *Obj);
+ explicit LLDDwarfObj(ObjFile<ELFT> *obj);
void forEachInfoSections(
- llvm::function_ref<void(const llvm::DWARFSection &)> F) const override {
- F(InfoSection);
+ llvm::function_ref<void(const llvm::DWARFSection &)> f) const override {
+ f(infoSection);
}
const llvm::DWARFSection &getRangeSection() const override {
- return RangeSection;
+ return rangeSection;
}
const llvm::DWARFSection &getRnglistsSection() const override {
- return RngListsSection;
+ return rngListsSection;
}
const llvm::DWARFSection &getLineSection() const override {
- return LineSection;
+ return lineSection;
}
const llvm::DWARFSection &getAddrSection() const override {
- return AddrSection;
+ return addrSection;
}
const llvm::DWARFSection &getGnuPubNamesSection() const override {
- return GnuPubNamesSection;
+ return gnuPubNamesSection;
}
const llvm::DWARFSection &getGnuPubTypesSection() const override {
- return GnuPubTypesSection;
+ return gnuPubTypesSection;
}
StringRef getFileName() const override { return ""; }
- StringRef getAbbrevSection() const override { return AbbrevSection; }
- StringRef getStringSection() const override { return StrSection; }
- StringRef getLineStringSection() const override { return LineStringSection; }
+ StringRef getAbbrevSection() const override { return abbrevSection; }
+ StringRef getStringSection() const override { return strSection; }
+ StringRef getLineStringSection() const override { return lineStringSection; }
bool isLittleEndian() const override {
return ELFT::TargetEndianness == llvm::support::little;
}
- llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec,
- uint64_t Pos) const override;
+ llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec,
+ uint64_t pos) const override;
private:
template <class RelTy>
- llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
- uint64_t Pos,
- ArrayRef<RelTy> Rels) const;
-
- LLDDWARFSection GnuPubNamesSection;
- LLDDWARFSection GnuPubTypesSection;
- LLDDWARFSection InfoSection;
- LLDDWARFSection RangeSection;
- LLDDWARFSection RngListsSection;
- LLDDWARFSection LineSection;
- LLDDWARFSection AddrSection;
- StringRef AbbrevSection;
- StringRef StrSection;
- StringRef LineStringSection;
+ llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &sec,
+ uint64_t pos,
+ ArrayRef<RelTy> rels) const;
+
+ LLDDWARFSection gnuPubNamesSection;
+ LLDDWARFSection gnuPubTypesSection;
+ LLDDWARFSection infoSection;
+ LLDDWARFSection rangeSection;
+ LLDDWARFSection rngListsSection;
+ LLDDWARFSection lineSection;
+ LLDDWARFSection addrSection;
+ StringRef abbrevSection;
+ StringRef strSection;
+ StringRef lineStringSection;
};
} // namespace elf
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 13b6119e2dc9..fbfc71d22b7e 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -1,9 +1,8 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -25,7 +24,6 @@
#include "Driver.h"
#include "Config.h"
-#include "Filesystem.h"
#include "ICF.h"
#include "InputFiles.h"
#include "InputSection.h"
@@ -41,6 +39,7 @@
#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
@@ -51,6 +50,7 @@
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
+#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
@@ -68,44 +68,49 @@ using namespace llvm::support;
using namespace lld;
using namespace lld::elf;
-Configuration *elf::Config;
-LinkerDriver *elf::Driver;
+Configuration *elf::config;
+LinkerDriver *elf::driver;
-static void setConfigs(opt::InputArgList &Args);
+static void setConfigs(opt::InputArgList &args);
+static void readConfigs(opt::InputArgList &args);
-bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
- raw_ostream &Error) {
- errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
- errorHandler().ErrorLimitExceededMsg =
+bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
+ raw_ostream &error) {
+ errorHandler().logName = args::getFilenameWithoutExe(args[0]);
+ errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now (use "
"-error-limit=0 to see all errors)";
- errorHandler().ErrorOS = &Error;
- errorHandler().ExitEarly = CanExitEarly;
- errorHandler().ColorDiagnostics = Error.has_colors();
+ errorHandler().errorOS = &error;
+ errorHandler().exitEarly = canExitEarly;
+ errorHandler().colorDiagnostics = error.has_colors();
+
+ inputSections.clear();
+ outputSections.clear();
+ binaryFiles.clear();
+ bitcodeFiles.clear();
+ objectFiles.clear();
+ sharedFiles.clear();
+
+ config = make<Configuration>();
+ driver = make<LinkerDriver>();
+ script = make<LinkerScript>();
+ symtab = make<SymbolTable>();
- InputSections.clear();
- OutputSections.clear();
- BinaryFiles.clear();
- BitcodeFiles.clear();
- ObjectFiles.clear();
- SharedFiles.clear();
+ tar = nullptr;
+ memset(&in, 0, sizeof(in));
- Config = make<Configuration>();
- Driver = make<LinkerDriver>();
- Script = make<LinkerScript>();
- Symtab = make<SymbolTable>();
+ partitions = {Partition()};
- Tar = nullptr;
- memset(&In, 0, sizeof(In));
+ SharedFile::vernauxNum = 0;
- Config->ProgName = Args[0];
+ config->progName = args[0];
- Driver->main(Args);
+ driver->main(args);
// Exit immediately if we don't need to return to the caller.
// This saves time because the overhead of calling destructors
// for all globally-allocated objects is not negligible.
- if (CanExitEarly)
+ if (canExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
@@ -113,16 +118,16 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
}
// Parses a linker -m option.
-static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
- uint8_t OSABI = 0;
- StringRef S = Emul;
- if (S.endswith("_fbsd")) {
- S = S.drop_back(5);
- OSABI = ELFOSABI_FREEBSD;
+static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) {
+ uint8_t osabi = 0;
+ StringRef s = emul;
+ if (s.endswith("_fbsd")) {
+ s = s.drop_back(5);
+ osabi = ELFOSABI_FREEBSD;
}
- std::pair<ELFKind, uint16_t> Ret =
- StringSwitch<std::pair<ELFKind, uint16_t>>(S)
+ std::pair<ELFKind, uint16_t> ret =
+ StringSwitch<std::pair<ELFKind, uint16_t>>(s)
.Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec",
{ELF64LEKind, EM_AARCH64})
.Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM})
@@ -130,7 +135,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
.Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
.Case("elf32lriscv", {ELF32LEKind, EM_RISCV})
- .Case("elf32ppc", {ELF32BEKind, EM_PPC})
+ .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64lriscv", {ELF64LEKind, EM_RISCV})
@@ -141,91 +146,101 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
.Default({ELFNoneKind, EM_NONE});
- if (Ret.first == ELFNoneKind)
- error("unknown emulation: " + Emul);
- return std::make_tuple(Ret.first, Ret.second, OSABI);
+ if (ret.first == ELFNoneKind)
+ error("unknown emulation: " + emul);
+ return std::make_tuple(ret.first, ret.second, osabi);
}
// Returns slices of MB by parsing MB as an archive file.
// Each slice consists of a member file in the archive.
std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
- MemoryBufferRef MB) {
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MB),
- MB.getBufferIdentifier() + ": failed to parse archive");
-
- std::vector<std::pair<MemoryBufferRef, uint64_t>> V;
- Error Err = Error::success();
- bool AddToTar = File->isThin() && Tar;
- for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
- Archive::Child C =
- CHECK(COrErr, MB.getBufferIdentifier() +
+ MemoryBufferRef mb) {
+ std::unique_ptr<Archive> file =
+ CHECK(Archive::create(mb),
+ mb.getBufferIdentifier() + ": failed to parse archive");
+
+ std::vector<std::pair<MemoryBufferRef, uint64_t>> v;
+ Error err = Error::success();
+ bool addToTar = file->isThin() && tar;
+ for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) {
+ Archive::Child c =
+ CHECK(cOrErr, mb.getBufferIdentifier() +
": could not get the child of the archive");
- MemoryBufferRef MBRef =
- CHECK(C.getMemoryBufferRef(),
- MB.getBufferIdentifier() +
+ MemoryBufferRef mbref =
+ CHECK(c.getMemoryBufferRef(),
+ mb.getBufferIdentifier() +
": could not get the buffer for a child of the archive");
- if (AddToTar)
- Tar->append(relativeToRoot(check(C.getFullName())), MBRef.getBuffer());
- V.push_back(std::make_pair(MBRef, C.getChildOffset()));
+ if (addToTar)
+ tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer());
+ v.push_back(std::make_pair(mbref, c.getChildOffset()));
}
- if (Err)
- fatal(MB.getBufferIdentifier() + ": Archive::children failed: " +
- toString(std::move(Err)));
+ if (err)
+ fatal(mb.getBufferIdentifier() + ": Archive::children failed: " +
+ toString(std::move(err)));
// Take ownership of memory buffers created for members of thin archives.
- for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers())
- make<std::unique_ptr<MemoryBuffer>>(std::move(MB));
+ for (std::unique_ptr<MemoryBuffer> &mb : file->takeThinBuffers())
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb));
- return V;
+ return v;
}
// Opens a file and create a file object. Path has to be resolved already.
-void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
+void LinkerDriver::addFile(StringRef path, bool withLOption) {
using namespace sys::fs;
- Optional<MemoryBufferRef> Buffer = readFile(Path);
- if (!Buffer.hasValue())
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer.hasValue())
return;
- MemoryBufferRef MBRef = *Buffer;
+ MemoryBufferRef mbref = *buffer;
- if (Config->FormatBinary) {
- Files.push_back(make<BinaryFile>(MBRef));
+ if (config->formatBinary) {
+ files.push_back(make<BinaryFile>(mbref));
return;
}
- switch (identify_magic(MBRef.getBuffer())) {
+ switch (identify_magic(mbref.getBuffer())) {
case file_magic::unknown:
- readLinkerScript(MBRef);
+ readLinkerScript(mbref);
return;
case file_magic::archive: {
// Handle -whole-archive.
- if (InWholeArchive) {
- for (const auto &P : getArchiveMembers(MBRef))
- Files.push_back(createObjectFile(P.first, Path, P.second));
+ if (inWholeArchive) {
+ for (const auto &p : getArchiveMembers(mbref))
+ files.push_back(createObjectFile(p.first, path, p.second));
return;
}
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MBRef), Path + ": failed to parse archive");
+ std::unique_ptr<Archive> file =
+ CHECK(Archive::create(mbref), path + ": failed to parse archive");
// If an archive file has no symbol table, it is likely that a user
// is attempting LTO and using a default ar command that doesn't
// understand the LLVM bitcode file. It is a pretty common error, so
// we'll handle it as if it had a symbol table.
- if (!File->isEmpty() && !File->hasSymbolTable()) {
- for (const auto &P : getArchiveMembers(MBRef))
- Files.push_back(make<LazyObjFile>(P.first, Path, P.second));
+ if (!file->isEmpty() && !file->hasSymbolTable()) {
+ // Check if all members are bitcode files. If not, ignore, which is the
+ // default action without the LTO hack described above.
+ for (const std::pair<MemoryBufferRef, uint64_t> &p :
+ getArchiveMembers(mbref))
+ if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) {
+ error(path + ": archive has no index; run ranlib to add one");
+ return;
+ }
+
+ for (const std::pair<MemoryBufferRef, uint64_t> &p :
+ getArchiveMembers(mbref))
+ files.push_back(make<LazyObjFile>(p.first, path, p.second));
return;
}
// Handle the regular case.
- Files.push_back(make<ArchiveFile>(std::move(File)));
+ files.push_back(make<ArchiveFile>(std::move(file)));
return;
}
case file_magic::elf_shared_object:
- if (Config->Static || Config->Relocatable) {
- error("attempted static link of dynamic object " + Path);
+ if (config->isStatic || config->relocatable) {
+ error("attempted static link of dynamic object " + path);
return;
}
@@ -239,27 +254,27 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
// If a file was specified by -lfoo, the directory part is not
// significant, as a user did not specify it. This behavior is
// compatible with GNU.
- Files.push_back(
- createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path));
+ files.push_back(
+ make<SharedFile>(mbref, withLOption ? path::filename(path) : path));
return;
case file_magic::bitcode:
case file_magic::elf_relocatable:
- if (InLib)
- Files.push_back(make<LazyObjFile>(MBRef, "", 0));
+ if (inLib)
+ files.push_back(make<LazyObjFile>(mbref, "", 0));
else
- Files.push_back(createObjectFile(MBRef));
+ files.push_back(createObjectFile(mbref));
break;
default:
- error(Path + ": unknown file type");
+ error(path + ": unknown file type");
}
}
// Add a given library by searching it from input search paths.
-void LinkerDriver::addLibrary(StringRef Name) {
- if (Optional<std::string> Path = searchLibrary(Name))
- addFile(*Path, /*WithLOption=*/true);
+void LinkerDriver::addLibrary(StringRef name) {
+ if (Optional<std::string> path = searchLibrary(name))
+ addFile(*path, /*withLOption=*/true);
else
- error("unable to find library -l" + Name);
+ error("unable to find library -l" + name);
}
// This function is called on startup. We need this for LTO since
@@ -278,102 +293,117 @@ static void initLLVM() {
static void checkOptions() {
// The MIPS ABI as of 2016 does not support the GNU-style symbol lookup
// table which is a relatively new feature.
- if (Config->EMachine == EM_MIPS && Config->GnuHash)
+ if (config->emachine == EM_MIPS && config->gnuHash)
error("the .gnu.hash section is not compatible with the MIPS target");
- if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64)
+ if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
- if (Config->TocOptimize && Config->EMachine != EM_PPC64)
+ if (config->tocOptimize && config->emachine != EM_PPC64)
error("--toc-optimize is only supported on the PowerPC64 target");
- if (Config->Pie && Config->Shared)
+ if (config->pie && config->shared)
error("-shared and -pie may not be used together");
- if (!Config->Shared && !Config->FilterList.empty())
+ if (!config->shared && !config->filterList.empty())
error("-F may not be used without -shared");
- if (!Config->Shared && !Config->AuxiliaryList.empty())
+ if (!config->shared && !config->auxiliaryList.empty())
error("-f may not be used without -shared");
- if (!Config->Relocatable && !Config->DefineCommon)
+ if (!config->relocatable && !config->defineCommon)
error("-no-define-common not supported in non relocatable output");
- if (Config->Relocatable) {
- if (Config->Shared)
+ if (config->zText && config->zIfuncNoplt)
+ error("-z text and -z ifunc-noplt may not be used together");
+
+ if (config->relocatable) {
+ if (config->shared)
error("-r and -shared may not be used together");
- if (Config->GcSections)
+ if (config->gcSections)
error("-r and --gc-sections may not be used together");
- if (Config->GdbIndex)
+ if (config->gdbIndex)
error("-r and --gdb-index may not be used together");
- if (Config->ICF != ICFLevel::None)
+ if (config->icf != ICFLevel::None)
error("-r and --icf may not be used together");
- if (Config->Pie)
+ if (config->pie)
error("-r and -pie may not be used together");
}
- if (Config->ExecuteOnly) {
- if (Config->EMachine != EM_AARCH64)
+ if (config->executeOnly) {
+ if (config->emachine != EM_AARCH64)
error("-execute-only is only supported on AArch64 targets");
- if (Config->SingleRoRx && !Script->HasSectionsCommand)
+ if (config->singleRoRx && !script->hasSectionsCommand)
error("-execute-only and -no-rosegment cannot be used together");
}
+
+ if (config->zRetpolineplt && config->requireCET)
+ error("--require-cet may not be used with -z retpolineplt");
+
+ if (config->emachine != EM_AARCH64) {
+ if (config->pacPlt)
+ error("--pac-plt only supported on AArch64");
+ if (config->forceBTI)
+ error("--force-bti only supported on AArch64");
+ }
}
-static const char *getReproduceOption(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_reproduce))
- return Arg->getValue();
+static const char *getReproduceOption(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_reproduce))
+ return arg->getValue();
return getenv("LLD_REPRODUCE");
}
-static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
- for (auto *Arg : Args.filtered(OPT_z))
- if (Key == Arg->getValue())
+static bool hasZOption(opt::InputArgList &args, StringRef key) {
+ for (auto *arg : args.filtered(OPT_z))
+ if (key == arg->getValue())
return true;
return false;
}
-static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2,
+static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
bool Default) {
- for (auto *Arg : Args.filtered_reverse(OPT_z)) {
- if (K1 == Arg->getValue())
+ for (auto *arg : args.filtered_reverse(OPT_z)) {
+ if (k1 == arg->getValue())
return true;
- if (K2 == Arg->getValue())
+ if (k2 == arg->getValue())
return false;
}
return Default;
}
-static bool isKnownZFlag(StringRef S) {
- return S == "combreloc" || S == "copyreloc" || S == "defs" ||
- S == "execstack" || S == "global" || S == "hazardplt" ||
- S == "initfirst" || S == "interpose" ||
- S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" ||
- S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" ||
- S == "nodelete" || S == "nodlopen" || S == "noexecstack" ||
- S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" ||
- S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" ||
- S == "rodynamic" || S == "text" || S == "wxneeded" ||
- S.startswith("max-page-size=") || S.startswith("stack-size=");
+static bool isKnownZFlag(StringRef s) {
+ return s == "combreloc" || s == "copyreloc" || s == "defs" ||
+ s == "execstack" || s == "global" || s == "hazardplt" ||
+ s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
+ s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
+ s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
+ s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
+ s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" ||
+ s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" ||
+ s == "rodynamic" || s == "text" || s == "wxneeded" ||
+ s.startswith("common-page-size") || s.startswith("max-page-size=") ||
+ s.startswith("stack-size=");
}
// Report an error for an unknown -z option.
-static void checkZOptions(opt::InputArgList &Args) {
- for (auto *Arg : Args.filtered(OPT_z))
- if (!isKnownZFlag(Arg->getValue()))
- error("unknown -z value: " + StringRef(Arg->getValue()));
+static void checkZOptions(opt::InputArgList &args) {
+ for (auto *arg : args.filtered(OPT_z))
+ if (!isKnownZFlag(arg->getValue()))
+ error("unknown -z value: " + StringRef(arg->getValue()));
}
-void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
- ELFOptTable Parser;
- opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
+void LinkerDriver::main(ArrayRef<const char *> argsArr) {
+ ELFOptTable parser;
+ opt::InputArgList args = parser.parse(argsArr.slice(1));
// Interpret this flag early because error() depends on them.
- errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
+ errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20);
+ checkZOptions(args);
// Handle -help
- if (Args.hasArg(OPT_help)) {
+ if (args.hasArg(OPT_help)) {
printHelp();
return;
}
@@ -393,213 +423,218 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
// lot of "configure" scripts out there that are generated by old version
// of Libtool. We cannot convince every software developer to migrate to
// the latest version and re-generate scripts. So we have this hack.
- if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
+ if (args.hasArg(OPT_v) || args.hasArg(OPT_version))
message(getLLDVersion() + " (compatible with GNU linkers)");
- if (const char *Path = getReproduceOption(Args)) {
+ if (const char *path = getReproduceOption(args)) {
// Note that --reproduce is a debug option so you can ignore it
// if you are trying to understand the whole picture of the code.
- Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
- TarWriter::create(Path, path::stem(Path));
- if (ErrOrWriter) {
- Tar = std::move(*ErrOrWriter);
- Tar->append("response.txt", createResponseFile(Args));
- Tar->append("version.txt", getLLDVersion() + "\n");
+ Expected<std::unique_ptr<TarWriter>> errOrWriter =
+ TarWriter::create(path, path::stem(path));
+ if (errOrWriter) {
+ tar = std::move(*errOrWriter);
+ tar->append("response.txt", createResponseFile(args));
+ tar->append("version.txt", getLLDVersion() + "\n");
} else {
- error("--reproduce: " + toString(ErrOrWriter.takeError()));
+ error("--reproduce: " + toString(errOrWriter.takeError()));
}
}
- readConfigs(Args);
- checkZOptions(Args);
+ readConfigs(args);
// The behavior of -v or --version is a bit strange, but this is
// needed for compatibility with GNU linkers.
- if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT))
+ if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT))
return;
- if (Args.hasArg(OPT_version))
+ if (args.hasArg(OPT_version))
return;
initLLVM();
- createFiles(Args);
+ createFiles(args);
if (errorCount())
return;
inferMachineType();
- setConfigs(Args);
+ setConfigs(args);
checkOptions();
if (errorCount())
return;
- switch (Config->EKind) {
+ // 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);
+ link<ELF32LE>(args);
return;
case ELF32BEKind:
- link<ELF32BE>(Args);
+ link<ELF32BE>(args);
return;
case ELF64LEKind:
- link<ELF64LE>(Args);
+ link<ELF64LE>(args);
return;
case ELF64BEKind:
- link<ELF64BE>(Args);
+ link<ELF64BE>(args);
return;
default:
llvm_unreachable("unknown Config->EKind");
}
}
-static std::string getRpath(opt::InputArgList &Args) {
- std::vector<StringRef> V = args::getStrings(Args, OPT_rpath);
- return llvm::join(V.begin(), V.end(), ":");
+static std::string getRpath(opt::InputArgList &args) {
+ std::vector<StringRef> v = args::getStrings(args, OPT_rpath);
+ return llvm::join(v.begin(), v.end(), ":");
}
// Determines what we should do if there are remaining unresolved
// symbols after the name resolution.
-static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
- UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols,
+static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
+ UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols,
OPT_warn_unresolved_symbols, true)
? UnresolvedPolicy::ReportError
: UnresolvedPolicy::Warn;
// Process the last of -unresolved-symbols, -no-undefined or -z defs.
- for (auto *Arg : llvm::reverse(Args)) {
- switch (Arg->getOption().getID()) {
+ for (auto *arg : llvm::reverse(args)) {
+ switch (arg->getOption().getID()) {
case OPT_unresolved_symbols: {
- StringRef S = Arg->getValue();
- if (S == "ignore-all" || S == "ignore-in-object-files")
+ StringRef s = arg->getValue();
+ if (s == "ignore-all" || s == "ignore-in-object-files")
return UnresolvedPolicy::Ignore;
- if (S == "ignore-in-shared-libs" || S == "report-all")
- return ErrorOrWarn;
- error("unknown --unresolved-symbols value: " + S);
+ if (s == "ignore-in-shared-libs" || s == "report-all")
+ return errorOrWarn;
+ error("unknown --unresolved-symbols value: " + s);
continue;
}
case OPT_no_undefined:
- return ErrorOrWarn;
+ return errorOrWarn;
case OPT_z:
- if (StringRef(Arg->getValue()) == "defs")
- return ErrorOrWarn;
+ if (StringRef(arg->getValue()) == "defs")
+ return errorOrWarn;
continue;
}
}
// -shared implies -unresolved-symbols=ignore-all because missing
// symbols are likely to be resolved at runtime using other DSOs.
- if (Config->Shared)
+ if (config->shared)
return UnresolvedPolicy::Ignore;
- return ErrorOrWarn;
+ return errorOrWarn;
}
-static Target2Policy getTarget2(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_target2, "got-rel");
- if (S == "rel")
+static Target2Policy getTarget2(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_target2, "got-rel");
+ if (s == "rel")
return Target2Policy::Rel;
- if (S == "abs")
+ if (s == "abs")
return Target2Policy::Abs;
- if (S == "got-rel")
+ if (s == "got-rel")
return Target2Policy::GotRel;
- error("unknown --target2 option: " + S);
+ error("unknown --target2 option: " + s);
return Target2Policy::GotRel;
}
-static bool isOutputFormatBinary(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_oformat, "elf");
- if (S == "binary")
+static bool isOutputFormatBinary(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_oformat, "elf");
+ if (s == "binary")
return true;
- if (!S.startswith("elf"))
- error("unknown --oformat value: " + S);
+ if (!s.startswith("elf"))
+ error("unknown --oformat value: " + s);
return false;
}
-static DiscardPolicy getDiscard(opt::InputArgList &Args) {
- if (Args.hasArg(OPT_relocatable))
+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)
+ auto *arg =
+ args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
+ if (!arg)
return DiscardPolicy::Default;
- if (Arg->getOption().getID() == OPT_discard_all)
+ if (arg->getOption().getID() == OPT_discard_all)
return DiscardPolicy::All;
- if (Arg->getOption().getID() == OPT_discard_locals)
+ if (arg->getOption().getID() == OPT_discard_locals)
return DiscardPolicy::Locals;
return DiscardPolicy::None;
}
-static StringRef getDynamicLinker(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker);
- if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker)
+static StringRef getDynamicLinker(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker);
+ if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker)
return "";
- return Arg->getValue();
+ return arg->getValue();
}
-static ICFLevel getICF(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all);
- if (!Arg || Arg->getOption().getID() == OPT_icf_none)
+static ICFLevel getICF(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all);
+ if (!arg || arg->getOption().getID() == OPT_icf_none)
return ICFLevel::None;
- if (Arg->getOption().getID() == OPT_icf_safe)
+ if (arg->getOption().getID() == OPT_icf_safe)
return ICFLevel::Safe;
return ICFLevel::All;
}
-static StripPolicy getStrip(opt::InputArgList &Args) {
- if (Args.hasArg(OPT_relocatable))
+static StripPolicy getStrip(opt::InputArgList &args) {
+ if (args.hasArg(OPT_relocatable))
return StripPolicy::None;
- auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug);
- if (!Arg)
+ auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug);
+ if (!arg)
return StripPolicy::None;
- if (Arg->getOption().getID() == OPT_strip_all)
+ if (arg->getOption().getID() == OPT_strip_all)
return StripPolicy::All;
return StripPolicy::Debug;
}
-static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) {
- uint64_t VA = 0;
- if (S.startswith("0x"))
- S = S.drop_front(2);
- if (!to_integer(S, VA, 16))
- error("invalid argument: " + toString(Arg));
- return VA;
+static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args,
+ const opt::Arg &arg) {
+ uint64_t va = 0;
+ if (s.startswith("0x"))
+ s = s.drop_front(2);
+ if (!to_integer(s, va, 16))
+ error("invalid argument: " + arg.getAsString(args));
+ return va;
}
-static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
- StringMap<uint64_t> Ret;
- for (auto *Arg : Args.filtered(OPT_section_start)) {
- StringRef Name;
- StringRef Addr;
- std::tie(Name, Addr) = StringRef(Arg->getValue()).split('=');
- Ret[Name] = parseSectionAddress(Addr, *Arg);
+static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &args) {
+ StringMap<uint64_t> ret;
+ for (auto *arg : args.filtered(OPT_section_start)) {
+ StringRef name;
+ StringRef addr;
+ std::tie(name, addr) = StringRef(arg->getValue()).split('=');
+ ret[name] = parseSectionAddress(addr, args, *arg);
}
- if (auto *Arg = Args.getLastArg(OPT_Ttext))
- Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg);
- if (auto *Arg = Args.getLastArg(OPT_Tdata))
- Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg);
- if (auto *Arg = Args.getLastArg(OPT_Tbss))
- Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg);
- return Ret;
+ if (auto *arg = args.getLastArg(OPT_Ttext))
+ ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg);
+ if (auto *arg = args.getLastArg(OPT_Tdata))
+ ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg);
+ if (auto *arg = args.getLastArg(OPT_Tbss))
+ ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg);
+ return ret;
}
-static SortSectionPolicy getSortSection(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_sort_section);
- if (S == "alignment")
+static SortSectionPolicy getSortSection(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_sort_section);
+ if (s == "alignment")
return SortSectionPolicy::Alignment;
- if (S == "name")
+ if (s == "name")
return SortSectionPolicy::Name;
- if (!S.empty())
- error("unknown --sort-section rule: " + S);
+ if (!s.empty())
+ error("unknown --sort-section rule: " + s);
return SortSectionPolicy::Default;
}
-static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place");
- if (S == "warn")
+static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_orphan_handling, "place");
+ if (s == "warn")
return OrphanHandlingPolicy::Warn;
- if (S == "error")
+ if (s == "error")
return OrphanHandlingPolicy::Error;
- if (S != "place")
- error("unknown --orphan-handling mode: " + S);
+ if (s != "place")
+ error("unknown --orphan-handling mode: " + s);
return OrphanHandlingPolicy::Place;
}
@@ -607,388 +642,412 @@ static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) {
// synonym for "sha1" because all our hash functions including
// -build-id=sha1 are actually tree hashes for performance reasons.
static std::pair<BuildIdKind, std::vector<uint8_t>>
-getBuildId(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_build_id, OPT_build_id_eq);
- if (!Arg)
+getBuildId(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_build_id, OPT_build_id_eq);
+ if (!arg)
return {BuildIdKind::None, {}};
- if (Arg->getOption().getID() == OPT_build_id)
+ if (arg->getOption().getID() == OPT_build_id)
return {BuildIdKind::Fast, {}};
- StringRef S = Arg->getValue();
- if (S == "fast")
+ StringRef s = arg->getValue();
+ if (s == "fast")
return {BuildIdKind::Fast, {}};
- if (S == "md5")
+ if (s == "md5")
return {BuildIdKind::Md5, {}};
- if (S == "sha1" || S == "tree")
+ if (s == "sha1" || s == "tree")
return {BuildIdKind::Sha1, {}};
- if (S == "uuid")
+ if (s == "uuid")
return {BuildIdKind::Uuid, {}};
- if (S.startswith("0x"))
- return {BuildIdKind::Hexstring, parseHex(S.substr(2))};
+ if (s.startswith("0x"))
+ return {BuildIdKind::Hexstring, parseHex(s.substr(2))};
- if (S != "none")
- error("unknown --build-id style: " + S);
+ if (s != "none")
+ error("unknown --build-id style: " + s);
return {BuildIdKind::None, {}};
}
-static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_pack_dyn_relocs, "none");
- if (S == "android")
+static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_pack_dyn_relocs, "none");
+ if (s == "android")
return {true, false};
- if (S == "relr")
+ if (s == "relr")
return {false, true};
- if (S == "android+relr")
+ if (s == "android+relr")
return {true, true};
- if (S != "none")
- error("unknown -pack-dyn-relocs format: " + S);
+ if (s != "none")
+ error("unknown -pack-dyn-relocs format: " + s);
return {false, false};
}
-static void readCallGraph(MemoryBufferRef MB) {
+static void readCallGraph(MemoryBufferRef mb) {
// Build a map from symbol name to section
- DenseMap<StringRef, Symbol *> Map;
- for (InputFile *File : ObjectFiles)
- for (Symbol *Sym : File->getSymbols())
- Map[Sym->getName()] = Sym;
-
- auto FindSection = [&](StringRef Name) -> InputSectionBase * {
- Symbol *Sym = Map.lookup(Name);
- if (!Sym) {
- if (Config->WarnSymbolOrdering)
- warn(MB.getBufferIdentifier() + ": no such symbol: " + Name);
+ DenseMap<StringRef, Symbol *> map;
+ for (InputFile *file : objectFiles)
+ for (Symbol *sym : file->getSymbols())
+ map[sym->getName()] = sym;
+
+ auto findSection = [&](StringRef name) -> InputSectionBase * {
+ Symbol *sym = map.lookup(name);
+ if (!sym) {
+ if (config->warnSymbolOrdering)
+ warn(mb.getBufferIdentifier() + ": no such symbol: " + name);
return nullptr;
}
- maybeWarnUnorderableSymbol(Sym);
+ maybeWarnUnorderableSymbol(sym);
- if (Defined *DR = dyn_cast_or_null<Defined>(Sym))
- return dyn_cast_or_null<InputSectionBase>(DR->Section);
+ if (Defined *dr = dyn_cast_or_null<Defined>(sym))
+ return dyn_cast_or_null<InputSectionBase>(dr->section);
return nullptr;
};
- for (StringRef Line : args::getLines(MB)) {
- SmallVector<StringRef, 3> Fields;
- Line.split(Fields, ' ');
- uint64_t Count;
+ for (StringRef line : args::getLines(mb)) {
+ SmallVector<StringRef, 3> fields;
+ line.split(fields, ' ');
+ uint64_t count;
- if (Fields.size() != 3 || !to_integer(Fields[2], Count)) {
- error(MB.getBufferIdentifier() + ": parse error");
+ if (fields.size() != 3 || !to_integer(fields[2], count)) {
+ error(mb.getBufferIdentifier() + ": parse error");
return;
}
- if (InputSectionBase *From = FindSection(Fields[0]))
- if (InputSectionBase *To = FindSection(Fields[1]))
- Config->CallGraphProfile[std::make_pair(From, To)] += Count;
+ if (InputSectionBase *from = findSection(fields[0]))
+ if (InputSectionBase *to = findSection(fields[1]))
+ config->callGraphProfile[std::make_pair(from, to)] += count;
}
}
template <class ELFT> static void readCallGraphsFromObjectFiles() {
- for (auto File : ObjectFiles) {
- auto *Obj = cast<ObjFile<ELFT>>(File);
+ for (auto file : objectFiles) {
+ auto *obj = cast<ObjFile<ELFT>>(file);
- for (const Elf_CGProfile_Impl<ELFT> &CGPE : Obj->CGProfile) {
- auto *FromSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_from));
- auto *ToSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_to));
- if (!FromSym || !ToSym)
+ for (const Elf_CGProfile_Impl<ELFT> &cgpe : obj->cgProfile) {
+ auto *fromSym = dyn_cast<Defined>(&obj->getSymbol(cgpe.cgp_from));
+ auto *toSym = dyn_cast<Defined>(&obj->getSymbol(cgpe.cgp_to));
+ if (!fromSym || !toSym)
continue;
- auto *From = dyn_cast_or_null<InputSectionBase>(FromSym->Section);
- auto *To = dyn_cast_or_null<InputSectionBase>(ToSym->Section);
- if (From && To)
- Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight;
+ auto *from = dyn_cast_or_null<InputSectionBase>(fromSym->section);
+ auto *to = dyn_cast_or_null<InputSectionBase>(toSym->section);
+ if (from && to)
+ config->callGraphProfile[{from, to}] += cgpe.cgp_weight;
}
}
}
-static bool getCompressDebugSections(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none");
- if (S == "none")
+static bool getCompressDebugSections(opt::InputArgList &args) {
+ StringRef s = args.getLastArgValue(OPT_compress_debug_sections, "none");
+ if (s == "none")
return false;
- if (S != "zlib")
- error("unknown --compress-debug-sections value: " + S);
+ if (s != "zlib")
+ error("unknown --compress-debug-sections value: " + s);
if (!zlib::isAvailable())
error("--compress-debug-sections: zlib is not available");
return true;
}
-static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &Args,
- unsigned Id) {
- auto *Arg = Args.getLastArg(Id);
- if (!Arg)
+static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
+ unsigned id) {
+ auto *arg = args.getLastArg(id);
+ if (!arg)
return {"", ""};
- StringRef S = Arg->getValue();
- std::pair<StringRef, StringRef> Ret = S.split(';');
- if (Ret.second.empty())
- error(Arg->getSpelling() + " expects 'old;new' format, but got " + S);
- return Ret;
+ StringRef s = arg->getValue();
+ std::pair<StringRef, StringRef> ret = s.split(';');
+ if (ret.second.empty())
+ error(arg->getSpelling() + " expects 'old;new' format, but got " + s);
+ return ret;
}
// Parse the symbol ordering file and warn for any duplicate entries.
-static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef MB) {
- SetVector<StringRef> Names;
- for (StringRef S : args::getLines(MB))
- if (!Names.insert(S) && Config->WarnSymbolOrdering)
- warn(MB.getBufferIdentifier() + ": duplicate ordered symbol: " + S);
+static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef mb) {
+ SetVector<StringRef> names;
+ for (StringRef s : args::getLines(mb))
+ if (!names.insert(s) && config->warnSymbolOrdering)
+ warn(mb.getBufferIdentifier() + ": duplicate ordered symbol: " + s);
- return Names.takeVector();
+ return names.takeVector();
}
-static void parseClangOption(StringRef Opt, const Twine &Msg) {
- std::string Err;
- raw_string_ostream OS(Err);
+static void parseClangOption(StringRef opt, const Twine &msg) {
+ std::string err;
+ raw_string_ostream os(err);
- const char *Argv[] = {Config->ProgName.data(), Opt.data()};
- if (cl::ParseCommandLineOptions(2, Argv, "", &OS))
+ const char *argv[] = {config->progName.data(), opt.data()};
+ if (cl::ParseCommandLineOptions(2, argv, "", &os))
return;
- OS.flush();
- error(Msg + ": " + StringRef(Err).trim());
+ os.flush();
+ error(msg + ": " + StringRef(err).trim());
}
// Initializes Config members by the command line options.
-void LinkerDriver::readConfigs(opt::InputArgList &Args) {
- errorHandler().Verbose = Args.hasArg(OPT_verbose);
- errorHandler().FatalWarnings =
- Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
- ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
-
- Config->AllowMultipleDefinition =
- Args.hasFlag(OPT_allow_multiple_definition,
+static void readConfigs(opt::InputArgList &args) {
+ errorHandler().verbose = args.hasArg(OPT_verbose);
+ errorHandler().fatalWarnings =
+ 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,
OPT_no_allow_multiple_definition, false) ||
- hasZOption(Args, "muldefs");
- Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary);
- Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
- Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
- Config->CheckSections =
- Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
- Config->Chroot = Args.getLastArgValue(OPT_chroot);
- Config->CompressDebugSections = getCompressDebugSections(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->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
- Config->DisableVerify = Args.hasArg(OPT_disable_verify);
- Config->Discard = getDiscard(Args);
- Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq);
- Config->DynamicLinker = getDynamicLinker(Args);
- Config->EhFrameHdr =
- Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
- Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false);
- Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
- Config->CallGraphProfileSort = Args.hasFlag(
+ hasZOption(args, "muldefs");
+ config->allowShlibUndefined =
+ args.hasFlag(OPT_allow_shlib_undefined, OPT_no_allow_shlib_undefined,
+ args.hasArg(OPT_shared));
+ config->auxiliaryList = args::getStrings(args, OPT_auxiliary);
+ config->bsymbolic = args.hasArg(OPT_Bsymbolic);
+ config->bsymbolicFunctions = args.hasArg(OPT_Bsymbolic_functions);
+ config->checkSections =
+ args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
+ config->chroot = args.getLastArgValue(OPT_chroot);
+ config->compressDebugSections = getCompressDebugSections(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->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);
+ config->discard = getDiscard(args);
+ config->dwoDir = args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq);
+ config->dynamicLinker = getDynamicLinker(args);
+ config->ehFrameHdr =
+ args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
+ config->emitLLVM = args.hasArg(OPT_plugin_opt_emit_llvm, false);
+ config->emitRelocs = args.hasArg(OPT_emit_relocs);
+ config->callGraphProfileSort = args.hasFlag(
OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true);
- Config->EnableNewDtags =
- Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
- Config->Entry = Args.getLastArgValue(OPT_entry);
- Config->ExecuteOnly =
- Args.hasFlag(OPT_execute_only, OPT_no_execute_only, false);
- Config->ExportDynamic =
- 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->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);
- Config->ICF = getICF(Args);
- Config->IgnoreDataAddressEquality =
- Args.hasArg(OPT_ignore_data_address_equality);
- Config->IgnoreFunctionAddressEquality =
- Args.hasArg(OPT_ignore_function_address_equality);
- Config->Init = Args.getLastArgValue(OPT_init, "_init");
- Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline);
- Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager);
- Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager);
- Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes);
- Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
- Config->LTOObjPath = Args.getLastArgValue(OPT_plugin_opt_obj_path_eq);
- Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
- Config->LTOSampleProfile = Args.getLastArgValue(OPT_lto_sample_profile);
- Config->MapFile = Args.getLastArgValue(OPT_Map);
- Config->MipsGotSize = args::getInteger(Args, OPT_mips_got_size, 0xfff0);
- Config->MergeArmExidx =
- Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
- Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
- Config->Nostdlib = Args.hasArg(OPT_nostdlib);
- Config->OFormatBinary = isOutputFormatBinary(Args);
- Config->Omagic = Args.hasFlag(OPT_omagic, OPT_no_omagic, false);
- Config->OptRemarksFilename = Args.getLastArgValue(OPT_opt_remarks_filename);
- Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness);
- Config->Optimize = args::getInteger(Args, OPT_O, 1);
- Config->OrphanHandling = getOrphanHandling(Args);
- Config->OutputFile = Args.getLastArgValue(OPT_o);
- 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->Rpath = getRpath(Args);
- Config->Relocatable = Args.hasArg(OPT_relocatable);
- Config->SaveTemps = Args.hasArg(OPT_save_temps);
- 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->SoName = Args.getLastArgValue(OPT_soname);
- Config->SortSection = getSortSection(Args);
- Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384);
- Config->Strip = getStrip(Args);
- Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
- Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
- Config->Target2 = getTarget2(Args);
- Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
- Config->ThinLTOCachePolicy = CHECK(
- parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
+ config->enableNewDtags =
+ args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
+ config->entry = args.getLastArgValue(OPT_entry);
+ config->executeOnly =
+ args.hasFlag(OPT_execute_only, OPT_no_execute_only, false);
+ config->exportDynamic =
+ 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->forceBTI = args.hasArg(OPT_force_bti);
+ config->requireCET = args.hasArg(OPT_require_cet);
+ 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);
+ config->icf = getICF(args);
+ config->ignoreDataAddressEquality =
+ args.hasArg(OPT_ignore_data_address_equality);
+ config->ignoreFunctionAddressEquality =
+ args.hasArg(OPT_ignore_function_address_equality);
+ config->init = args.getLastArgValue(OPT_init, "_init");
+ config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline);
+ 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->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
+ config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
+ config->ltoo = args::getInteger(args, OPT_lto_O, 2);
+ config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq);
+ config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
+ config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
+ config->mapFile = args.getLastArgValue(OPT_Map);
+ config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0);
+ config->mergeArmExidx =
+ args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
+ config->nmagic = args.hasFlag(OPT_nmagic, OPT_no_nmagic, false);
+ config->noinhibitExec = args.hasArg(OPT_noinhibit_exec);
+ config->nostdlib = args.hasArg(OPT_nostdlib);
+ config->oFormatBinary = isOutputFormatBinary(args);
+ config->omagic = args.hasFlag(OPT_omagic, OPT_no_omagic, false);
+ config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename);
+ config->optRemarksPasses = args.getLastArgValue(OPT_opt_remarks_passes);
+ config->optRemarksWithHotness = args.hasArg(OPT_opt_remarks_with_hotness);
+ config->optRemarksFormat = args.getLastArgValue(OPT_opt_remarks_format);
+ config->optimize = args::getInteger(args, OPT_O, 1);
+ config->orphanHandling = getOrphanHandling(args);
+ config->outputFile = args.getLastArgValue(OPT_o);
+ config->pacPlt = args.hasArg(OPT_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->printSymbolOrder =
+ args.getLastArgValue(OPT_print_symbol_order);
+ config->rpath = getRpath(args);
+ config->relocatable = args.hasArg(OPT_relocatable);
+ config->saveTemps = args.hasArg(OPT_save_temps);
+ 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->soName = args.getLastArgValue(OPT_soname);
+ config->sortSection = getSortSection(args);
+ config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384);
+ config->strip = getStrip(args);
+ config->sysroot = args.getLastArgValue(OPT_sysroot);
+ config->target1Rel = args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
+ config->target2 = getTarget2(args);
+ config->thinLTOCacheDir = args.getLastArgValue(OPT_thinlto_cache_dir);
+ config->thinLTOCachePolicy = CHECK(
+ parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
- Config->ThinLTOEmitImportsFiles =
- Args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
- Config->ThinLTOIndexOnly = Args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
- Args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
- Config->ThinLTOIndexOnlyArg =
- Args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
- Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
- Config->ThinLTOObjectSuffixReplace =
- getOldNewOptions(Args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
- Config->ThinLTOPrefixReplace =
- getOldNewOptions(Args, OPT_plugin_opt_thinlto_prefix_replace_eq);
- 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->UseAndroidRelrTags = Args.hasFlag(
+ config->thinLTOEmitImportsFiles =
+ args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
+ config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
+ args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
+ config->thinLTOIndexOnlyArg =
+ args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
+ config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
+ config->thinLTOObjectSuffixReplace =
+ getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
+ config->thinLTOPrefixReplace =
+ getOldNewOptions(args, OPT_plugin_opt_thinlto_prefix_replace_eq);
+ 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->useAndroidRelrTags = args.hasFlag(
OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false);
- Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
- Config->WarnBackrefs =
- Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false);
- Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
- Config->WarnIfuncTextrel =
- Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false);
- Config->WarnSymbolOrdering =
- 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->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false);
- Config->ZGlobal = hasZOption(Args, "global");
- Config->ZHazardplt = hasZOption(Args, "hazardplt");
- Config->ZInitfirst = hasZOption(Args, "initfirst");
- Config->ZInterpose = hasZOption(Args, "interpose");
- Config->ZKeepTextSectionPrefix = getZFlag(
- Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
- Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib");
- Config->ZNodelete = hasZOption(Args, "nodelete");
- Config->ZNodlopen = hasZOption(Args, "nodlopen");
- Config->ZNow = getZFlag(Args, "now", "lazy", false);
- Config->ZOrigin = hasZOption(Args, "origin");
- Config->ZRelro = getZFlag(Args, "relro", "norelro", true);
- Config->ZRetpolineplt = hasZOption(Args, "retpolineplt");
- Config->ZRodynamic = hasZOption(Args, "rodynamic");
- Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0);
- Config->ZText = getZFlag(Args, "text", "notext", true);
- Config->ZWxneeded = hasZOption(Args, "wxneeded");
+ config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
+ config->warnBackrefs =
+ args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false);
+ config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
+ config->warnIfuncTextrel =
+ args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false);
+ config->warnSymbolOrdering =
+ 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->zExecstack = getZFlag(args, "execstack", "noexecstack", false);
+ config->zGlobal = hasZOption(args, "global");
+ config->zHazardplt = hasZOption(args, "hazardplt");
+ config->zIfuncNoplt = hasZOption(args, "ifunc-noplt");
+ config->zInitfirst = hasZOption(args, "initfirst");
+ config->zInterpose = hasZOption(args, "interpose");
+ config->zKeepTextSectionPrefix = getZFlag(
+ args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
+ config->zNodefaultlib = hasZOption(args, "nodefaultlib");
+ config->zNodelete = hasZOption(args, "nodelete");
+ config->zNodlopen = hasZOption(args, "nodlopen");
+ config->zNow = getZFlag(args, "now", "lazy", false);
+ config->zOrigin = hasZOption(args, "origin");
+ config->zRelro = getZFlag(args, "relro", "norelro", true);
+ config->zRetpolineplt = hasZOption(args, "retpolineplt");
+ config->zRodynamic = hasZOption(args, "rodynamic");
+ config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
+ config->zText = getZFlag(args, "text", "notext", true);
+ config->zWxneeded = hasZOption(args, "wxneeded");
// Parse LTO options.
- if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq))
- parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())),
- Arg->getSpelling());
+ 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 (auto *arg : args.filtered(OPT_plugin_opt))
+ parseClangOption(arg->getValue(), arg->getSpelling());
// Parse -mllvm options.
- for (auto *Arg : Args.filtered(OPT_mllvm))
- parseClangOption(Arg->getValue(), Arg->getSpelling());
+ for (auto *arg : args.filtered(OPT_mllvm))
+ parseClangOption(arg->getValue(), arg->getSpelling());
- if (Config->LTOO > 3)
- error("invalid optimization level for LTO: " + Twine(Config->LTOO));
- if (Config->LTOPartitions == 0)
+ 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)
+ if (config->thinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
- if (Config->SplitStackAdjustSize < 0)
+ if (config->splitStackAdjustSize < 0)
error("--split-stack-adjust-size: size must be >= 0");
// Parse ELF{32,64}{LE,BE} and CPU type.
- if (auto *Arg = Args.getLastArg(OPT_m)) {
- StringRef S = Arg->getValue();
- std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
- parseEmulation(S);
- Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32");
- Config->Emulation = S;
+ if (auto *arg = args.getLastArg(OPT_m)) {
+ StringRef s = arg->getValue();
+ std::tie(config->ekind, config->emachine, config->osabi) =
+ parseEmulation(s);
+ config->mipsN32Abi = (s == "elf32btsmipn32" || s == "elf32ltsmipn32");
+ config->emulation = s;
}
// Parse -hash-style={sysv,gnu,both}.
- if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
- StringRef S = Arg->getValue();
- if (S == "sysv")
- Config->SysvHash = true;
- else if (S == "gnu")
- Config->GnuHash = true;
- else if (S == "both")
- Config->SysvHash = Config->GnuHash = true;
+ if (auto *arg = args.getLastArg(OPT_hash_style)) {
+ StringRef s = arg->getValue();
+ if (s == "sysv")
+ config->sysvHash = true;
+ else if (s == "gnu")
+ config->gnuHash = true;
+ else if (s == "both")
+ config->sysvHash = config->gnuHash = true;
else
- error("unknown -hash-style: " + S);
+ error("unknown -hash-style: " + s);
}
- if (Args.hasArg(OPT_print_map))
- Config->MapFile = "-";
-
- // --omagic is an option to create old-fashioned executables in which
- // .text segments are writable. Today, the option is still in use to
- // create special-purpose programs such as boot loaders. It doesn't
- // make sense to create PT_GNU_RELRO for such executables.
- if (Config->Omagic)
- Config->ZRelro = false;
-
- std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
-
- std::tie(Config->AndroidPackDynRelocs, Config->RelrPackDynRelocs) =
- getPackDynRelocs(Args);
-
- if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer);
+ if (args.hasArg(OPT_print_map))
+ config->mapFile = "-";
+
+ // Page alignment can be disabled by the -n (--nmagic) and -N (--omagic).
+ // As PT_GNU_RELRO relies on Paging, do not create it when we have disabled
+ // it.
+ if (config->nmagic || config->omagic)
+ config->zRelro = false;
+
+ std::tie(config->buildId, config->buildIdVector) = getBuildId(args);
+
+ std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) =
+ getPackDynRelocs(args);
+
+ if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){
+ if (args.hasArg(OPT_call_graph_ordering_file))
+ error("--symbol-ordering-file and --call-graph-order-file "
+ "may not be used together");
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())){
+ config->symbolOrderingFile = getSymbolOrderingFile(*buffer);
+ // Also need to disable CallGraphProfileSort to prevent
+ // LLD order symbols with CGProfile
+ config->callGraphProfileSort = false;
+ }
+ }
// If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others.
- if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- for (StringRef S : args::getLines(*Buffer))
- Config->VersionScriptGlobals.push_back(
- {S, /*IsExternCpp*/ false, /*HasWildcard*/ false});
+ if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
+ config->defaultSymbolVersion = VER_NDX_LOCAL;
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ for (StringRef s : args::getLines(*buffer))
+ config->versionScriptGlobals.push_back(
+ {s, /*IsExternCpp*/ false, /*HasWildcard*/ false});
}
- bool HasExportDynamic =
- Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
+ bool hasExportDynamic =
+ args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, 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 (!HasExportDynamic) {
- 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});
+ if (!hasExportDynamic) {
+ 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});
}
// 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.)
- for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
- Config->Undefined.push_back(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
+ config->undefined.push_back(arg->getValue());
- for (auto *Arg : Args.filtered(OPT_version_script))
- if (Optional<std::string> Path = searchScript(Arg->getValue())) {
- if (Optional<MemoryBufferRef> Buffer = readFile(*Path))
- readVersionScript(*Buffer);
+ for (auto *arg : args.filtered(OPT_version_script))
+ if (Optional<std::string> path = searchScript(arg->getValue())) {
+ if (Optional<MemoryBufferRef> buffer = readFile(*path))
+ readVersionScript(*buffer);
} else {
- error(Twine("cannot find version script ") + Arg->getValue());
+ error(Twine("cannot find version script ") + arg->getValue());
}
}
@@ -996,17 +1055,18 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
// command line options, but computed based on other Config values.
// This function initialize such members. See Config.h for the details
// of these values.
-static void setConfigs(opt::InputArgList &Args) {
- ELFKind K = Config->EKind;
- uint16_t M = Config->EMachine;
-
- Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
- Config->Is64 = (K == ELF64LEKind || K == ELF64BEKind);
- Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind);
- Config->Endianness = Config->IsLE ? endianness::little : endianness::big;
- Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS);
- Config->Pic = Config->Pie || Config->Shared;
- Config->Wordsize = Config->Is64 ? 8 : 4;
+static void setConfigs(opt::InputArgList &args) {
+ ELFKind k = config->ekind;
+ uint16_t m = config->emachine;
+
+ config->copyRelocs = (config->relocatable || config->emitRelocs);
+ config->is64 = (k == ELF64LEKind || k == ELF64BEKind);
+ config->isLE = (k == ELF32LEKind || k == ELF64LEKind);
+ config->endianness = config->isLE ? endianness::little : endianness::big;
+ config->isMips64EL = (k == ELF64LEKind && m == EM_MIPS);
+ config->isPic = config->pie || config->shared;
+ config->picThunk = args.hasArg(OPT_pic_veneer, config->isPic);
+ config->wordsize = config->is64 ? 8 : 4;
// ELF defines two different ways to store relocation addends as shown below:
//
@@ -1021,148 +1081,150 @@ static void setConfigs(opt::InputArgList &Args) {
// 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;
+ config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON ||
+ m == EM_PPC || m == EM_PPC64 || m == EM_RISCV ||
+ m == EM_X86_64;
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations
// if --apply-dynamic-relocs is used.
// We default to not writing the addends when using RELA relocations since
// any standard conforming tool can find it in r_addend.
- Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs,
+ config->writeAddends = args.hasFlag(OPT_apply_dynamic_relocs,
OPT_no_apply_dynamic_relocs, false) ||
- !Config->IsRela;
+ !config->isRela;
- Config->TocOptimize =
- Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64);
+ config->tocOptimize =
+ args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, m == EM_PPC64);
}
// Returns a value of "-format" option.
-static bool isFormatBinary(StringRef S) {
- if (S == "binary")
+static bool isFormatBinary(StringRef s) {
+ if (s == "binary")
return true;
- if (S == "elf" || S == "default")
+ if (s == "elf" || s == "default")
return false;
- error("unknown -format value: " + S +
+ error("unknown -format value: " + s +
" (supported formats: elf, default, binary)");
return false;
}
-void LinkerDriver::createFiles(opt::InputArgList &Args) {
+void LinkerDriver::createFiles(opt::InputArgList &args) {
// For --{push,pop}-state.
- std::vector<std::tuple<bool, bool, bool>> Stack;
+ std::vector<std::tuple<bool, bool, bool>> stack;
// Iterate over argv to process input files and positional arguments.
- for (auto *Arg : Args) {
- switch (Arg->getOption().getUnaliasedOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_library:
- addLibrary(Arg->getValue());
+ addLibrary(arg->getValue());
break;
case OPT_INPUT:
- addFile(Arg->getValue(), /*WithLOption=*/false);
+ addFile(arg->getValue(), /*withLOption=*/false);
break;
case OPT_defsym: {
- StringRef From;
- StringRef To;
- std::tie(From, To) = StringRef(Arg->getValue()).split('=');
- if (From.empty() || To.empty())
- error("-defsym: syntax error: " + StringRef(Arg->getValue()));
+ StringRef from;
+ StringRef to;
+ std::tie(from, to) = StringRef(arg->getValue()).split('=');
+ if (from.empty() || to.empty())
+ error("-defsym: syntax error: " + StringRef(arg->getValue()));
else
- readDefsym(From, MemoryBufferRef(To, "-defsym"));
+ readDefsym(from, MemoryBufferRef(to, "-defsym"));
break;
}
case OPT_script:
- if (Optional<std::string> Path = searchScript(Arg->getValue())) {
- if (Optional<MemoryBufferRef> MB = readFile(*Path))
- readLinkerScript(*MB);
+ if (Optional<std::string> path = searchScript(arg->getValue())) {
+ if (Optional<MemoryBufferRef> mb = readFile(*path))
+ readLinkerScript(*mb);
break;
}
- error(Twine("cannot find linker script ") + Arg->getValue());
+ error(Twine("cannot find linker script ") + arg->getValue());
break;
case OPT_as_needed:
- Config->AsNeeded = true;
+ config->asNeeded = true;
break;
case OPT_format:
- Config->FormatBinary = isFormatBinary(Arg->getValue());
+ config->formatBinary = isFormatBinary(arg->getValue());
break;
case OPT_no_as_needed:
- Config->AsNeeded = false;
+ config->asNeeded = false;
break;
case OPT_Bstatic:
- Config->Static = true;
+ case OPT_omagic:
+ case OPT_nmagic:
+ config->isStatic = true;
break;
case OPT_Bdynamic:
- Config->Static = false;
+ config->isStatic = false;
break;
case OPT_whole_archive:
- InWholeArchive = true;
+ inWholeArchive = true;
break;
case OPT_no_whole_archive:
- InWholeArchive = false;
+ inWholeArchive = false;
break;
case OPT_just_symbols:
- if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) {
- Files.push_back(createObjectFile(*MB));
- Files.back()->JustSymbols = true;
+ if (Optional<MemoryBufferRef> mb = readFile(arg->getValue())) {
+ files.push_back(createObjectFile(*mb));
+ files.back()->justSymbols = true;
}
break;
case OPT_start_group:
- if (InputFile::IsInGroup)
+ if (InputFile::isInGroup)
error("nested --start-group");
- InputFile::IsInGroup = true;
+ InputFile::isInGroup = true;
break;
case OPT_end_group:
- if (!InputFile::IsInGroup)
+ if (!InputFile::isInGroup)
error("stray --end-group");
- InputFile::IsInGroup = false;
- ++InputFile::NextGroupId;
+ InputFile::isInGroup = false;
+ ++InputFile::nextGroupId;
break;
case OPT_start_lib:
- if (InLib)
+ if (inLib)
error("nested --start-lib");
- if (InputFile::IsInGroup)
+ if (InputFile::isInGroup)
error("may not nest --start-lib in --start-group");
- InLib = true;
- InputFile::IsInGroup = true;
+ inLib = true;
+ InputFile::isInGroup = true;
break;
case OPT_end_lib:
- if (!InLib)
+ if (!inLib)
error("stray --end-lib");
- InLib = false;
- InputFile::IsInGroup = false;
- ++InputFile::NextGroupId;
+ inLib = false;
+ InputFile::isInGroup = false;
+ ++InputFile::nextGroupId;
break;
case OPT_push_state:
- Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive);
+ stack.emplace_back(config->asNeeded, config->isStatic, inWholeArchive);
break;
case OPT_pop_state:
- if (Stack.empty()) {
+ if (stack.empty()) {
error("unbalanced --push-state/--pop-state");
break;
}
- std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back();
- Stack.pop_back();
+ std::tie(config->asNeeded, config->isStatic, inWholeArchive) = stack.back();
+ stack.pop_back();
break;
}
}
- if (Files.empty() && errorCount() == 0)
+ if (files.empty() && errorCount() == 0)
error("no input files");
}
// If -m <machine_type> was not given, infer it from object files.
void LinkerDriver::inferMachineType() {
- if (Config->EKind != ELFNoneKind)
+ if (config->ekind != ELFNoneKind)
return;
- for (InputFile *F : Files) {
- if (F->EKind == ELFNoneKind)
+ for (InputFile *f : files) {
+ if (f->ekind == ELFNoneKind)
continue;
- Config->EKind = F->EKind;
- Config->EMachine = F->EMachine;
- Config->OSABI = F->OSABI;
- Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F);
+ config->ekind = f->ekind;
+ config->emachine = f->emachine;
+ config->osabi = f->osabi;
+ config->mipsN32Abi = config->emachine == EM_MIPS && isMipsN32Abi(f);
return;
}
error("target emulation unknown: -m or at least one .o file required");
@@ -1170,49 +1232,72 @@ void LinkerDriver::inferMachineType() {
// Parse -z max-page-size=<value>. The default value is defined by
// each target.
-static uint64_t getMaxPageSize(opt::InputArgList &Args) {
- uint64_t Val = args::getZOptionValue(Args, OPT_z, "max-page-size",
- Target->DefaultMaxPageSize);
- if (!isPowerOf2_64(Val))
+static uint64_t getMaxPageSize(opt::InputArgList &args) {
+ uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size",
+ target->defaultMaxPageSize);
+ if (!isPowerOf2_64(val))
error("max-page-size: value isn't a power of 2");
- return Val;
+ if (config->nmagic || config->omagic) {
+ if (val != target->defaultMaxPageSize)
+ warn("-z max-page-size set, but paging disabled by omagic or nmagic");
+ return 1;
+ }
+ return val;
+}
+
+// Parse -z common-page-size=<value>. The default value is defined by
+// each target.
+static uint64_t getCommonPageSize(opt::InputArgList &args) {
+ uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size",
+ target->defaultCommonPageSize);
+ if (!isPowerOf2_64(val))
+ error("common-page-size: value isn't a power of 2");
+ if (config->nmagic || config->omagic) {
+ if (val != target->defaultCommonPageSize)
+ warn("-z common-page-size set, but paging disabled by omagic or nmagic");
+ return 1;
+ }
+ // commonPageSize can't be larger than maxPageSize.
+ if (val > config->maxPageSize)
+ val = config->maxPageSize;
+ return val;
}
// Parses -image-base option.
-static Optional<uint64_t> getImageBase(opt::InputArgList &Args) {
- // Because we are using "Config->MaxPageSize" here, this function has to be
+static Optional<uint64_t> getImageBase(opt::InputArgList &args) {
+ // Because we are using "Config->maxPageSize" here, this function has to be
// called after the variable is initialized.
- auto *Arg = Args.getLastArg(OPT_image_base);
- if (!Arg)
+ auto *arg = args.getLastArg(OPT_image_base);
+ if (!arg)
return None;
- StringRef S = Arg->getValue();
- uint64_t V;
- if (!to_integer(S, V)) {
- error("-image-base: number expected, but got " + S);
+ StringRef s = arg->getValue();
+ uint64_t v;
+ if (!to_integer(s, v)) {
+ error("-image-base: number expected, but got " + s);
return 0;
}
- if ((V % Config->MaxPageSize) != 0)
- warn("-image-base: address isn't multiple of page size: " + S);
- return V;
+ if ((v % config->maxPageSize) != 0)
+ warn("-image-base: address isn't multiple of page size: " + s);
+ return v;
}
// Parses `--exclude-libs=lib,lib,...`.
// The library names may be delimited by commas or colons.
-static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
- DenseSet<StringRef> Ret;
- for (auto *Arg : Args.filtered(OPT_exclude_libs)) {
- StringRef S = Arg->getValue();
+static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &args) {
+ DenseSet<StringRef> ret;
+ for (auto *arg : args.filtered(OPT_exclude_libs)) {
+ StringRef s = arg->getValue();
for (;;) {
- size_t Pos = S.find_first_of(",:");
- if (Pos == StringRef::npos)
+ size_t pos = s.find_first_of(",:");
+ if (pos == StringRef::npos)
break;
- Ret.insert(S.substr(0, Pos));
- S = S.substr(Pos + 1);
+ ret.insert(s.substr(0, pos));
+ s = s.substr(pos + 1);
}
- Ret.insert(S);
+ ret.insert(s);
}
- return Ret;
+ return ret;
}
// Handles the -exclude-libs option. If a static library file is specified
@@ -1221,139 +1306,247 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
// A special library name "ALL" means all archive files.
//
// This is not a popular option, but some programs such as bionic libc use it.
-template <class ELFT>
-static void excludeLibs(opt::InputArgList &Args) {
- DenseSet<StringRef> Libs = getExcludeLibs(Args);
- bool All = Libs.count("ALL");
-
- auto Visit = [&](InputFile *File) {
- if (!File->ArchiveName.empty())
- if (All || Libs.count(path::filename(File->ArchiveName)))
- for (Symbol *Sym : File->getSymbols())
- if (!Sym->isLocal() && Sym->File == File)
- Sym->VersionId = VER_NDX_LOCAL;
+static void excludeLibs(opt::InputArgList &args) {
+ DenseSet<StringRef> libs = getExcludeLibs(args);
+ bool all = libs.count("ALL");
+
+ auto visit = [&](InputFile *file) {
+ if (!file->archiveName.empty())
+ if (all || libs.count(path::filename(file->archiveName)))
+ for (Symbol *sym : file->getSymbols())
+ if (!sym->isLocal() && sym->file == file)
+ sym->versionId = VER_NDX_LOCAL;
};
- for (InputFile *File : ObjectFiles)
- Visit(File);
+ for (InputFile *file : objectFiles)
+ visit(file);
- for (BitcodeFile *File : BitcodeFiles)
- Visit(File);
+ for (BitcodeFile *file : bitcodeFiles)
+ visit(file);
}
// Force Sym to be entered in the output. Used for -u or equivalent.
-template <class ELFT> static void handleUndefined(StringRef Name) {
- Symbol *Sym = Symtab->find(Name);
- if (!Sym)
+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.
+ sym->isUsedInRegularObj = true;
+
+ if (sym->isLazy())
+ sym->fetch();
+}
+
+// As an extention to GNU linkers, lld supports a variant of `-u`
+// which accepts wildcard patterns. All symbols that match a given
+// pattern are handled as if they were given by `-u`.
+static void handleUndefinedGlob(StringRef arg) {
+ Expected<GlobPattern> pat = GlobPattern::create(arg);
+ if (!pat) {
+ error("--undefined-glob: " + toString(pat.takeError()));
return;
+ }
- // Since symbol S may not be used inside the program, LTO may
- // eliminate it. Mark the symbol as "used" to prevent it.
- Sym->IsUsedInRegularObj = true;
+ std::vector<Symbol *> syms;
+ symtab->forEachSymbol([&](Symbol *sym) {
+ // Calling Sym->fetch() from here is not safe because it may
+ // add new symbols to the symbol table, invalidating the
+ // current iterator. So we just keep a note.
+ if (pat->match(sym->getName()))
+ syms.push_back(sym);
+ });
- if (Sym->isLazy())
- Symtab->fetchLazy<ELFT>(Sym);
+ for (Symbol *sym : syms)
+ handleUndefined(sym);
}
-template <class ELFT> static void handleLibcall(StringRef Name) {
- Symbol *Sym = Symtab->find(Name);
- if (!Sym || !Sym->isLazy())
+static void handleLibcall(StringRef name) {
+ Symbol *sym = symtab->find(name);
+ if (!sym || !sym->isLazy())
return;
- MemoryBufferRef MB;
- if (auto *LO = dyn_cast<LazyObject>(Sym))
- MB = LO->File->MB;
+ MemoryBufferRef mb;
+ if (auto *lo = dyn_cast<LazyObject>(sym))
+ mb = lo->file->mb;
else
- MB = cast<LazyArchive>(Sym)->getMemberBuffer();
+ mb = cast<LazyArchive>(sym)->getMemberBuffer();
- if (isBitcode(MB))
- Symtab->fetchLazy<ELFT>(Sym);
+ if (isBitcode(mb))
+ sym->fetch();
+}
+
+// Replaces common symbols with defined symbols reside in .bss sections.
+// This function is called after all symbol names are resolved. As a
+// result, the passes after the symbol resolution won't see any
+// symbols of type CommonSymbol.
+static void replaceCommonSymbols() {
+ symtab->forEachSymbol([](Symbol *sym) {
+ auto *s = dyn_cast<CommonSymbol>(sym);
+ if (!s)
+ return;
+
+ auto *bss = make<BssSection>("COMMON", s->size, s->alignment);
+ bss->file = s->file;
+ bss->markDead();
+ inputSections.push_back(bss);
+ s->replace(Defined{s->file, s->getName(), s->binding, s->stOther, s->type,
+ /*value=*/0, s->size, bss});
+ });
}
// If all references to a DSO happen to be weak, the DSO is not added
// to DT_NEEDED. If that happens, we need to eliminate shared symbols
// created from the DSO. Otherwise, they become dangling references
// that point to a non-existent DSO.
-template <class ELFT> static void demoteSharedSymbols() {
- for (Symbol *Sym : Symtab->getSymbols()) {
- if (auto *S = dyn_cast<SharedSymbol>(Sym)) {
- if (!S->getFile<ELFT>().IsNeeded) {
- bool Used = S->Used;
- replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
- S->Type);
- S->Used = Used;
- }
- }
- }
+static void demoteSharedSymbols() {
+ symtab->forEachSymbol([](Symbol *sym) {
+ auto *s = dyn_cast<SharedSymbol>(sym);
+ if (!s || s->getFile().isNeeded)
+ return;
+
+ bool used = s->used;
+ s->replace(Undefined{nullptr, s->getName(), STB_WEAK, s->stOther, s->type});
+ s->used = used;
+ });
}
-// The section referred to by S is considered address-significant. Set the
-// KeepUnique flag on the section if appropriate.
-static void markAddrsig(Symbol *S) {
- if (auto *D = dyn_cast_or_null<Defined>(S))
- if (D->Section)
+// The section referred to by `s` is considered address-significant. Set the
+// keepUnique flag on the section if appropriate.
+static void markAddrsig(Symbol *s) {
+ if (auto *d = dyn_cast_or_null<Defined>(s))
+ if (d->section)
// We don't need to keep text sections unique under --icf=all even if they
// are address-significant.
- if (Config->ICF == ICFLevel::Safe || !(D->Section->Flags & SHF_EXECINSTR))
- D->Section->KeepUnique = true;
+ if (config->icf == ICFLevel::Safe || !(d->section->flags & SHF_EXECINSTR))
+ d->section->keepUnique = true;
}
// Record sections that define symbols mentioned in --keep-unique <symbol>
// and symbols referred to by address-significance tables. These sections are
// ineligible for ICF.
template <class ELFT>
-static void findKeepUniqueSections(opt::InputArgList &Args) {
- for (auto *Arg : Args.filtered(OPT_keep_unique)) {
- StringRef Name = Arg->getValue();
- auto *D = dyn_cast_or_null<Defined>(Symtab->find(Name));
- if (!D || !D->Section) {
- warn("could not find symbol " + Name + " to keep unique");
+static void findKeepUniqueSections(opt::InputArgList &args) {
+ for (auto *arg : args.filtered(OPT_keep_unique)) {
+ StringRef name = arg->getValue();
+ auto *d = dyn_cast_or_null<Defined>(symtab->find(name));
+ if (!d || !d->section) {
+ warn("could not find symbol " + name + " to keep unique");
continue;
}
- D->Section->KeepUnique = true;
+ d->section->keepUnique = true;
}
// --icf=all --ignore-data-address-equality means that we can ignore
// the dynsym and address-significance tables entirely.
- if (Config->ICF == ICFLevel::All && Config->IgnoreDataAddressEquality)
+ if (config->icf == ICFLevel::All && config->ignoreDataAddressEquality)
return;
// Symbols in the dynsym could be address-significant in other executables
// or DSOs, so we conservatively mark them as address-significant.
- for (Symbol *S : Symtab->getSymbols())
- if (S->includeInDynsym())
- markAddrsig(S);
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (sym->includeInDynsym())
+ markAddrsig(sym);
+ });
// Visit the address-significance table in each object file and mark each
// referenced symbol as address-significant.
- for (InputFile *F : ObjectFiles) {
- auto *Obj = cast<ObjFile<ELFT>>(F);
- ArrayRef<Symbol *> Syms = Obj->getSymbols();
- if (Obj->AddrsigSec) {
- ArrayRef<uint8_t> Contents =
- check(Obj->getObj().getSectionContents(Obj->AddrsigSec));
- const uint8_t *Cur = Contents.begin();
- while (Cur != Contents.end()) {
- unsigned Size;
- const char *Err;
- uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
- if (Err)
- fatal(toString(F) + ": could not decode addrsig section: " + Err);
- markAddrsig(Syms[SymIndex]);
- Cur += Size;
+ for (InputFile *f : objectFiles) {
+ auto *obj = cast<ObjFile<ELFT>>(f);
+ ArrayRef<Symbol *> syms = obj->getSymbols();
+ if (obj->addrsigSec) {
+ ArrayRef<uint8_t> contents =
+ check(obj->getObj().getSectionContents(obj->addrsigSec));
+ const uint8_t *cur = contents.begin();
+ while (cur != contents.end()) {
+ unsigned size;
+ const char *err;
+ uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err);
+ if (err)
+ fatal(toString(f) + ": could not decode addrsig section: " + err);
+ markAddrsig(syms[symIndex]);
+ cur += size;
}
} else {
// If an object file does not have an address-significance table,
// conservatively mark all of its symbols as address-significant.
- for (Symbol *S : Syms)
- markAddrsig(S);
+ for (Symbol *s : syms)
+ markAddrsig(s);
+ }
+ }
+}
+
+// This function reads a symbol partition specification section. These sections
+// are used to control which partition a symbol is allocated to. See
+// https://lld.llvm.org/Partitions.html for more details on partitions.
+template <typename ELFT>
+static void readSymbolPartitionSection(InputSectionBase *s) {
+ // Read the relocation that refers to the partition's entry point symbol.
+ Symbol *sym;
+ if (s->areRelocsRela)
+ sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template relas<ELFT>()[0]);
+ else
+ sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template rels<ELFT>()[0]);
+ if (!isa<Defined>(sym) || !sym->includeInDynsym())
+ return;
+
+ StringRef partName = reinterpret_cast<const char *>(s->data().data());
+ for (Partition &part : partitions) {
+ if (part.name == partName) {
+ sym->partition = part.getNumber();
+ return;
}
}
+
+ // Forbid partitions from being used on incompatible targets, and forbid them
+ // from being used together with various linker features that assume a single
+ // set of output sections.
+ if (script->hasSectionsCommand)
+ error(toString(s->file) +
+ ": partitions cannot be used with the SECTIONS command");
+ if (script->hasPhdrsCommands())
+ error(toString(s->file) +
+ ": partitions cannot be used with the PHDRS command");
+ if (!config->sectionStartMap.empty())
+ error(toString(s->file) + ": partitions cannot be used with "
+ "--section-start, -Ttext, -Tdata or -Tbss");
+ if (config->emachine == EM_MIPS)
+ error(toString(s->file) + ": partitions cannot be used on this target");
+
+ // Impose a limit of no more than 254 partitions. This limit comes from the
+ // sizes of the Partition fields in InputSectionBase and Symbol, as well as
+ // the amount of space devoted to the partition number in RankFlags.
+ if (partitions.size() == 254)
+ fatal("may not have more than 254 partitions");
+
+ partitions.emplace_back();
+ Partition &newPart = partitions.back();
+ newPart.name = partName;
+ sym->partition = newPart.getNumber();
}
-template <class ELFT> static Symbol *addUndefined(StringRef Name) {
- return Symtab->addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT, 0, false,
- nullptr);
+static Symbol *addUndefined(StringRef name) {
+ return symtab->addSymbol(
+ Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0});
+}
+
+// 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.
+// This function compiles bitcode files into a few big native files
+// using LLVM functions and replaces bitcode symbols with the results.
+// 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() {
+ // Compile bitcode files and replace bitcode symbols.
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *file : bitcodeFiles)
+ lto->add(*file);
+
+ for (InputFile *file : lto->compile()) {
+ auto *obj = cast<ObjFile<ELFT>>(file);
+ obj->parse(/*ignoreComdats=*/true);
+ for (Symbol *sym : obj->getGlobalSymbols())
+ sym->parseSymbolVersion();
+ objectFiles.push_back(file);
+ }
}
// The --wrap option is a feature to rename symbols so that you can write
@@ -1365,9 +1558,9 @@ template <class ELFT> static Symbol *addUndefined(StringRef Name) {
//
// This data structure is instantiated for each -wrap option.
struct WrappedSymbol {
- Symbol *Sym;
- Symbol *Real;
- Symbol *Wrap;
+ Symbol *sym;
+ Symbol *real;
+ Symbol *wrap;
};
// Handles -wrap option.
@@ -1375,34 +1568,33 @@ struct WrappedSymbol {
// This function instantiates wrapper symbols. At this point, they seem
// like they are not being used at all, so we explicitly set some flags so
// that LTO won't eliminate them.
-template <class ELFT>
-static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
- std::vector<WrappedSymbol> V;
- DenseSet<StringRef> Seen;
+static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) {
+ std::vector<WrappedSymbol> v;
+ DenseSet<StringRef> seen;
- for (auto *Arg : Args.filtered(OPT_wrap)) {
- StringRef Name = Arg->getValue();
- if (!Seen.insert(Name).second)
+ for (auto *arg : args.filtered(OPT_wrap)) {
+ StringRef name = arg->getValue();
+ if (!seen.insert(name).second)
continue;
- Symbol *Sym = Symtab->find(Name);
- if (!Sym)
+ Symbol *sym = symtab->find(name);
+ if (!sym)
continue;
- Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
- Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
- V.push_back({Sym, Real, Wrap});
+ Symbol *real = addUndefined(saver.save("__real_" + name));
+ Symbol *wrap = addUndefined(saver.save("__wrap_" + name));
+ v.push_back({sym, real, wrap});
// We want to tell LTO not to inline symbols to be overwritten
// because LTO doesn't know the final symbol contents after renaming.
- Real->CanInline = false;
- Sym->CanInline = false;
+ real->canInline = false;
+ sym->canInline = false;
// Tell LTO not to eliminate these symbols.
- Sym->IsUsedInRegularObj = true;
- Wrap->IsUsedInRegularObj = true;
+ sym->isUsedInRegularObj = true;
+ wrap->isUsedInRegularObj = true;
}
- return V;
+ return v;
}
// Do renaming for -wrap by updating pointers to symbols.
@@ -1410,27 +1602,63 @@ static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
// When this function is executed, only InputFiles and symbol table
// contain pointers to symbol objects. We visit them to replace pointers,
// so that wrapped symbols are swapped as instructed by the command line.
-template <class ELFT> static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
- DenseMap<Symbol *, Symbol *> Map;
- for (const WrappedSymbol &W : Wrapped) {
- Map[W.Sym] = W.Wrap;
- Map[W.Real] = W.Sym;
+static void wrapSymbols(ArrayRef<WrappedSymbol> wrapped) {
+ DenseMap<Symbol *, Symbol *> map;
+ for (const WrappedSymbol &w : wrapped) {
+ map[w.sym] = w.wrap;
+ map[w.real] = w.sym;
}
// Update pointers in input files.
- parallelForEach(ObjectFiles, [&](InputFile *File) {
- std::vector<Symbol *> &Syms = File->getMutableSymbols();
- for (size_t I = 0, E = Syms.size(); I != E; ++I)
- if (Symbol *S = Map.lookup(Syms[I]))
- Syms[I] = S;
+ parallelForEach(objectFiles, [&](InputFile *file) {
+ MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
+ for (size_t i = 0, e = syms.size(); i != e; ++i)
+ if (Symbol *s = map.lookup(syms[i]))
+ syms[i] = s;
});
// Update pointers in the symbol table.
- for (const WrappedSymbol &W : Wrapped)
- Symtab->wrap(W.Sym, W.Real, W.Wrap);
+ for (const WrappedSymbol &w : wrapped)
+ symtab->wrap(w.sym, w.real, w.wrap);
}
-static const char *LibcallRoutineNames[] = {
+// To enable CET (x86's hardware-assited control flow enforcement), each
+// source file must be compiled with -fcf-protection. Object files compiled
+// with the flag contain feature flags indicating that they are compatible
+// with CET. We enable the feature only when all object files are compatible
+// with CET.
+//
+// This function returns the merged feature flags. If 0, we cannot enable CET.
+// This is also the case with AARCH64's BTI and PAC which use the similar
+// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism.
+//
+// Note that the CET-aware PLT is not implemented yet. We do error
+// check only.
+template <class ELFT> static uint32_t getAndFeatures() {
+ if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
+ config->emachine != EM_AARCH64)
+ return 0;
+
+ 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) + ": --force-bti: file does not have BTI property");
+ features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ } else if (!features && config->requireCET)
+ error(toString(f) + ": --require-cet: file is not compatible with CET");
+ 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;
+
+ return ret;
+}
+
+static const char *libcallRoutineNames[] = {
#define HANDLE_LIBCALL(code, name) name,
#include "llvm/IR/RuntimeLibcalls.def"
#undef HANDLE_LIBCALL
@@ -1438,53 +1666,48 @@ static const char *LibcallRoutineNames[] = {
// 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) {
- Target = getTarget();
- InX<ELFT>::VerSym = nullptr;
- InX<ELFT>::VerNeed = nullptr;
-
- Config->MaxPageSize = getMaxPageSize(Args);
- Config->ImageBase = getImageBase(Args);
-
+template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// 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)) {
- if (Config->EMachine == EM_MIPS)
- Config->SysvHash = true;
+ if (!args.hasArg(OPT_hash_style)) {
+ if (config->emachine == EM_MIPS)
+ config->sysvHash = true;
else
- Config->SysvHash = Config->GnuHash = true;
+ config->sysvHash = config->gnuHash = true;
}
// Default output filename is "a.out" by the Unix tradition.
- if (Config->OutputFile.empty())
- Config->OutputFile = "a.out";
+ if (config->outputFile.empty())
+ config->outputFile = "a.out";
// Fail early if the output file or map file is not writable. If a user has a
// long link, e.g. due to a large LTO link, they do not wish to run it and
// find that it failed because there was a mistake in their command-line.
- if (auto E = tryCreateFile(Config->OutputFile))
- error("cannot open output file " + Config->OutputFile + ": " + E.message());
- if (auto E = tryCreateFile(Config->MapFile))
- error("cannot open map file " + Config->MapFile + ": " + E.message());
+ if (auto e = tryCreateFile(config->outputFile))
+ error("cannot open output file " + config->outputFile + ": " + e.message());
+ if (auto e = tryCreateFile(config->mapFile))
+ error("cannot open map file " + config->mapFile + ": " + e.message());
if (errorCount())
return;
// Use default entry point name if no name was given via the command
// line nor linker scripts. For some reason, MIPS entry point name is
// different from others.
- Config->WarnMissingEntry =
- (!Config->Entry.empty() || (!Config->Shared && !Config->Relocatable));
- if (Config->Entry.empty() && !Config->Relocatable)
- Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start";
+ config->warnMissingEntry =
+ (!config->entry.empty() || (!config->shared && !config->relocatable));
+ if (config->entry.empty() && !config->relocatable)
+ config->entry = (config->emachine == EM_MIPS) ? "__start" : "_start";
// Handle --trace-symbol.
- for (auto *Arg : Args.filtered(OPT_trace_symbol))
- Symtab->trace(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_trace_symbol))
+ symtab->insert(arg->getValue())->traced = true;
// Add all files to the symbol table. This will add almost all
- // symbols that we need to the symbol table.
- for (InputFile *F : Files)
- Symtab->addFile<ELFT>(F);
+ // 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]);
// Now that we have every file, we can decide if we will need a
// dynamic symbol table.
@@ -1492,20 +1715,26 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// producing a shared library.
// We also need one if any shared libraries are used and for pie executables
// (probably because the dynamic linker needs it).
- Config->HasDynSymTab =
- !SharedFiles.empty() || Config->Pic || Config->ExportDynamic;
+ config->hasDynSymTab =
+ !sharedFiles.empty() || config->isPic || config->exportDynamic;
// Some symbols (such as __ehdr_start) are defined lazily only when there
// are undefined symbols for them, so we add these to trigger that logic.
- for (StringRef Name : Script->ReferencedSymbols)
- addUndefined<ELFT>(Name);
+ for (StringRef name : script->referencedSymbols)
+ addUndefined(name);
// Handle the `--undefined <sym>` options.
- for (StringRef S : Config->Undefined)
- handleUndefined<ELFT>(S);
+ for (StringRef arg : config->undefined)
+ if (Symbol *sym = symtab->find(arg))
+ handleUndefined(sym);
// If an entry symbol is in a static archive, pull out that file now.
- handleUndefined<ELFT>(Config->Entry);
+ if (Symbol *sym = symtab->find(config->entry))
+ handleUndefined(sym);
+
+ // Handle the `--undefined-glob <pattern>` options.
+ for (StringRef pat : args::getStrings(args, OPT_undefined_glob))
+ handleUndefinedGlob(pat);
// If any of our inputs are bitcode files, the LTO code generator may create
// references to certain library functions that might not be explicit in the
@@ -1524,9 +1753,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// to, i.e. if the symbol's definition is in bitcode. Any other required
// libcall symbols will be added to the link after LTO when we add the LTO
// object file to the link.
- if (!BitcodeFiles.empty())
- for (const char *S : LibcallRoutineNames)
- handleLibcall<ELFT>(S);
+ if (!bitcodeFiles.empty())
+ for (const char *s : libcallRoutineNames)
+ handleLibcall(s);
// Return if there were name resolution errors.
if (errorCount())
@@ -1534,27 +1763,27 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// 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();
+ 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.
- Script->declareSymbols();
+ script->declareSymbols();
// Handle the -exclude-libs option.
- if (Args.hasArg(OPT_exclude_libs))
- excludeLibs<ELFT>(Args);
+ if (args.hasArg(OPT_exclude_libs))
+ excludeLibs(args);
- // Create ElfHeader early. We need a dummy section in
+ // Create elfHeader early. We need a dummy section in
// addReservedSymbols to mark the created symbols as not absolute.
- Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
- Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr);
+ Out::elfHeader = make<OutputSection>("", 0, SHF_ALLOC);
+ Out::elfHeader->size = sizeof(typename ELFT::Ehdr);
// Create wrapped symbols for -wrap option.
- std::vector<WrappedSymbol> Wrapped = addWrappedSymbols<ELFT>(Args);
+ std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
// We need to create some reserved symbols such as _end. Create them.
- if (!Config->Relocatable)
+ if (!config->relocatable)
addReservedSymbols();
// Apply version scripts.
@@ -1562,84 +1791,118 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// For a relocatable output, version scripts don't make sense, and
// parsing a symbol version string (e.g. dropping "@ver1" from a symbol
// name "foo@ver1") rather do harm, so we don't call this if -r is given.
- if (!Config->Relocatable)
- Symtab->scanVersionScript();
+ if (!config->relocatable)
+ symtab->scanVersionScript();
// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
//
// 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.
- Symtab->addCombinedLTOObject<ELFT>();
+ compileBitcodeFiles<ELFT>();
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)
+ 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)
+ if (config->emitLLVM)
return;
// Apply symbol renames for -wrap.
- if (!Wrapped.empty())
- wrapSymbols<ELFT>(Wrapped);
+ if (!wrapped.empty())
+ wrapSymbols(wrapped);
// Now that we have a complete list of input files.
// Beyond this point, no new files are added.
// Aggregate all input sections into one place.
- for (InputFile *F : ObjectFiles)
- for (InputSectionBase *S : F->getSections())
- if (S && S != &InputSection::Discarded)
- InputSections.push_back(S);
- for (BinaryFile *F : BinaryFiles)
- for (InputSectionBase *S : F->getSections())
- InputSections.push_back(cast<InputSection>(S));
-
- // We do not want to emit debug sections if --strip-all
- // or -strip-debug are given.
- if (Config->Strip != StripPolicy::None)
- llvm::erase_if(InputSections, [](InputSectionBase *S) {
- return S->Name.startswith(".debug") || S->Name.startswith(".zdebug");
- });
-
- Config->EFlags = Target->calcEFlags();
-
- if (Config->EMachine == EM_ARM) {
+ for (InputFile *f : objectFiles)
+ for (InputSectionBase *s : f->getSections())
+ if (s && s != &InputSection::discarded)
+ inputSections.push_back(s);
+ for (BinaryFile *f : binaryFiles)
+ for (InputSectionBase *s : f->getSections())
+ inputSections.push_back(cast<InputSection>(s));
+
+ llvm::erase_if(inputSections, [](InputSectionBase *s) {
+ if (s->type == SHT_LLVM_SYMPART) {
+ readSymbolPartitionSection<ELFT>(s);
+ return true;
+ }
+
+ // We do not want to emit debug sections if --strip-all
+ // or -strip-debug are given.
+ return config->strip != StripPolicy::None &&
+ (s->name.startswith(".debug") || s->name.startswith(".zdebug"));
+ });
+
+ // Now that the number of partitions is fixed, save a pointer to the main
+ // partition.
+ mainPart = &partitions[0];
+
+ // Read .note.gnu.property sections from input object files which
+ // contain a hint to tweak linker's and loader's behaviors.
+ config->andFeatures = getAndFeatures<ELFT>();
+
+ // 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();
+
+ config->eflags = target->calcEFlags();
+ // maxPageSize (sometimes called abi page size) is the maximum page size that
+ // the output can be run on. For example if the OS can use 4k or 64k page
+ // sizes then maxPageSize must be 64k for the output to be useable on both.
+ // All important alignment decisions must use this value.
+ config->maxPageSize = getMaxPageSize(args);
+ // commonPageSize is the most common page size that the output will be run on.
+ // For example if an OS can use 4k or 64k page sizes and 4k is more common
+ // than 64k then commonPageSize is set to 4k. commonPageSize can be used for
+ // optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it
+ // is limited to writing trap instructions on the last executable segment.
+ config->commonPageSize = getCommonPageSize(args);
+
+ config->imageBase = getImageBase(args);
+
+ if (config->emachine == EM_ARM) {
// FIXME: These warnings can be removed when lld only uses these features
// when the input objects have been compiled with an architecture that
// supports them.
- if (Config->ARMHasBlx == false)
+ if (config->armHasBlx == false)
warn("lld uses blx instruction, no object with architecture supporting "
"feature detected");
}
// This adds a .comment section containing a version string. We have to add it
// before mergeSections because the .comment section is a mergeable section.
- if (!Config->Relocatable)
- InputSections.push_back(createCommentSection());
+ if (!config->relocatable)
+ inputSections.push_back(createCommentSection());
+
+ // Replace common symbols with regular symbols.
+ replaceCommonSymbols();
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
// and identical code folding.
splitSections<ELFT>();
markLive<ELFT>();
- demoteSharedSymbols<ELFT>();
+ demoteSharedSymbols();
mergeSections();
- if (Config->ICF != ICFLevel::None) {
- findKeepUniqueSections<ELFT>(Args);
+ if (config->icf != ICFLevel::None) {
+ findKeepUniqueSections<ELFT>(args);
doIcf<ELFT>();
}
// Read the callgraph now that we know what was gced or icfed
- if (Config->CallGraphProfileSort) {
- if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- readCallGraph(*Buffer);
+ if (config->callGraphProfileSort) {
+ if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file))
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ readCallGraph(*buffer);
readCallGraphsFromObjectFiles<ELFT>();
}
diff --git a/ELF/Driver.h b/ELF/Driver.h
index 81d7f608e588..3115e28d1669 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -1,15 +1,15 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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_ELF_DRIVER_H
#define LLD_ELF_DRIVER_H
+#include "LTO.h"
#include "SymbolTable.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Reproduce.h"
@@ -22,34 +22,37 @@
namespace lld {
namespace elf {
-extern class LinkerDriver *Driver;
+extern class LinkerDriver *driver;
class LinkerDriver {
public:
- void main(ArrayRef<const char *> Args);
- void addFile(StringRef Path, bool WithLOption);
- void addLibrary(StringRef Name);
+ void main(ArrayRef<const char *> args);
+ void addFile(StringRef path, bool withLOption);
+ void addLibrary(StringRef name);
private:
- void readConfigs(llvm::opt::InputArgList &Args);
- void createFiles(llvm::opt::InputArgList &Args);
+ void createFiles(llvm::opt::InputArgList &args);
void inferMachineType();
- template <class ELFT> void link(llvm::opt::InputArgList &Args);
+ template <class ELFT> void link(llvm::opt::InputArgList &args);
+ template <class ELFT> void compileBitcodeFiles();
// True if we are in --whole-archive and --no-whole-archive.
- bool InWholeArchive = false;
+ bool inWholeArchive = false;
// True if we are in --start-lib and --end-lib.
- bool InLib = false;
+ bool inLib = false;
+
+ // For LTO.
+ std::unique_ptr<BitcodeCompiler> lto;
- std::vector<InputFile *> Files;
+ std::vector<InputFile *> files;
};
// Parses command line options.
class ELFOptTable : public llvm::opt::OptTable {
public:
ELFOptTable();
- llvm::opt::InputArgList parse(ArrayRef<const char *> Argv);
+ llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
};
// Create enum with OPT_xxx values for each option in Options.td
@@ -61,11 +64,12 @@ enum {
};
void printHelp();
-std::string createResponseFile(const llvm::opt::InputArgList &Args);
+std::string createResponseFile(const llvm::opt::InputArgList &args);
-llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
-llvm::Optional<std::string> searchScript(StringRef Path);
-llvm::Optional<std::string> searchLibrary(StringRef Path);
+llvm::Optional<std::string> findFromSearchPaths(StringRef path);
+llvm::Optional<std::string> searchScript(StringRef path);
+llvm::Optional<std::string> searchLibraryBaseName(StringRef path);
+llvm::Optional<std::string> searchLibrary(StringRef path);
} // namespace elf
} // namespace lld
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index e51d02e38da1..87f0aa2a9827 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -1,9 +1,8 @@
//===- DriverUtils.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -42,7 +41,7 @@ using namespace lld::elf;
#undef PREFIX
// Create table mapping all options defined in Options.td
-static const opt::OptTable::Info OptInfo[] = {
+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},
@@ -50,36 +49,36 @@ static const opt::OptTable::Info OptInfo[] = {
#undef OPTION
};
-ELFOptTable::ELFOptTable() : OptTable(OptInfo) {}
+ELFOptTable::ELFOptTable() : OptTable(optInfo) {}
// Set color diagnostics according to -color-diagnostics={auto,always,never}
// or -no-color-diagnostics flags.
-static void handleColorDiagnostics(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+static void handleColorDiagnostics(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
- if (!Arg)
+ if (!arg)
return;
- if (Arg->getOption().getID() == OPT_color_diagnostics) {
- errorHandler().ColorDiagnostics = true;
- } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
- errorHandler().ColorDiagnostics = false;
+ if (arg->getOption().getID() == OPT_color_diagnostics) {
+ errorHandler().colorDiagnostics = true;
+ } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
+ errorHandler().colorDiagnostics = false;
} else {
- StringRef S = Arg->getValue();
- if (S == "always")
- errorHandler().ColorDiagnostics = true;
- else if (S == "never")
- errorHandler().ColorDiagnostics = false;
- else if (S != "auto")
- error("unknown option: --color-diagnostics=" + S);
+ StringRef s = arg->getValue();
+ if (s == "always")
+ errorHandler().colorDiagnostics = true;
+ else if (s == "never")
+ errorHandler().colorDiagnostics = false;
+ else if (s != "auto")
+ error("unknown option: --color-diagnostics=" + s);
}
}
-static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
- StringRef S = Arg->getValue();
- if (S != "windows" && S != "posix")
- error("invalid response file quoting: " + S);
- if (S == "windows")
+static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
+ StringRef s = arg->getValue();
+ if (s != "windows" && s != "posix")
+ error("invalid response file quoting: " + s);
+ if (s == "windows")
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
@@ -97,50 +96,56 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
// `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a
// bit hacky, but looks like it is still better than handling --plugin-opt
// options by hand.
-static void concatLTOPluginOptions(SmallVectorImpl<const char *> &Args) {
- SmallVector<const char *, 256> V;
- for (size_t I = 0, E = Args.size(); I != E; ++I) {
- StringRef S = Args[I];
- if ((S == "-plugin-opt" || S == "--plugin-opt") && I + 1 != E) {
- V.push_back(Saver.save(S + "=" + Args[I + 1]).data());
- ++I;
+static void concatLTOPluginOptions(SmallVectorImpl<const char *> &args) {
+ SmallVector<const char *, 256> v;
+ for (size_t i = 0, e = args.size(); i != e; ++i) {
+ StringRef s = args[i];
+ if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) {
+ v.push_back(saver.save(s + "=" + args[i + 1]).data());
+ ++i;
} else {
- V.push_back(Args[I]);
+ v.push_back(args[i]);
}
}
- Args = std::move(V);
+ args = std::move(v);
}
// Parses a given list of options.
-opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
+opt::InputArgList ELFOptTable::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());
+ unsigned missingIndex;
+ unsigned missingCount;
+ SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
// We need to get the quoting style for response files before parsing all
// options so we parse here before and ignore all the options but
// --rsp-quoting.
- opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
+ opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
- cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
- concatLTOPluginOptions(Vec);
- Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
-
- handleColorDiagnostics(Args);
- if (MissingCount)
- error(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
-
- for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- error("unknown argument: " + Arg->getSpelling());
- return Args;
+ cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec);
+ concatLTOPluginOptions(vec);
+ args = this->ParseArgs(vec, missingIndex, missingCount);
+
+ handleColorDiagnostics(args);
+ if (missingCount)
+ error(Twine(args.getArgString(missingIndex)) + ": missing argument");
+
+ for (auto *arg : args.filtered(OPT_UNKNOWN)) {
+ std::string nearest;
+ if (findNearest(arg->getAsString(args), nearest) > 1)
+ error("unknown argument '" + arg->getAsString(args) + "'");
+ else
+ error("unknown argument '" + arg->getAsString(args) +
+ "', did you mean '" + nearest + "'");
+ }
+ return args;
}
void elf::printHelp() {
ELFOptTable().PrintHelp(
- outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld",
+ outs(), (config->progName + " [options] file...").str().c_str(), "lld",
false /*ShowHidden*/, true /*ShowAllAliases*/);
outs() << "\n";
@@ -149,30 +154,36 @@ void elf::printHelp() {
// in a message for the -help option. If it doesn't match, the scripts
// assume that the linker doesn't support very basic features such as
// shared libraries. Therefore, we need to print out at least "elf".
- outs() << Config->ProgName << ": supported targets: elf\n";
+ outs() << config->progName << ": supported targets: elf\n";
+}
+
+static std::string rewritePath(StringRef s) {
+ if (fs::exists(s))
+ return relativeToRoot(s);
+ return 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 elf::createResponseFile(const opt::InputArgList &Args) {
- SmallString<0> Data;
- raw_svector_ostream OS(Data);
- OS << "--chroot .\n";
+std::string elf::createResponseFile(const opt::InputArgList &args) {
+ SmallString<0> data;
+ raw_svector_ostream os(data);
+ os << "--chroot .\n";
// Copy the command line to the output while rewriting paths.
- for (auto *Arg : Args) {
- switch (Arg->getOption().getUnaliasedOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_reproduce:
break;
case OPT_INPUT:
- OS << quote(rewritePath(Arg->getValue())) << "\n";
+ os << quote(rewritePath(arg->getValue())) << "\n";
break;
case OPT_o:
// If -o path contains directories, "lld @response.txt" will likely
// fail because the archive we are creating doesn't contain empty
// directories for the output path (-o doesn't create directories).
// Strip directories to prevent the issue.
- OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n";
+ os << "-o " << quote(sys::path::filename(arg->getValue())) << "\n";
break;
case OPT_dynamic_list:
case OPT_library_path:
@@ -181,58 +192,62 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
case OPT_symbol_ordering_file:
case OPT_sysroot:
case OPT_version_script:
- OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
+ os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue()))
<< "\n";
break;
default:
- OS << toString(*Arg) << "\n";
+ os << toString(*arg) << "\n";
}
}
- return Data.str();
+ return data.str();
}
// Find a file by concatenating given paths. If a resulting path
// starts with "=", the character is replaced with a --sysroot value.
-static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
- SmallString<128> S;
- if (Path1.startswith("="))
- path::append(S, Config->Sysroot, Path1.substr(1), Path2);
+static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
+ SmallString<128> s;
+ if (path1.startswith("="))
+ path::append(s, config->sysroot, path1.substr(1), path2);
else
- path::append(S, Path1, Path2);
+ path::append(s, path1, path2);
- if (fs::exists(S))
- return S.str().str();
+ if (fs::exists(s))
+ return s.str().str();
return None;
}
-Optional<std::string> elf::findFromSearchPaths(StringRef Path) {
- for (StringRef Dir : Config->SearchPaths)
- if (Optional<std::string> S = findFile(Dir, Path))
- return S;
+Optional<std::string> elf::findFromSearchPaths(StringRef path) {
+ for (StringRef dir : config->searchPaths)
+ if (Optional<std::string> s = findFile(dir, path))
+ return s;
return None;
}
-// This is for -lfoo. We'll look for libfoo.so or libfoo.a from
+// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
// search paths.
-Optional<std::string> elf::searchLibrary(StringRef Name) {
- if (Name.startswith(":"))
- return findFromSearchPaths(Name.substr(1));
-
- for (StringRef Dir : Config->SearchPaths) {
- if (!Config->Static)
- if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".so"))
- return S;
- if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a"))
- return S;
+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"))
+ return s;
+ if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
+ return s;
}
return None;
}
+// This is for -l<namespec>.
+Optional<std::string> elf::searchLibrary(StringRef name) {
+ if (name.startswith(":"))
+ return findFromSearchPaths(name.substr(1));
+ return searchLibraryBaseName (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> elf::searchScript(StringRef Name) {
- if (fs::exists(Name))
- return Name.str();
- return findFromSearchPaths(Name);
+Optional<std::string> elf::searchScript(StringRef name) {
+ if (fs::exists(name))
+ return name.str();
+ return findFromSearchPaths(name);
}
diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp
index 95d444bdc2a1..b3245dd01669 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -1,9 +1,8 @@
//===- EhFrame.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -37,72 +36,72 @@ using namespace lld::elf;
namespace {
class EhReader {
public:
- EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
+ EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
size_t readEhRecordSize();
uint8_t getFdeEncoding();
private:
- template <class P> void failOn(const P *Loc, const Twine &Msg) {
- fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
- IS->getObjMsg((const uint8_t *)Loc - IS->data().data()));
+ template <class P> void failOn(const P *loc, const Twine &msg) {
+ fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " +
+ isec->getObjMsg((const uint8_t *)loc - isec->data().data()));
}
uint8_t readByte();
- void skipBytes(size_t Count);
+ void skipBytes(size_t count);
StringRef readString();
void skipLeb128();
void skipAugP();
- InputSectionBase *IS;
- ArrayRef<uint8_t> D;
+ InputSectionBase *isec;
+ ArrayRef<uint8_t> d;
};
}
-size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
- return EhReader(S, S->data().slice(Off)).readEhRecordSize();
+size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) {
+ return EhReader(s, s->data().slice(off)).readEhRecordSize();
}
// .eh_frame section is a sequence of records. Each record starts with
// a 4 byte length field. This function reads the length.
size_t EhReader::readEhRecordSize() {
- if (D.size() < 4)
- failOn(D.data(), "CIE/FDE too small");
+ if (d.size() < 4)
+ failOn(d.data(), "CIE/FDE too small");
// First 4 bytes of CIE/FDE is the size of the record.
// If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
// but we do not support that format yet.
- uint64_t V = read32(D.data());
- if (V == UINT32_MAX)
- failOn(D.data(), "CIE/FDE too large");
- uint64_t Size = V + 4;
- if (Size > D.size())
- failOn(D.data(), "CIE/FDE ends past the end of the section");
- return Size;
+ uint64_t v = read32(d.data());
+ if (v == UINT32_MAX)
+ failOn(d.data(), "CIE/FDE too large");
+ uint64_t size = v + 4;
+ if (size > d.size())
+ failOn(d.data(), "CIE/FDE ends past the end of the section");
+ return size;
}
// Read a byte and advance D by one byte.
uint8_t EhReader::readByte() {
- if (D.empty())
- failOn(D.data(), "unexpected end of CIE");
- uint8_t B = D.front();
- D = D.slice(1);
- return B;
+ if (d.empty())
+ failOn(d.data(), "unexpected end of CIE");
+ uint8_t b = d.front();
+ d = d.slice(1);
+ return b;
}
-void EhReader::skipBytes(size_t Count) {
- if (D.size() < Count)
- failOn(D.data(), "CIE is too small");
- D = D.slice(Count);
+void EhReader::skipBytes(size_t count) {
+ if (d.size() < count)
+ failOn(d.data(), "CIE is too small");
+ d = d.slice(count);
}
// Read a null-terminated string.
StringRef EhReader::readString() {
- const uint8_t *End = std::find(D.begin(), D.end(), '\0');
- if (End == D.end())
- failOn(D.data(), "corrupted CIE (failed to read string)");
- StringRef S = toStringRef(D.slice(0, End - D.begin()));
- D = D.slice(S.size() + 1);
- return S;
+ const uint8_t *end = llvm::find(d, '\0');
+ if (end == d.end())
+ failOn(d.data(), "corrupted CIE (failed to read string)");
+ StringRef s = toStringRef(d.slice(0, end - d.begin()));
+ d = d.slice(s.size() + 1);
+ return s;
}
// Skip an integer encoded in the LEB128 format.
@@ -110,21 +109,21 @@ StringRef EhReader::readString() {
// But we need to be at least able to skip it so that we can read
// the field that follows a LEB128 number.
void EhReader::skipLeb128() {
- const uint8_t *ErrPos = D.data();
- while (!D.empty()) {
- uint8_t Val = D.front();
- D = D.slice(1);
- if ((Val & 0x80) == 0)
+ const uint8_t *errPos = d.data();
+ while (!d.empty()) {
+ uint8_t val = d.front();
+ d = d.slice(1);
+ if ((val & 0x80) == 0)
return;
}
- failOn(ErrPos, "corrupted CIE (failed to read LEB128)");
+ failOn(errPos, "corrupted CIE (failed to read LEB128)");
}
-static size_t getAugPSize(unsigned Enc) {
- switch (Enc & 0x0f) {
+static size_t getAugPSize(unsigned enc) {
+ switch (enc & 0x0f) {
case DW_EH_PE_absptr:
case DW_EH_PE_signed:
- return Config->Wordsize;
+ return config->wordsize;
case DW_EH_PE_udata2:
case DW_EH_PE_sdata2:
return 2;
@@ -139,29 +138,29 @@ static size_t getAugPSize(unsigned Enc) {
}
void EhReader::skipAugP() {
- uint8_t Enc = readByte();
- if ((Enc & 0xf0) == DW_EH_PE_aligned)
- failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported");
- size_t Size = getAugPSize(Enc);
- if (Size == 0)
- failOn(D.data() - 1, "unknown FDE encoding");
- if (Size >= D.size())
- failOn(D.data() - 1, "corrupted CIE");
- D = D.slice(Size);
+ uint8_t enc = readByte();
+ if ((enc & 0xf0) == DW_EH_PE_aligned)
+ failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
+ size_t size = getAugPSize(enc);
+ if (size == 0)
+ failOn(d.data() - 1, "unknown FDE encoding");
+ if (size >= d.size())
+ failOn(d.data() - 1, "corrupted CIE");
+ d = d.slice(size);
}
-uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
- return EhReader(P->Sec, P->data()).getFdeEncoding();
+uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
+ return EhReader(p->sec, p->data()).getFdeEncoding();
}
uint8_t EhReader::getFdeEncoding() {
skipBytes(8);
- int Version = readByte();
- if (Version != 1 && Version != 3)
- failOn(D.data() - 1,
- "FDE version 1 or 3 expected, but got " + Twine(Version));
+ int version = readByte();
+ if (version != 1 && version != 3)
+ failOn(d.data() - 1,
+ "FDE version 1 or 3 expected, but got " + Twine(version));
- StringRef Aug = readString();
+ StringRef aug = readString();
// Skip code and data alignment factors.
skipLeb128();
@@ -169,7 +168,7 @@ uint8_t EhReader::getFdeEncoding() {
// Skip the return address register. In CIE version 1 this is a single
// byte. In CIE version 3 this is an unsigned LEB128.
- if (Version == 1)
+ if (version == 1)
readByte();
else
skipLeb128();
@@ -177,22 +176,22 @@ uint8_t EhReader::getFdeEncoding() {
// We only care about an 'R' value, but other records may precede an 'R'
// record. Unfortunately records are not in TLV (type-length-value) format,
// so we need to teach the linker how to skip records for each type.
- for (char C : Aug) {
- if (C == 'R')
+ for (char c : aug) {
+ if (c == 'R')
return readByte();
- if (C == 'z') {
+ if (c == 'z') {
skipLeb128();
continue;
}
- if (C == 'P') {
+ if (c == 'P') {
skipAugP();
continue;
}
- if (C == 'L') {
+ if (c == 'L') {
readByte();
continue;
}
- failOn(Aug.data(), "unknown .eh_frame augmentation string: " + Aug);
+ failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
}
return DW_EH_PE_absptr;
}
diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h
index 5112891a911e..20dd6126ec8e 100644
--- a/ELF/EhFrame.h
+++ b/ELF/EhFrame.h
@@ -1,9 +1,8 @@
//===- EhFrame.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -17,8 +16,8 @@ namespace elf {
class InputSectionBase;
struct EhSectionPiece;
-size_t readEhRecordSize(InputSectionBase *S, size_t Off);
-uint8_t getFdeEncoding(EhSectionPiece *P);
+size_t readEhRecordSize(InputSectionBase *s, size_t off);
+uint8_t getFdeEncoding(EhSectionPiece *p);
} // namespace elf
} // namespace lld
diff --git a/ELF/Filesystem.h b/ELF/Filesystem.h
deleted file mode 100644
index 987a74a6bcb6..000000000000
--- a/ELF/Filesystem.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//===- Filesystem.h ---------------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_ELF_FILESYSTEM_H
-#define LLD_ELF_FILESYSTEM_H
-
-#include "lld/Common/LLVM.h"
-#include <system_error>
-
-namespace lld {
-namespace elf {
-void unlinkAsync(StringRef Path);
-std::error_code tryCreateFile(StringRef Path);
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index e917ae76a689..8b01d06b0248 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -1,9 +1,8 @@
//===- ICF.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -99,33 +98,33 @@ public:
void run();
private:
- void segregate(size_t Begin, size_t End, bool Constant);
+ void segregate(size_t begin, size_t end, bool constant);
template <class RelTy>
- bool constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
- const InputSection *B, ArrayRef<RelTy> RelsB);
+ bool constantEq(const InputSection *a, ArrayRef<RelTy> relsA,
+ const InputSection *b, ArrayRef<RelTy> relsB);
template <class RelTy>
- bool variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
- const InputSection *B, ArrayRef<RelTy> RelsB);
+ bool variableEq(const InputSection *a, ArrayRef<RelTy> relsA,
+ const InputSection *b, ArrayRef<RelTy> relsB);
- bool equalsConstant(const InputSection *A, const InputSection *B);
- bool equalsVariable(const InputSection *A, const InputSection *B);
+ bool equalsConstant(const InputSection *a, const InputSection *b);
+ bool equalsVariable(const InputSection *a, const InputSection *b);
- size_t findBoundary(size_t Begin, size_t End);
+ size_t findBoundary(size_t begin, size_t end);
- void forEachClassRange(size_t Begin, size_t End,
- llvm::function_ref<void(size_t, size_t)> Fn);
+ void forEachClassRange(size_t begin, size_t end,
+ llvm::function_ref<void(size_t, size_t)> fn);
- void forEachClass(llvm::function_ref<void(size_t, size_t)> Fn);
+ void forEachClass(llvm::function_ref<void(size_t, size_t)> fn);
- std::vector<InputSection *> Sections;
+ std::vector<InputSection *> sections;
// We repeat the main loop while `Repeat` is true.
- std::atomic<bool> Repeat;
+ std::atomic<bool> repeat;
// The main loop counter.
- int Cnt = 0;
+ int cnt = 0;
// We have two locations for equivalence classes. On the first iteration
// of the main loop, Class[0] has a valid value, and Class[1] contains
@@ -151,42 +150,42 @@ private:
// because we can safely read the next class without worrying about race
// conditions. Using the same location makes this algorithm converge
// faster because it uses results of the same iteration earlier.
- int Current = 0;
- int Next = 0;
+ int current = 0;
+ int next = 0;
};
}
// Returns true if section S is subject of ICF.
-static bool isEligible(InputSection *S) {
- if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC))
+static bool isEligible(InputSection *s) {
+ if (!s->isLive() || s->keepUnique || !(s->flags & SHF_ALLOC))
return false;
// Don't merge writable sections. .data.rel.ro sections are marked as writable
// but are semantically read-only.
- if ((S->Flags & SHF_WRITE) && S->Name != ".data.rel.ro" &&
- !S->Name.startswith(".data.rel.ro."))
+ if ((s->flags & SHF_WRITE) && s->name != ".data.rel.ro" &&
+ !s->name.startswith(".data.rel.ro."))
return false;
// SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections,
// so we don't consider them for ICF individually.
- if (S->Flags & SHF_LINK_ORDER)
+ if (s->flags & SHF_LINK_ORDER)
return false;
// Don't merge synthetic sections as their Data member is not valid and empty.
// The Data member needs to be valid for ICF as it is used by ICF to determine
// the equality of section contents.
- if (isa<SyntheticSection>(S))
+ if (isa<SyntheticSection>(s))
return false;
// .init and .fini contains instructions that must be executed to initialize
// and finalize the process. They cannot and should not be merged.
- if (S->Name == ".init" || S->Name == ".fini")
+ if (s->name == ".init" || s->name == ".fini")
return false;
// A user program may enumerate sections named with a C identifier using
// __start_* and __stop_* symbols. We cannot ICF any such sections because
// that could change program semantics.
- if (isValidCIdentifier(S->Name))
+ if (isValidCIdentifier(s->name))
return false;
return true;
@@ -194,7 +193,7 @@ static bool isEligible(InputSection *S) {
// Split an equivalence class into smaller classes.
template <class ELFT>
-void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
+void ICF<ELFT>::segregate(size_t begin, size_t end, bool constant) {
// This loop rearranges sections in [Begin, End) so that all sections
// that are equal in terms of equals{Constant,Variable} are contiguous
// in [Begin, End).
@@ -203,93 +202,93 @@ void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
// issue in practice because the number of the distinct sections in
// each range is usually very small.
- while (Begin < End) {
+ while (begin < end) {
// Divide [Begin, End) into two. Let Mid be the start index of the
// second group.
- auto Bound =
- std::stable_partition(Sections.begin() + Begin + 1,
- Sections.begin() + End, [&](InputSection *S) {
- if (Constant)
- return equalsConstant(Sections[Begin], S);
- return equalsVariable(Sections[Begin], S);
+ auto bound =
+ std::stable_partition(sections.begin() + begin + 1,
+ sections.begin() + end, [&](InputSection *s) {
+ if (constant)
+ return equalsConstant(sections[begin], s);
+ return equalsVariable(sections[begin], s);
});
- size_t Mid = Bound - Sections.begin();
+ size_t mid = bound - sections.begin();
// Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by
// updating the sections in [Begin, Mid). We use Mid as an equivalence
// class ID because every group ends with a unique index.
- for (size_t I = Begin; I < Mid; ++I)
- Sections[I]->Class[Next] = Mid;
+ for (size_t i = begin; i < mid; ++i)
+ sections[i]->eqClass[next] = mid;
// If we created a group, we need to iterate the main loop again.
- if (Mid != End)
- Repeat = true;
+ if (mid != end)
+ repeat = true;
- Begin = Mid;
+ begin = mid;
}
}
// Compare two lists of relocations.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
- const InputSection *SecB, ArrayRef<RelTy> RB) {
- for (size_t I = 0; I < RA.size(); ++I) {
- if (RA[I].r_offset != RB[I].r_offset ||
- RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL))
+bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra,
+ const InputSection *secB, ArrayRef<RelTy> rb) {
+ for (size_t i = 0; i < ra.size(); ++i) {
+ if (ra[i].r_offset != rb[i].r_offset ||
+ ra[i].getType(config->isMips64EL) != rb[i].getType(config->isMips64EL))
return false;
- uint64_t AddA = getAddend<ELFT>(RA[I]);
- uint64_t AddB = getAddend<ELFT>(RB[I]);
+ uint64_t addA = getAddend<ELFT>(ra[i]);
+ uint64_t addB = getAddend<ELFT>(rb[i]);
- Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
- Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
- if (&SA == &SB) {
- if (AddA == AddB)
+ Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]);
+ Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]);
+ if (&sa == &sb) {
+ if (addA == addB)
continue;
return false;
}
- auto *DA = dyn_cast<Defined>(&SA);
- auto *DB = dyn_cast<Defined>(&SB);
+ auto *da = dyn_cast<Defined>(&sa);
+ auto *db = dyn_cast<Defined>(&sb);
// Placeholder symbols generated by linker scripts look the same now but
// may have different values later.
- if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined)
+ if (!da || !db || da->scriptDefined || db->scriptDefined)
return false;
// Relocations referring to absolute symbols are constant-equal if their
// values are equal.
- if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB)
+ if (!da->section && !db->section && da->value + addA == db->value + addB)
continue;
- if (!DA->Section || !DB->Section)
+ if (!da->section || !db->section)
return false;
- if (DA->Section->kind() != DB->Section->kind())
+ if (da->section->kind() != db->section->kind())
return false;
// Relocations referring to InputSections are constant-equal if their
// section offsets are equal.
- if (isa<InputSection>(DA->Section)) {
- if (DA->Value + AddA == DB->Value + AddB)
+ if (isa<InputSection>(da->section)) {
+ if (da->value + addA == db->value + addB)
continue;
return false;
}
// Relocations referring to MergeInputSections are constant-equal if their
// offsets in the output section are equal.
- auto *X = dyn_cast<MergeInputSection>(DA->Section);
- if (!X)
+ auto *x = dyn_cast<MergeInputSection>(da->section);
+ if (!x)
return false;
- auto *Y = cast<MergeInputSection>(DB->Section);
- if (X->getParent() != Y->getParent())
+ auto *y = cast<MergeInputSection>(db->section);
+ if (x->getParent() != y->getParent())
return false;
- uint64_t OffsetA =
- SA.isSection() ? X->getOffset(AddA) : X->getOffset(DA->Value) + AddA;
- uint64_t OffsetB =
- SB.isSection() ? Y->getOffset(AddB) : Y->getOffset(DB->Value) + AddB;
- if (OffsetA != OffsetB)
+ uint64_t offsetA =
+ sa.isSection() ? x->getOffset(addA) : x->getOffset(da->value) + addA;
+ uint64_t offsetB =
+ sb.isSection() ? y->getOffset(addB) : y->getOffset(db->value) + addB;
+ if (offsetA != offsetB)
return false;
}
@@ -299,57 +298,57 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
// Compare "non-moving" part of two InputSections, namely everything
// except relocation targets.
template <class ELFT>
-bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
- if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags ||
- A->getSize() != B->getSize() || A->data() != B->data())
+bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
+ if (a->numRelocations != b->numRelocations || a->flags != b->flags ||
+ a->getSize() != b->getSize() || a->data() != b->data())
return false;
// If two sections have different output sections, we cannot merge them.
// FIXME: This doesn't do the right thing in the case where there is a linker
// script. We probably need to move output section assignment before ICF to
// get the correct behaviour here.
- if (getOutputSectionName(A) != getOutputSectionName(B))
+ if (getOutputSectionName(a) != getOutputSectionName(b))
return false;
- if (A->AreRelocsRela)
- return constantEq(A, A->template relas<ELFT>(), B,
- B->template relas<ELFT>());
- return constantEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>());
+ if (a->areRelocsRela)
+ return constantEq(a, a->template relas<ELFT>(), b,
+ b->template relas<ELFT>());
+ return constantEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>());
}
// Compare two lists of relocations. Returns true if all pairs of
// relocations point to the same section in terms of ICF.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA,
- const InputSection *SecB, ArrayRef<RelTy> RB) {
- assert(RA.size() == RB.size());
+bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra,
+ const InputSection *secB, ArrayRef<RelTy> rb) {
+ assert(ra.size() == rb.size());
- for (size_t I = 0; I < RA.size(); ++I) {
+ for (size_t i = 0; i < ra.size(); ++i) {
// The two sections must be identical.
- Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
- Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
- if (&SA == &SB)
+ Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]);
+ Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]);
+ if (&sa == &sb)
continue;
- auto *DA = cast<Defined>(&SA);
- auto *DB = cast<Defined>(&SB);
+ auto *da = cast<Defined>(&sa);
+ auto *db = cast<Defined>(&sb);
// We already dealt with absolute and non-InputSection symbols in
// constantEq, and for InputSections we have already checked everything
// except the equivalence class.
- if (!DA->Section)
+ if (!da->section)
continue;
- auto *X = dyn_cast<InputSection>(DA->Section);
- if (!X)
+ auto *x = dyn_cast<InputSection>(da->section);
+ if (!x)
continue;
- auto *Y = cast<InputSection>(DB->Section);
+ auto *y = cast<InputSection>(db->section);
// Ineligible sections are in the special equivalence class 0.
// They can never be the same in terms of the equivalence class.
- if (X->Class[Current] == 0)
+ if (x->eqClass[current] == 0)
return false;
- if (X->Class[Current] != Y->Class[Current])
+ if (x->eqClass[current] != y->eqClass[current])
return false;
};
@@ -358,19 +357,19 @@ bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA,
// Compare "moving" part of two InputSections, namely relocation targets.
template <class ELFT>
-bool ICF<ELFT>::equalsVariable(const InputSection *A, const InputSection *B) {
- if (A->AreRelocsRela)
- return variableEq(A, A->template relas<ELFT>(), B,
- B->template relas<ELFT>());
- return variableEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>());
+bool ICF<ELFT>::equalsVariable(const InputSection *a, const InputSection *b) {
+ if (a->areRelocsRela)
+ return variableEq(a, a->template relas<ELFT>(), b,
+ b->template relas<ELFT>());
+ return variableEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>());
}
-template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
- uint32_t Class = Sections[Begin]->Class[Current];
- for (size_t I = Begin + 1; I < End; ++I)
- if (Class != Sections[I]->Class[Current])
- return I;
- return End;
+template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t begin, size_t end) {
+ uint32_t eqClass = sections[begin]->eqClass[current];
+ for (size_t i = begin + 1; i < end; ++i)
+ if (eqClass != sections[i]->eqClass[current])
+ return i;
+ return end;
}
// Sections in the same equivalence class are contiguous in Sections
@@ -379,123 +378,125 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
//
// This function calls Fn on every group within [Begin, End).
template <class ELFT>
-void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
- llvm::function_ref<void(size_t, size_t)> Fn) {
- while (Begin < End) {
- size_t Mid = findBoundary(Begin, End);
- Fn(Begin, Mid);
- Begin = Mid;
+void ICF<ELFT>::forEachClassRange(size_t begin, size_t end,
+ llvm::function_ref<void(size_t, size_t)> fn) {
+ while (begin < end) {
+ size_t mid = findBoundary(begin, end);
+ fn(begin, mid);
+ begin = mid;
}
}
// Call Fn on each equivalence class.
template <class ELFT>
-void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) {
+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) {
- forEachClassRange(0, Sections.size(), Fn);
- ++Cnt;
+ if (!threadsEnabled || sections.size() < 1024) {
+ forEachClassRange(0, sections.size(), fn);
+ ++cnt;
return;
}
- Current = Cnt % 2;
- Next = (Cnt + 1) % 2;
+ current = cnt % 2;
+ next = (cnt + 1) % 2;
// Shard into non-overlapping intervals, and call Fn in parallel.
// The sharding must be completed before any calls to Fn are made
// so that Fn can modify the Chunks in its shard without causing data
// races.
- const size_t NumShards = 256;
- size_t Step = Sections.size() / NumShards;
- size_t Boundaries[NumShards + 1];
- Boundaries[0] = 0;
- Boundaries[NumShards] = Sections.size();
-
- parallelForEachN(1, NumShards, [&](size_t I) {
- Boundaries[I] = findBoundary((I - 1) * Step, Sections.size());
+ const size_t numShards = 256;
+ size_t step = sections.size() / numShards;
+ size_t boundaries[numShards + 1];
+ boundaries[0] = 0;
+ boundaries[numShards] = sections.size();
+
+ parallelForEachN(1, numShards, [&](size_t i) {
+ boundaries[i] = findBoundary((i - 1) * step, sections.size());
});
- parallelForEachN(1, NumShards + 1, [&](size_t I) {
- if (Boundaries[I - 1] < Boundaries[I])
- forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
+ parallelForEachN(1, numShards + 1, [&](size_t i) {
+ if (boundaries[i - 1] < boundaries[i])
+ forEachClassRange(boundaries[i - 1], boundaries[i], fn);
});
- ++Cnt;
+ ++cnt;
}
// Combine the hashes of the sections referenced by the given section into its
// hash.
template <class ELFT, class RelTy>
-static void combineRelocHashes(InputSection *IS, ArrayRef<RelTy> Rels) {
- uint32_t Hash = IS->Class[1];
- for (RelTy Rel : Rels) {
- Symbol &S = IS->template getFile<ELFT>()->getRelocTargetSym(Rel);
- if (auto *D = dyn_cast<Defined>(&S))
- if (auto *RelSec = dyn_cast_or_null<InputSection>(D->Section))
- Hash ^= RelSec->Class[1];
+static void combineRelocHashes(unsigned cnt, InputSection *isec,
+ ArrayRef<RelTy> rels) {
+ uint32_t hash = isec->eqClass[cnt % 2];
+ for (RelTy rel : rels) {
+ Symbol &s = isec->template getFile<ELFT>()->getRelocTargetSym(rel);
+ if (auto *d = dyn_cast<Defined>(&s))
+ if (auto *relSec = dyn_cast_or_null<InputSection>(d->section))
+ hash += relSec->eqClass[cnt % 2];
}
// Set MSB to 1 to avoid collisions with non-hash IDs.
- IS->Class[0] = Hash | (1U << 31);
+ isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
}
-static void print(const Twine &S) {
- if (Config->PrintIcfSections)
- message(S);
+static void print(const Twine &s) {
+ if (config->printIcfSections)
+ message(s);
}
// The main function of ICF.
template <class ELFT> void ICF<ELFT>::run() {
// Collect sections to merge.
- for (InputSectionBase *Sec : InputSections)
- if (auto *S = dyn_cast<InputSection>(Sec))
- if (isEligible(S))
- Sections.push_back(S);
+ for (InputSectionBase *sec : inputSections)
+ if (auto *s = dyn_cast<InputSection>(sec))
+ if (isEligible(s))
+ sections.push_back(s);
// Initially, we use hash values to partition sections.
- parallelForEach(Sections, [&](InputSection *S) {
- S->Class[1] = xxHash64(S->data());
+ parallelForEach(sections, [&](InputSection *s) {
+ s->eqClass[0] = xxHash64(s->data());
});
- parallelForEach(Sections, [&](InputSection *S) {
- if (S->AreRelocsRela)
- combineRelocHashes<ELFT>(S, S->template relas<ELFT>());
- else
- combineRelocHashes<ELFT>(S, S->template rels<ELFT>());
- });
+ for (unsigned cnt = 0; cnt != 2; ++cnt) {
+ parallelForEach(sections, [&](InputSection *s) {
+ if (s->areRelocsRela)
+ combineRelocHashes<ELFT>(cnt, s, s->template relas<ELFT>());
+ else
+ combineRelocHashes<ELFT>(cnt, s, s->template rels<ELFT>());
+ });
+ }
// From now on, sections in Sections vector are ordered so that sections
// in the same equivalence class are consecutive in the vector.
- std::stable_sort(Sections.begin(), Sections.end(),
- [](InputSection *A, InputSection *B) {
- return A->Class[0] < B->Class[0];
- });
+ llvm::stable_sort(sections, [](const InputSection *a, const InputSection *b) {
+ return a->eqClass[0] < b->eqClass[0];
+ });
// Compare static contents and assign unique IDs for each static content.
- forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
+ forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
// Split groups by comparing relocations until convergence is obtained.
do {
- Repeat = false;
+ repeat = false;
forEachClass(
- [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
- } while (Repeat);
+ [&](size_t begin, size_t end) { segregate(begin, end, false); });
+ } while (repeat);
- log("ICF needed " + Twine(Cnt) + " iterations");
+ log("ICF needed " + Twine(cnt) + " iterations");
// Merge sections by the equivalence class.
- forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) {
- if (End - Begin == 1)
+ forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) {
+ if (end - begin == 1)
return;
- print("selected section " + toString(Sections[Begin]));
- for (size_t I = Begin + 1; I < End; ++I) {
- print(" removing identical section " + toString(Sections[I]));
- Sections[Begin]->replace(Sections[I]);
+ print("selected section " + toString(sections[begin]));
+ for (size_t i = begin + 1; i < end; ++i) {
+ print(" removing identical section " + toString(sections[i]));
+ sections[begin]->replace(sections[i]);
// At this point we know sections merged are fully identical and hence
// we want to remove duplicate implicit dependencies such as link order
// and relocation sections.
- for (InputSection *IS : Sections[I]->DependentSections)
- IS->Live = false;
+ for (InputSection *isec : sections[i]->dependentSections)
+ isec->markDead();
}
});
}
diff --git a/ELF/ICF.h b/ELF/ICF.h
index a6c8636ead6d..ed828fc4a949 100644
--- a/ELF/ICF.h
+++ b/ELF/ICF.h
@@ -1,9 +1,8 @@
//===- ICF.h --------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index e4d1dec7cbcb..98b88283cf09 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -1,13 +1,13 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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 "Driver.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "SymbolTable.h"
@@ -25,6 +25,7 @@
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/ARMAttributeParser.h"
#include "llvm/Support/ARMBuildAttributes.h"
+#include "llvm/Support/Endian.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/raw_ostream.h"
@@ -34,145 +35,272 @@ using namespace llvm::ELF;
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<BinaryFile *> elf::BinaryFiles;
-std::vector<BitcodeFile *> elf::BitcodeFiles;
-std::vector<LazyObjFile *> elf::LazyObjFiles;
-std::vector<InputFile *> elf::ObjectFiles;
-std::vector<InputFile *> elf::SharedFiles;
-
-std::unique_ptr<TarWriter> elf::Tar;
+bool InputFile::isInGroup;
+uint32_t InputFile::nextGroupId;
+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;
+
+static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
+ unsigned char size;
+ unsigned char endian;
+ std::tie(size, endian) = getElfArchType(mb.getBuffer());
+
+ auto report = [&](StringRef msg) {
+ StringRef filename = mb.getBufferIdentifier();
+ if (archiveName.empty())
+ fatal(filename + ": " + msg);
+ else
+ fatal(archiveName + "(" + filename + "): " + msg);
+ };
+
+ if (!mb.getBuffer().startswith(ElfMagic))
+ report("not an ELF file");
+ if (endian != ELFDATA2LSB && endian != ELFDATA2MSB)
+ report("corrupted ELF file: invalid data encoding");
+ if (size != ELFCLASS32 && size != ELFCLASS64)
+ report("corrupted ELF file: invalid file class");
+
+ size_t bufSize = mb.getBuffer().size();
+ if ((size == ELFCLASS32 && bufSize < sizeof(Elf32_Ehdr)) ||
+ (size == ELFCLASS64 && bufSize < sizeof(Elf64_Ehdr)))
+ report("corrupted ELF file: file is too short");
+
+ if (size == ELFCLASS32)
+ return (endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind;
+ return (endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind;
+}
-InputFile::InputFile(Kind K, MemoryBufferRef M)
- : MB(M), GroupId(NextGroupId), FileKind(K) {
+InputFile::InputFile(Kind k, MemoryBufferRef m)
+ : mb(m), groupId(nextGroupId), fileKind(k) {
// All files within the same --{start,end}-group get the same group ID.
// Otherwise, a new file will get a new group ID.
- if (!IsInGroup)
- ++NextGroupId;
+ if (!isInGroup)
+ ++nextGroupId;
}
-Optional<MemoryBufferRef> elf::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("/"))
- Path = Saver.save(Config->Chroot + Path);
+ if (!config->chroot.empty() && path.startswith("/"))
+ path = saver.save(config->chroot + path);
- log(Path);
+ log(path);
- auto MBOrErr = MemoryBuffer::getFile(Path, -1, false);
- if (auto EC = MBOrErr.getError()) {
- error("cannot open " + Path + ": " + EC.message());
+ auto mbOrErr = MemoryBuffer::getFile(path, -1, false);
+ 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
+ std::unique_ptr<MemoryBuffer> &mb = *mbOrErr;
+ MemoryBufferRef mbref = mb->getMemBufferRef();
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take MB ownership
+
+ if (tar)
+ tar->append(relativeToRoot(path), mbref.getBuffer());
+ return mbref;
+}
+
+// All input object files must be for the same architecture
+// (e.g. it does not make sense to link x86 object files with
+// MIPS object files.) This function checks for that error.
+static bool isCompatible(InputFile *file) {
+ if (!file->isElf() && !isa<BitcodeFile>(file))
+ return true;
+
+ if (file->ekind == config->ekind && file->emachine == config->emachine) {
+ if (config->emachine != EM_MIPS)
+ return true;
+ if (isMipsN32Abi(file) == config->mipsN32Abi)
+ return true;
+ }
+
+ if (!config->emulation.empty()) {
+ error(toString(file) + " is incompatible with " + config->emulation);
+ } else {
+ InputFile *existing;
+ if (!objectFiles.empty())
+ existing = objectFiles[0];
+ else if (!sharedFiles.empty())
+ existing = sharedFiles[0];
+ else
+ existing = bitcodeFiles[0];
+
+ error(toString(file) + " is incompatible with " + toString(existing));
+ }
+
+ return false;
+}
+
+template <class ELFT> static void doParseFile(InputFile *file) {
+ if (!isCompatible(file))
+ return;
+
+ // Binary file
+ if (auto *f = dyn_cast<BinaryFile>(file)) {
+ binaryFiles.push_back(f);
+ f->parse();
+ return;
+ }
+
+ // .a file
+ if (auto *f = dyn_cast<ArchiveFile>(file)) {
+ f->parse();
+ return;
+ }
+
+ // Lazy object file
+ if (auto *f = dyn_cast<LazyObjFile>(file)) {
+ lazyObjFiles.push_back(f);
+ f->parse<ELFT>();
+ return;
+ }
+
+ if (config->trace)
+ message(toString(file));
+
+ // .so file
+ if (auto *f = dyn_cast<SharedFile>(file)) {
+ f->parse<ELFT>();
+ return;
+ }
+
+ // LLVM bitcode file
+ if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ bitcodeFiles.push_back(f);
+ f->parse<ELFT>();
+ return;
+ }
- if (Tar)
- Tar->append(relativeToRoot(Path), MBRef.getBuffer());
- return MBRef;
+ // Regular object file
+ objectFiles.push_back(file);
+ cast<ObjFile<ELFT>>(file)->parse();
+}
+
+// Add symbols in File to the symbol table.
+void elf::parseFile(InputFile *file) {
+ switch (config->ekind) {
+ case ELF32LEKind:
+ doParseFile<ELF32LE>(file);
+ return;
+ case ELF32BEKind:
+ doParseFile<ELF32BE>(file);
+ return;
+ case ELF64LEKind:
+ doParseFile<ELF64LE>(file);
+ return;
+ case ELF64BEKind:
+ doParseFile<ELF64BE>(file);
+ return;
+ default:
+ llvm_unreachable("unknown ELFT");
+ }
}
// 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 Lineno = ":" + std::to_string(Line);
- if (Filename == Path)
- return Filename + Lineno;
- return Filename + Lineno + " (" + Path.str() + Lineno + ")";
+static std::string createFileLineMsg(StringRef path, unsigned line) {
+ std::string filename = path::filename(path);
+ std::string lineno = ":" + std::to_string(line);
+ if (filename == path)
+ return filename + lineno;
+ return filename + lineno + " (" + path.str() + lineno + ")";
}
template <class ELFT>
-static std::string getSrcMsgAux(ObjFile<ELFT> &File, const Symbol &Sym,
- InputSectionBase &Sec, uint64_t Offset) {
+static std::string getSrcMsgAux(ObjFile<ELFT> &file, const Symbol &sym,
+ InputSectionBase &sec, uint64_t offset) {
// In DWARF, functions and variables are stored to different places.
// First, lookup a function for a given offset.
- if (Optional<DILineInfo> Info = File.getDILineInfo(&Sec, Offset))
- return createFileLineMsg(Info->FileName, Info->Line);
+ if (Optional<DILineInfo> info = file.getDILineInfo(&sec, offset))
+ return createFileLineMsg(info->FileName, info->Line);
// If it failed, lookup again as a variable.
- if (Optional<std::pair<std::string, unsigned>> FileLine =
- File.getVariableLoc(Sym.getName()))
- return createFileLineMsg(FileLine->first, FileLine->second);
+ if (Optional<std::pair<std::string, unsigned>> fileLine =
+ file.getVariableLoc(sym.getName()))
+ return createFileLineMsg(fileLine->first, fileLine->second);
- // File.SourceFile contains STT_FILE symbol, and that is a last resort.
- return File.SourceFile;
+ // File.sourceFile contains STT_FILE symbol, and that is a last resort.
+ return file.sourceFile;
}
-std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
- uint64_t Offset) {
+std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
+ uint64_t offset) {
if (kind() != ObjKind)
return "";
- switch (Config->EKind) {
+ switch (config->ekind) {
default:
llvm_unreachable("Invalid kind");
case ELF32LEKind:
- return getSrcMsgAux(cast<ObjFile<ELF32LE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF32LE>>(*this), sym, sec, offset);
case ELF32BEKind:
- return getSrcMsgAux(cast<ObjFile<ELF32BE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF32BE>>(*this), sym, sec, offset);
case ELF64LEKind:
- return getSrcMsgAux(cast<ObjFile<ELF64LE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF64LE>>(*this), sym, sec, offset);
case ELF64BEKind:
- return getSrcMsgAux(cast<ObjFile<ELF64BE>>(*this), Sym, Sec, Offset);
+ return getSrcMsgAux(cast<ObjFile<ELF64BE>>(*this), sym, sec, offset);
}
}
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
- Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
- for (std::unique_ptr<DWARFUnit> &CU : Dwarf->compile_units()) {
- auto Report = [](Error Err) {
- handleAllErrors(std::move(Err),
- [](ErrorInfoBase &Info) { warn(Info.message()); });
+ dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) {
+ auto report = [](Error err) {
+ handleAllErrors(std::move(err),
+ [](ErrorInfoBase &info) { warn(info.message()); });
};
- Expected<const DWARFDebugLine::LineTable *> ExpectedLT =
- Dwarf->getLineTableForUnit(CU.get(), Report);
- const DWARFDebugLine::LineTable *LT = nullptr;
- if (ExpectedLT)
- LT = *ExpectedLT;
+ Expected<const DWARFDebugLine::LineTable *> expectedLT =
+ dwarf->getLineTableForUnit(cu.get(), report);
+ const DWARFDebugLine::LineTable *lt = nullptr;
+ if (expectedLT)
+ lt = *expectedLT;
else
- Report(ExpectedLT.takeError());
- if (!LT)
+ report(expectedLT.takeError());
+ if (!lt)
continue;
- LineTables.push_back(LT);
+ lineTables.push_back(lt);
- // Loop over variable records and insert them to VariableLoc.
- for (const auto &Entry : CU->dies()) {
- DWARFDie Die(CU.get(), &Entry);
+ // Loop over variable records and insert them to variableLoc.
+ for (const auto &entry : cu->dies()) {
+ DWARFDie die(cu.get(), &entry);
// Skip all tags that are not variables.
- if (Die.getTag() != dwarf::DW_TAG_variable)
+ if (die.getTag() != dwarf::DW_TAG_variable)
continue;
// Skip if a local variable because we don't need them for generating
// error messages. In general, only non-local symbols can fail to be
// linked.
- if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
+ if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
continue;
// Get the source filename index for the variable.
- unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
- if (!LT->hasFileAtIndex(File))
+ unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
+ if (!lt->hasFileAtIndex(file))
continue;
// Get the line number on which the variable is declared.
- unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
+ unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
- // Here we want to take the variable name to add it into VariableLoc.
+ // Here we want to take the variable name to add it into variableLoc.
// Variable can have regular and linkage name associated. At first, we try
// to get linkage name as it can be different, for example when we have
// two variables in different namespaces of the same object. Use common
// name otherwise, but handle the case when it also absent in case if the
// input object file lacks some debug info.
- StringRef Name =
- dwarf::toString(Die.find(dwarf::DW_AT_linkage_name),
- dwarf::toString(Die.find(dwarf::DW_AT_name), ""));
- if (!Name.empty())
- VariableLoc.insert({Name, {LT, File, Line}});
+ StringRef name =
+ dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
+ dwarf::toString(die.find(dwarf::DW_AT_name), ""));
+ if (!name.empty())
+ variableLoc.insert({name, {lt, file, line}});
}
}
}
@@ -181,112 +309,152 @@ template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
// object (variable, array, etc) definition.
template <class ELFT>
Optional<std::pair<std::string, unsigned>>
-ObjFile<ELFT>::getVariableLoc(StringRef Name) {
- llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
+ObjFile<ELFT>::getVariableLoc(StringRef name) {
+ llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
// Return if we have no debug information about data object.
- auto It = VariableLoc.find(Name);
- if (It == VariableLoc.end())
+ auto it = variableLoc.find(name);
+ if (it == variableLoc.end())
return None;
// Take file name string from line table.
- std::string FileName;
- if (!It->second.LT->getFileNameByIndex(
- It->second.File, nullptr,
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
+ std::string fileName;
+ if (!it->second.lt->getFileNameByIndex(
+ it->second.file, {},
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
return None;
- return std::make_pair(FileName, It->second.Line);
+ return std::make_pair(fileName, it->second.line);
}
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
-Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
- uint64_t Offset) {
- llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
+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();
+ for (uint64_t curIndex = 0; curIndex < sections.size(); ++curIndex) {
+ if (s == sections[curIndex]) {
+ sectionIndex = curIndex;
+ break;
+ }
+ }
// Use fake address calcuated by adding section file offset and offset in
// section. See comments for ObjectInfo class.
- DILineInfo Info;
- for (const llvm::DWARFDebugLine::LineTable *LT : LineTables)
- if (LT->getFileLineInfoForAddress(
- S->getOffsetInFile() + Offset, nullptr,
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info))
- return Info;
+ DILineInfo info;
+ for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
+ if (lt->getFileLineInfoForAddress(
+ {s->getOffsetInFile() + offset, sectionIndex}, nullptr,
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
+ return info;
+ }
return None;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
-std::string lld::toString(const InputFile *F) {
- if (!F)
+std::string lld::toString(const InputFile *f) {
+ if (!f)
return "<internal>";
- if (F->ToStringCache.empty()) {
- if (F->ArchiveName.empty())
- F->ToStringCache = F->getName();
+ if (f->toStringCache.empty()) {
+ if (f->archiveName.empty())
+ f->toStringCache = f->getName();
else
- F->ToStringCache = (F->ArchiveName + "(" + F->getName() + ")").str();
+ f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
}
- return F->ToStringCache;
+ return f->toStringCache;
}
-template <class ELFT>
-ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
- if (ELFT::TargetEndianness == support::little)
- EKind = ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
- else
- EKind = ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind;
+ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
+ ekind = getELFKind(mb, "");
- EMachine = getObj().getHeader()->e_machine;
- OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
+ switch (ekind) {
+ case ELF32LEKind:
+ init<ELF32LE>();
+ break;
+ case ELF32BEKind:
+ init<ELF32BE>();
+ break;
+ case ELF64LEKind:
+ init<ELF64LE>();
+ break;
+ case ELF64BEKind:
+ init<ELF64BE>();
+ break;
+ default:
+ llvm_unreachable("getELFKind");
+ }
}
-template <class ELFT>
-typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
- return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end());
+template <typename Elf_Shdr>
+static const Elf_Shdr *findSection(ArrayRef<Elf_Shdr> sections, uint32_t type) {
+ for (const Elf_Shdr &sec : sections)
+ if (sec.sh_type == type)
+ return &sec;
+ return nullptr;
}
-template <class ELFT>
-uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
- return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this);
-}
+template <class ELFT> void ELFFileBase::init() {
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
-template <class ELFT>
-void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr *Symtab) {
- FirstGlobal = Symtab->sh_info;
- ELFSyms = CHECK(getObj().symbols(Symtab), this);
- if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size())
+ // Initialize trivial attributes.
+ const ELFFile<ELFT> &obj = getObj<ELFT>();
+ emachine = obj.getHeader()->e_machine;
+ osabi = obj.getHeader()->e_ident[llvm::ELF::EI_OSABI];
+ abiVersion = obj.getHeader()->e_ident[llvm::ELF::EI_ABIVERSION];
+
+ ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this);
+
+ // Find a symbol table.
+ bool isDSO =
+ (identify_magic(mb.getBuffer()) == file_magic::elf_shared_object);
+ const Elf_Shdr *symtabSec =
+ findSection(sections, isDSO ? SHT_DYNSYM : SHT_SYMTAB);
+
+ if (!symtabSec)
+ return;
+
+ // Initialize members corresponding to a symbol table.
+ firstGlobal = symtabSec->sh_info;
+
+ ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(symtabSec), this);
+ if (firstGlobal == 0 || firstGlobal > eSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
- StringTable =
- CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this);
+ elfSyms = reinterpret_cast<const void *>(eSyms.data());
+ numELFSyms = eSyms.size();
+ stringTable = CHECK(obj.getStringTableForSymtab(*symtabSec, sections), this);
}
template <class ELFT>
-ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName)
- : ELFFileBase<ELFT>(Base::ObjKind, M) {
- this->ArchiveName = ArchiveName;
+uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const {
+ return CHECK(
+ this->getObj().getSectionIndex(&sym, getELFSyms<ELFT>(), shndxTable),
+ this);
}
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
- if (this->Symbols.empty())
+ if (this->symbols.empty())
return {};
- return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1);
+ return makeArrayRef(this->symbols).slice(1, this->firstGlobal - 1);
}
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() {
- return makeArrayRef(this->Symbols).slice(this->FirstGlobal);
+ return makeArrayRef(this->symbols).slice(this->firstGlobal);
}
-template <class ELFT>
-void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
- // Read a section table. JustSymbols is usually false.
- if (this->JustSymbols)
+template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
+ // Read a section table. justSymbols is usually false.
+ if (this->justSymbols)
initializeJustSymbols();
else
- initializeSections(ComdatGroups);
+ initializeSections(ignoreComdats);
// Read a symbol table.
initializeSymbols();
@@ -296,17 +464,13 @@ void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
-StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr &Sec) {
- // Group signatures are stored as symbol names in object files.
- // sh_info contains a symbol index, so we fetch a symbol and read its name.
- if (this->ELFSyms.empty())
- this->initSymtab(
- Sections, CHECK(object::getSection<ELFT>(Sections, Sec.sh_link), this));
-
- const Elf_Sym *Sym =
- CHECK(object::getSymbol<ELFT>(this->ELFSyms, Sec.sh_info), this);
- StringRef Signature = CHECK(Sym->getName(this->StringTable), this);
+StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
+ const Elf_Shdr &sec) {
+ typename ELFT::SymRange symbols = this->getELFSyms<ELFT>();
+ if (sec.sh_info >= symbols.size())
+ fatal(toString(this) + ": invalid symbol index");
+ const typename ELFT::Sym &sym = symbols[sec.sh_info];
+ StringRef signature = CHECK(sym.getName(this->stringTable), this);
// As a special case, if a symbol is a section symbol and has no name,
// we use a section name as a signature.
@@ -315,23 +479,12 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
// standard, but GNU gold 1.14 (the newest version as of July 2017) or
// older produce such sections as outputs for the -r option, so we need
// a bug-compatibility.
- if (Signature.empty() && Sym->getType() == STT_SECTION)
- return getSectionName(Sec);
- return Signature;
-}
-
-template <class ELFT>
-ArrayRef<typename ObjFile<ELFT>::Elf_Word>
-ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
- const ELFFile<ELFT> &Obj = this->getObj();
- ArrayRef<Elf_Word> Entries =
- CHECK(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), this);
- if (Entries.empty() || Entries[0] != GRP_COMDAT)
- fatal(toString(this) + ": unsupported SHT_GROUP format");
- return Entries.slice(1);
+ if (signature.empty() && sym.getType() == STT_SECTION)
+ return getSectionName(sec);
+ return signature;
}
-template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
+template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) {
// 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.
@@ -344,14 +497,14 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// SHF_MERGE sections based both on their name and sh_entsize, but that seems
// to be more trouble than it is worth. Instead, we just use the regular (-O1)
// logic for -r.
- if (Config->Optimize == 0 && !Config->Relocatable)
+ if (config->optimize == 0 && !config->relocatable)
return false;
// A mergeable section with size 0 is useless because they don't have
// any data to merge. A mergeable string section with size 0 can be
// argued as invalid because it doesn't end with a null character.
// We'll avoid a mess by handling them as if they were non-mergeable.
- if (Sec.sh_size == 0)
+ if (sec.sh_size == 0)
return false;
// Check for sh_entsize. The ELF spec is not clear about the zero
@@ -359,17 +512,17 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// the section does not hold a table of fixed-size entries". We know
// that Rust 1.13 produces a string mergeable section with a zero
// sh_entsize. Here we just accept it rather than being picky about it.
- uint64_t EntSize = Sec.sh_entsize;
- if (EntSize == 0)
+ uint64_t entSize = sec.sh_entsize;
+ if (entSize == 0)
return false;
- if (Sec.sh_size % EntSize)
+ if (sec.sh_size % entSize)
fatal(toString(this) +
": SHF_MERGE section size must be a multiple of sh_entsize");
- uint64_t Flags = Sec.sh_flags;
- if (!(Flags & SHF_MERGE))
+ uint64_t flags = sec.sh_flags;
+ if (!(flags & SHF_MERGE))
return false;
- if (Flags & SHF_WRITE)
+ if (flags & SHF_WRITE)
fatal(toString(this) + ": writable SHF_MERGE section is not supported");
return true;
@@ -385,118 +538,138 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// When the option is given, we link "just symbols". The section table is
// initialized with null pointers.
template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() {
- ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
- this->Sections.resize(ObjSections.size());
+ ArrayRef<Elf_Shdr> sections = CHECK(this->getObj().sections(), this);
+ this->sections.resize(sections.size());
+}
- for (const Elf_Shdr &Sec : ObjSections) {
- if (Sec.sh_type != SHT_SYMTAB)
- continue;
- this->initSymtab(ObjSections, &Sec);
+// An ELF object file may contain a `.deplibs` section. If it exists, the
+// section contains a list of library specifiers such as `m` for libm. This
+// function resolves a given name by finding the first matching library checking
+// the various ways that a library can be specified to LLD. This ELF extension
+// is a form of autolinking and is called `dependent libraries`. It is currently
+// unique to LLVM and lld.
+static void addDependentLibrary(StringRef specifier, const InputFile *f) {
+ if (!config->dependentLibraries)
return;
- }
+ if (fs::exists(specifier))
+ driver->addFile(specifier, /*withLOption=*/false);
+ else if (Optional<std::string> s = findFromSearchPaths(specifier))
+ driver->addFile(*s, /*withLOption=*/true);
+ else if (Optional<std::string> s = searchLibraryBaseName(specifier))
+ driver->addFile(*s, /*withLOption=*/true);
+ else
+ error(toString(f) +
+ ": unable to find library from dependent library specifier: " +
+ specifier);
}
template <class ELFT>
-void ObjFile<ELFT>::initializeSections(
- DenseSet<CachedHashStringRef> &ComdatGroups) {
- const ELFFile<ELFT> &Obj = this->getObj();
-
- ArrayRef<Elf_Shdr> ObjSections = CHECK(Obj.sections(), this);
- uint64_t Size = ObjSections.size();
- this->Sections.resize(Size);
- this->SectionStringTable =
- CHECK(Obj.getSectionStringTable(ObjSections), this);
-
- for (size_t I = 0, E = ObjSections.size(); I < E; I++) {
- if (this->Sections[I] == &InputSection::Discarded)
+void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
+ const ELFFile<ELFT> &obj = this->getObj();
+
+ ArrayRef<Elf_Shdr> objSections = CHECK(obj.sections(), this);
+ uint64_t size = objSections.size();
+ this->sections.resize(size);
+ this->sectionStringTable =
+ CHECK(obj.getSectionStringTable(objSections), this);
+
+ for (size_t i = 0, e = objSections.size(); i < e; i++) {
+ if (this->sections[i] == &InputSection::discarded)
continue;
- const Elf_Shdr &Sec = ObjSections[I];
+ const Elf_Shdr &sec = objSections[i];
- if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
- CGProfile = check(
- this->getObj().template getSectionContentsAsArray<Elf_CGProfile>(
- &Sec));
+ if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
+ cgProfile =
+ check(obj.template getSectionContentsAsArray<Elf_CGProfile>(&sec));
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
// if -r is given, we'll let the final link discard such sections.
// This is compatible with GNU.
- if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) {
- if (Sec.sh_type == SHT_LLVM_ADDRSIG) {
+ if ((sec.sh_flags & SHF_EXCLUDE) && !config->relocatable) {
+ if (sec.sh_type == SHT_LLVM_ADDRSIG) {
// We ignore the address-significance table if we know that the object
// file was created by objcopy or ld -r. This is because these tools
// will reorder the symbols in the symbol table, invalidating the data
// in the address-significance table, which refers to symbols by index.
- if (Sec.sh_link != 0)
- this->AddrsigSec = &Sec;
- else if (Config->ICF == ICFLevel::Safe)
+ if (sec.sh_link != 0)
+ this->addrsigSec = &sec;
+ else if (config->icf == ICFLevel::Safe)
warn(toString(this) + ": --icf=safe is incompatible with object "
"files created using objcopy or ld -r");
}
- this->Sections[I] = &InputSection::Discarded;
+ this->sections[i] = &InputSection::discarded;
continue;
}
- switch (Sec.sh_type) {
+ switch (sec.sh_type) {
case SHT_GROUP: {
// De-duplicate section groups by their signatures.
- StringRef Signature = getShtGroupSignature(ObjSections, Sec);
- bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second;
- this->Sections[I] = &InputSection::Discarded;
-
- // We only support GRP_COMDAT type of group. Get the all entries of the
- // section here to let getShtGroupEntries to check the type early for us.
- ArrayRef<Elf_Word> Entries = getShtGroupEntries(Sec);
-
- // If it is a new section group, we want to keep group members.
- // Group leader sections, which contain indices of group members, are
- // discarded because they are useless beyond this point. The only
- // exception is the -r option because in order to produce re-linkable
- // object files, we want to pass through basically everything.
- if (IsNew) {
- if (Config->Relocatable)
- this->Sections[I] = createInputSection(Sec);
+ StringRef signature = getShtGroupSignature(objSections, sec);
+ this->sections[i] = &InputSection::discarded;
+
+
+ ArrayRef<Elf_Word> entries =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Word>(&sec), this);
+ if (entries.empty())
+ fatal(toString(this) + ": empty SHT_GROUP");
+
+ // The first word of a SHT_GROUP section contains flags. Currently,
+ // the standard defines only "GRP_COMDAT" flag for the COMDAT group.
+ // An group with the empty flag doesn't define anything; such sections
+ // are just skipped.
+ if (entries[0] == 0)
+ continue;
+
+ if (entries[0] != GRP_COMDAT)
+ fatal(toString(this) + ": unsupported SHT_GROUP format");
+
+ bool isNew =
+ ignoreComdats ||
+ symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this)
+ .second;
+ if (isNew) {
+ if (config->relocatable)
+ this->sections[i] = createInputSection(sec);
continue;
}
// Otherwise, discard group members.
- for (uint32_t SecIndex : Entries) {
- if (SecIndex >= Size)
+ for (uint32_t secIndex : entries.slice(1)) {
+ if (secIndex >= size)
fatal(toString(this) +
- ": invalid section index in group: " + Twine(SecIndex));
- this->Sections[SecIndex] = &InputSection::Discarded;
+ ": invalid section index in group: " + Twine(secIndex));
+ this->sections[secIndex] = &InputSection::discarded;
}
break;
}
- case SHT_SYMTAB:
- this->initSymtab(ObjSections, &Sec);
- break;
case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this);
+ shndxTable = CHECK(obj.getSHNDXTable(sec, objSections), this);
break;
+ case SHT_SYMTAB:
case SHT_STRTAB:
case SHT_NULL:
break;
default:
- this->Sections[I] = createInputSection(Sec);
+ this->sections[i] = createInputSection(sec);
}
// .ARM.exidx sections have a reverse dependency on the InputSection they
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
- if (Sec.sh_flags & SHF_LINK_ORDER) {
- InputSectionBase *LinkSec = nullptr;
- if (Sec.sh_link < this->Sections.size())
- LinkSec = this->Sections[Sec.sh_link];
- if (!LinkSec)
+ if (sec.sh_flags & SHF_LINK_ORDER) {
+ InputSectionBase *linkSec = nullptr;
+ if (sec.sh_link < this->sections.size())
+ linkSec = this->sections[sec.sh_link];
+ if (!linkSec)
fatal(toString(this) +
- ": invalid sh_link index: " + Twine(Sec.sh_link));
+ ": invalid sh_link index: " + Twine(sec.sh_link));
- InputSection *IS = cast<InputSection>(this->Sections[I]);
- LinkSec->DependentSections.push_back(IS);
- if (!isa<InputSection>(LinkSec))
- error("a section " + IS->Name +
+ InputSection *isec = cast<InputSection>(this->sections[i]);
+ linkSec->dependentSections.push_back(isec);
+ if (!isa<InputSection>(linkSec))
+ error("a section " + isec->name +
" with SHF_LINK_ORDER should not refer a non-regular "
"section: " +
- toString(LinkSec));
+ toString(linkSec));
}
}
}
@@ -504,9 +677,9 @@ void ObjFile<ELFT>::initializeSections(
// For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD
// flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how
// the input objects have been compiled.
-static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
- const InputFile *F) {
- if (!Attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
+static void updateARMVFPArgs(const ARMAttributeParser &attributes,
+ const InputFile *f) {
+ if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
// 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)
@@ -514,31 +687,31 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
// as a clash.
return;
- unsigned VFPArgs = Attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
- ARMVFPArgKind Arg;
- switch (VFPArgs) {
+ unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ ARMVFPArgKind arg;
+ switch (vfpArgs) {
case ARMBuildAttrs::BaseAAPCS:
- Arg = ARMVFPArgKind::Base;
+ arg = ARMVFPArgKind::Base;
break;
case ARMBuildAttrs::HardFPAAPCS:
- Arg = ARMVFPArgKind::VFP;
+ arg = ARMVFPArgKind::VFP;
break;
case ARMBuildAttrs::ToolChainFPPCS:
// Tool chain specific convention that conforms to neither AAPCS variant.
- Arg = ARMVFPArgKind::ToolChain;
+ arg = ARMVFPArgKind::ToolChain;
break;
case ARMBuildAttrs::CompatibleFPAAPCS:
// Object compatible with all conventions.
return;
default:
- error(toString(F) + ": unknown Tag_ABI_VFP_args value: " + Twine(VFPArgs));
+ error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs));
return;
}
// Follow ld.bfd and error if there is a mix of calling conventions.
- if (Config->ARMVFPArgs != Arg && Config->ARMVFPArgs != ARMVFPArgKind::Default)
- error(toString(F) + ": incompatible Tag_ABI_VFP_args");
+ if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default)
+ error(toString(f) + ": incompatible Tag_ABI_VFP_args");
else
- Config->ARMVFPArgs = Arg;
+ config->armVFPArgs = arg;
}
// The ARM support in lld makes some use of instructions that are not available
@@ -550,11 +723,11 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
// at compile time. We follow the convention that if at least one input object
// 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))
+static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
+ if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
return;
- auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
- switch (Arch) {
+ auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ switch (arch) {
case ARMBuildAttrs::Pre_v4:
case ARMBuildAttrs::v4:
case ARMBuildAttrs::v4T:
@@ -566,70 +739,156 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) {
case ARMBuildAttrs::v6:
case ARMBuildAttrs::v6KZ:
case ARMBuildAttrs::v6K:
- Config->ARMHasBlx = true;
+ config->armHasBlx = true;
// Architectures used in pre-Cortex processors do not support
// The J1 = 1 J2 = 1 Thumb branch range extension, with the exception
// of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do.
break;
default:
// All other Architectures have BLX and extended branch encoding
- Config->ARMHasBlx = true;
- Config->ARMJ1J2BranchEncoding = true;
- if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M)
+ config->armHasBlx = true;
+ config->armJ1J2BranchEncoding = true;
+ if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M)
// All Architectures used in Cortex processors with the exception
// of v6-M and v6S-M have the MOVT and MOVW instructions.
- Config->ARMHasMovtMovw = true;
+ config->armHasMovtMovw = true;
break;
}
}
+// If a source file is compiled with x86 hardware-assisted call flow control
+// enabled, the generated object file contains feature flags indicating that
+// fact. This function reads the feature flags and returns it.
+//
+// Essentially we want to read a single 32-bit value in this function, but this
+// function is rather complicated because the value is buried deep inside a
+// .note.gnu.property section.
+//
+// The section consists of one or more NOTE records. Each NOTE record consists
+// of zero or more type-length-value fields. We want to find a field of a
+// certain type. It seems a bit too much to just store a 32-bit value, perhaps
+// the ABI is unnecessarily complicated.
+template <class ELFT>
+static uint32_t readAndFeatures(ObjFile<ELFT> *obj, ArrayRef<uint8_t> data) {
+ using Elf_Nhdr = typename ELFT::Nhdr;
+ using Elf_Note = typename ELFT::Note;
+
+ uint32_t featuresSet = 0;
+ while (!data.empty()) {
+ // Read one NOTE record.
+ if (data.size() < sizeof(Elf_Nhdr))
+ fatal(toString(obj) + ": .note.gnu.property: section too short");
+
+ auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
+ if (data.size() < nhdr->getSize())
+ fatal(toString(obj) + ": .note.gnu.property: section too short");
+
+ Elf_Note note(*nhdr);
+ if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") {
+ data = data.slice(nhdr->getSize());
+ continue;
+ }
+
+ uint32_t featureAndType = config->emachine == EM_AARCH64
+ ? GNU_PROPERTY_AARCH64_FEATURE_1_AND
+ : GNU_PROPERTY_X86_FEATURE_1_AND;
+
+ // Read a body of a NOTE record, which consists of type-length-value fields.
+ ArrayRef<uint8_t> desc = note.getDesc();
+ while (!desc.empty()) {
+ if (desc.size() < 8)
+ fatal(toString(obj) + ": .note.gnu.property: section too short");
+
+ uint32_t type = read32le(desc.data());
+ uint32_t size = read32le(desc.data() + 4);
+
+ if (type == featureAndType) {
+ // We found a FEATURE_1_AND field. There may be more than one of these
+ // in a .note.gnu.propery section, for a relocatable object we
+ // accumulate the bits set.
+ featuresSet |= read32le(desc.data() + 8);
+ }
+
+ // On 64-bit, a payload may be followed by a 4-byte padding to make its
+ // size a multiple of 8.
+ if (ELFT::Is64Bits)
+ size = alignTo(size, 8);
+
+ desc = desc.slice(size + 8); // +8 for Type and Size
+ }
+
+ // Go to next NOTE record to look for more FEATURE_1_AND descriptions.
+ data = data.slice(nhdr->getSize());
+ }
+
+ return featuresSet;
+}
+
template <class ELFT>
-InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
- uint32_t Idx = Sec.sh_info;
- if (Idx >= this->Sections.size())
- fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx));
- InputSectionBase *Target = this->Sections[Idx];
+InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &sec) {
+ uint32_t idx = sec.sh_info;
+ if (idx >= this->sections.size())
+ fatal(toString(this) + ": invalid relocated section index: " + Twine(idx));
+ InputSectionBase *target = this->sections[idx];
// Strictly speaking, a relocation section must be included in the
// group of the section it relocates. However, LLVM 3.3 and earlier
// would fail to do so, so we gracefully handle that case.
- if (Target == &InputSection::Discarded)
+ if (target == &InputSection::discarded)
return nullptr;
- if (!Target)
+ if (!target)
fatal(toString(this) + ": unsupported relocation reference");
- return Target;
+ return target;
}
// Create a regular InputSection class that has the same contents
// as a given section.
-static InputSection *toRegularSection(MergeInputSection *Sec) {
- return make<InputSection>(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment,
- Sec->data(), Sec->Name);
+static InputSection *toRegularSection(MergeInputSection *sec) {
+ return make<InputSection>(sec->file, sec->flags, sec->type, sec->alignment,
+ sec->data(), sec->name);
}
template <class ELFT>
-InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
- StringRef Name = getSectionName(Sec);
+InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
+ StringRef name = getSectionName(sec);
- switch (Sec.sh_type) {
+ switch (sec.sh_type) {
case SHT_ARM_ATTRIBUTES: {
- if (Config->EMachine != EM_ARM)
+ if (config->emachine != EM_ARM)
break;
- ARMAttributeParser Attributes;
- ArrayRef<uint8_t> Contents = check(this->getObj().getSectionContents(&Sec));
- Attributes.Parse(Contents, /*isLittle*/ Config->EKind == ELF32LEKind);
- updateSupportedARMFeatures(Attributes);
- updateARMVFPArgs(Attributes, this);
+ ARMAttributeParser attributes;
+ ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec));
+ attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind);
+ updateSupportedARMFeatures(attributes);
+ updateARMVFPArgs(attributes, this);
// FIXME: Retain the first attribute section we see. The eglibc ARM
// dynamic loaders require the presence of an attribute section for dlopen
// to work. In a full implementation we would merge all attribute sections.
- if (In.ARMAttributes == nullptr) {
- In.ARMAttributes = make<InputSection>(*this, Sec, Name);
- return In.ARMAttributes;
+ if (in.armAttributes == nullptr) {
+ in.armAttributes = make<InputSection>(*this, sec, name);
+ return in.armAttributes;
}
- return &InputSection::Discarded;
+ return &InputSection::discarded;
+ }
+ case SHT_LLVM_DEPENDENT_LIBRARIES: {
+ if (config->relocatable)
+ break;
+ ArrayRef<char> data =
+ CHECK(this->getObj().template getSectionContentsAsArray<char>(&sec), this);
+ if (!data.empty() && data.back() != '\0') {
+ error(toString(this) +
+ ": corrupted dependent libraries section (unterminated string): " +
+ name);
+ return &InputSection::discarded;
+ }
+ for (const char *d = data.begin(), *e = data.end(); d < e;) {
+ StringRef s(d);
+ addDependentLibrary(s, this);
+ d += s.size() + 1;
+ }
+ return &InputSection::discarded;
}
case SHT_RELA:
case SHT_REL: {
@@ -638,25 +897,25 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// and the group is discarded, even though it's a violation of the
// spec. We handle that situation gracefully by discarding dangling
// relocation sections.
- InputSectionBase *Target = getRelocTarget(Sec);
- if (!Target)
+ InputSectionBase *target = getRelocTarget(sec);
+ if (!target)
return nullptr;
// This section contains relocation information.
// If -r is given, we do not interpret or apply relocation
// but just copy relocation sections to output.
- if (Config->Relocatable) {
- InputSection *RelocSec = make<InputSection>(*this, Sec, Name);
+ if (config->relocatable) {
+ InputSection *relocSec = make<InputSection>(*this, sec, name);
// We want to add a dependency to target, similar like we do for
// -emit-relocs below. This is useful for the case when linker script
// contains the "/DISCARD/". It is perhaps uncommon to use a script with
// -r, but we faced it in the Linux kernel and have to handle such case
// and not to crash.
- Target->DependentSections.push_back(RelocSec);
- return RelocSec;
+ target->dependentSections.push_back(relocSec);
+ return relocSec;
}
- if (Target->FirstRelocation)
+ if (target->firstRelocation)
fatal(toString(this) +
": multiple relocation sections to one section are not supported");
@@ -665,33 +924,33 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// because applying relocations at end of linking changes section
// contents. So, we simply handle such sections as non-mergeable ones.
// Degrading like this is acceptable because section merging is optional.
- if (auto *MS = dyn_cast<MergeInputSection>(Target)) {
- Target = toRegularSection(MS);
- this->Sections[Sec.sh_info] = Target;
+ if (auto *ms = dyn_cast<MergeInputSection>(target)) {
+ target = toRegularSection(ms);
+ this->sections[sec.sh_info] = target;
}
- if (Sec.sh_type == SHT_RELA) {
- ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
- Target->FirstRelocation = Rels.begin();
- Target->NumRelocations = Rels.size();
- Target->AreRelocsRela = true;
+ if (sec.sh_type == SHT_RELA) {
+ ArrayRef<Elf_Rela> rels = CHECK(getObj().relas(&sec), this);
+ target->firstRelocation = rels.begin();
+ target->numRelocations = rels.size();
+ target->areRelocsRela = true;
} else {
- ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
- Target->FirstRelocation = Rels.begin();
- Target->NumRelocations = Rels.size();
- Target->AreRelocsRela = false;
+ ArrayRef<Elf_Rel> rels = CHECK(getObj().rels(&sec), this);
+ target->firstRelocation = rels.begin();
+ target->numRelocations = rels.size();
+ target->areRelocsRela = false;
}
- assert(isUInt<31>(Target->NumRelocations));
+ assert(isUInt<31>(target->numRelocations));
// Relocation sections processed by the linker are usually removed
// from the output, so returning `nullptr` for the normal case.
// However, if -emit-relocs is given, we need to leave them in the output.
// (Some post link analysis tools need this information.)
- if (Config->EmitRelocs) {
- InputSection *RelocSec = make<InputSection>(*this, Sec, Name);
+ if (config->emitRelocs) {
+ InputSection *relocSec = make<InputSection>(*this, sec, name);
// We will not emit relocation section if target was discarded.
- Target->DependentSections.push_back(RelocSec);
- return RelocSec;
+ target->dependentSections.push_back(relocSec);
+ return relocSec;
}
return nullptr;
}
@@ -710,28 +969,42 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// explicitly told to do otherwise (by -z execstack). Because the stack
// executable-ness is controlled solely by command line options,
// .note.GNU-stack sections are simply ignored.
- if (Name == ".note.GNU-stack")
- return &InputSection::Discarded;
+ if (name == ".note.GNU-stack")
+ return &InputSection::discarded;
+
+ // Object files that use processor features such as Intel Control-Flow
+ // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
+ // .note.gnu.property section containing a bitfield of feature bits like the
+ // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
+ //
+ // Since we merge bitmaps from multiple object files to create a new
+ // .note.gnu.property containing a single AND'ed bitmap, we discard an input
+ // file's .note.gnu.property section.
+ if (name == ".note.gnu.property") {
+ ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec));
+ this->andFeatures = readAndFeatures(this, contents);
+ return &InputSection::discarded;
+ }
// Split stacks is a feature to support a discontiguous stack,
// commonly used in the programming language Go. For the details,
// see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled
// for split stack will include a .note.GNU-split-stack section.
- if (Name == ".note.GNU-split-stack") {
- if (Config->Relocatable) {
+ if (name == ".note.GNU-split-stack") {
+ if (config->relocatable) {
error("cannot mix split-stack and non-split-stack in a relocatable link");
- return &InputSection::Discarded;
+ return &InputSection::discarded;
}
- this->SplitStack = true;
- return &InputSection::Discarded;
+ this->splitStack = true;
+ return &InputSection::discarded;
}
// An object file cmpiled for split stack, but where some of the
// functions were compiled with the no_split_stack_attribute will
// include a .note.GNU-no-split-stack section.
- if (Name == ".note.GNU-no-split-stack") {
- this->SomeNoSplitStack = true;
- return &InputSection::Discarded;
+ if (name == ".note.GNU-no-split-stack") {
+ this->someNoSplitStack = true;
+ return &InputSection::discarded;
}
// The linkonce feature is a sort of proto-comdat. Some glibc i386 object
@@ -739,245 +1012,205 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// sections. Drop those sections to avoid duplicate symbol errors.
// FIXME: This is glibc PR20543, we should remove this hack once that has been
// fixed for a while.
- if (Name.startswith(".gnu.linkonce."))
- return &InputSection::Discarded;
+ if (name == ".gnu.linkonce.t.__x86.get_pc_thunk.bx" ||
+ name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx")
+ return &InputSection::discarded;
// If we are creating a new .build-id section, strip existing .build-id
// sections so that the output won't have more than one .build-id.
// This is not usually a problem because input object files normally don't
// have .build-id sections, but you can create such files by
// "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it.
- if (Name == ".note.gnu.build-id" && Config->BuildId != BuildIdKind::None)
- return &InputSection::Discarded;
+ if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None)
+ return &InputSection::discarded;
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
- if (Name == ".eh_frame" && !Config->Relocatable)
- return make<EhInputSection>(*this, Sec, Name);
+ if (name == ".eh_frame" && !config->relocatable)
+ return make<EhInputSection>(*this, sec, name);
- if (shouldMerge(Sec))
- return make<MergeInputSection>(*this, Sec, Name);
- return make<InputSection>(*this, Sec, Name);
+ if (shouldMerge(sec))
+ return make<MergeInputSection>(*this, sec, name);
+ return make<InputSection>(*this, sec, name);
}
template <class ELFT>
-StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
- return CHECK(this->getObj().getSectionName(&Sec, SectionStringTable), this);
+StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &sec) {
+ return CHECK(getObj().getSectionName(&sec, sectionStringTable), this);
}
+// Initialize this->Symbols. this->Symbols is a parallel array as
+// its corresponding ELF symbol table.
template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
- this->Symbols.reserve(this->ELFSyms.size());
- for (const Elf_Sym &Sym : this->ELFSyms)
- this->Symbols.push_back(createSymbol(&Sym));
-}
-
-template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) {
- int Binding = Sym->getBinding();
-
- uint32_t SecIdx = this->getSectionIndex(*Sym);
- if (SecIdx >= this->Sections.size())
- fatal(toString(this) + ": invalid section index: " + Twine(SecIdx));
-
- InputSectionBase *Sec = this->Sections[SecIdx];
- uint8_t StOther = Sym->st_other;
- uint8_t Type = Sym->getType();
- uint64_t Value = Sym->st_value;
- uint64_t Size = Sym->st_size;
-
- if (Binding == STB_LOCAL) {
- if (Sym->getType() == STT_FILE)
- SourceFile = CHECK(Sym->getName(this->StringTable), this);
+ ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
+ this->symbols.resize(eSyms.size());
+
+ // Our symbol table may have already been partially 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) {
+ 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));
+
+ InputSectionBase *sec = this->sections[secIdx];
+ uint8_t binding = eSym.getBinding();
+ 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;
+ }
- if (this->StringTable.size() <= Sym->st_name)
- fatal(toString(this) + ": invalid symbol name offset");
+ // Handle global undefined symbols.
+ if (eSym.st_shndx == SHN_UNDEF) {
+ this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
+ continue;
+ }
- StringRefZ Name = this->StringTable.data() + Sym->st_name;
- if (Sym->st_shndx == SHN_UNDEF)
- return make<Undefined>(this, Name, Binding, StOther, Type);
+ // Handle global common symbols.
+ if (eSym.st_shndx == SHN_COMMON) {
+ if (value == 0 || value >= UINT32_MAX)
+ fatal(toString(this) + ": common symbol '" + StringRef(name.data) +
+ "' has invalid alignment: " + Twine(value));
+ this->symbols[i]->resolve(
+ CommonSymbol{this, name, binding, stOther, type, value, size});
+ continue;
+ }
- return make<Defined>(this, Name, Binding, StOther, Type, Value, Size, Sec);
- }
+ // If a defined symbol is in a discarded section, handle it as if it
+ // were an undefined symbol. Such symbol doesn't comply with the
+ // standard, but in practice, a .eh_frame often directly refer
+ // 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});
+ continue;
+ }
- StringRef Name = CHECK(Sym->getName(this->StringTable), this);
-
- switch (Sym->st_shndx) {
- case SHN_UNDEF:
- return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this);
- case SHN_COMMON:
- if (Value == 0 || Value >= UINT32_MAX)
- fatal(toString(this) + ": common symbol '" + Name +
- "' has invalid alignment: " + Twine(Value));
- return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, *this);
- }
+ // Handle global defined symbols.
+ if (binding == STB_GLOBAL || binding == STB_WEAK ||
+ binding == STB_GNU_UNIQUE) {
+ this->symbols[i]->resolve(
+ Defined{this, name, binding, stOther, type, value, size, sec});
+ continue;
+ }
- switch (Binding) {
- default:
- fatal(toString(this) + ": unexpected binding: " + Twine(Binding));
- case STB_GLOBAL:
- case STB_WEAK:
- case STB_GNU_UNIQUE:
- if (Sec == &InputSection::Discarded)
- return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this);
- return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec,
- this);
+ fatal(toString(this) + ": unexpected binding: " + Twine((int)binding));
}
}
-ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
- : InputFile(ArchiveKind, File->getMemoryBufferRef()),
- File(std::move(File)) {}
+ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&file)
+ : InputFile(ArchiveKind, file->getMemoryBufferRef()),
+ file(std::move(file)) {}
-template <class ELFT> void ArchiveFile::parse() {
- for (const Archive::Symbol &Sym : File->symbols())
- Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym);
+void ArchiveFile::parse() {
+ for (const Archive::Symbol &sym : file->symbols())
+ symtab->addSymbol(LazyArchive{*this, sym});
}
// Returns a buffer pointing to a member file containing a given symbol.
-InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) {
- Archive::Child C =
- CHECK(Sym.getMember(), toString(this) +
+void ArchiveFile::fetch(const Archive::Symbol &sym) {
+ Archive::Child c =
+ CHECK(sym.getMember(), toString(this) +
": could not get the member for symbol " +
- Sym.getName());
+ sym.getName());
- if (!Seen.insert(C.getChildOffset()).second)
- return nullptr;
+ if (!seen.insert(c.getChildOffset()).second)
+ return;
- MemoryBufferRef MB =
- CHECK(C.getMemoryBufferRef(),
+ MemoryBufferRef mb =
+ CHECK(c.getMemoryBufferRef(),
toString(this) +
": could not get the buffer for the member defining symbol " +
- Sym.getName());
+ sym.getName());
- if (Tar && C.getParent()->isThin())
- Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), MB.getBuffer());
+ 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());
- File->GroupId = GroupId;
- return File;
+ InputFile *file = createObjectFile(
+ mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset());
+ file->groupId = groupId;
+ parseFile(file);
}
-template <class ELFT>
-SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName)
- : ELFFileBase<ELFT>(Base::SharedKind, M), SoName(DefaultSoName),
- IsNeeded(!Config->AsNeeded) {}
-
-// Partially parse the shared object file so that we can call
-// getSoName on this object.
-template <class ELFT> void SharedFile<ELFT>::parseSoName() {
- const Elf_Shdr *DynamicSec = nullptr;
- const ELFFile<ELFT> Obj = this->getObj();
- ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
-
- // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
- for (const Elf_Shdr &Sec : Sections) {
- switch (Sec.sh_type) {
- default:
- continue;
- case SHT_DYNSYM:
- this->initSymtab(Sections, &Sec);
- break;
- case SHT_DYNAMIC:
- DynamicSec = &Sec;
- break;
- case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, Sections), this);
- break;
- case SHT_GNU_versym:
- this->VersymSec = &Sec;
- break;
- case SHT_GNU_verdef:
- this->VerdefSec = &Sec;
- break;
- }
- }
-
- if (this->VersymSec && this->ELFSyms.empty())
- error("SHT_GNU_versym should be associated with symbol table");
-
- // Search for a DT_SONAME tag to initialize this->SoName.
- if (!DynamicSec)
- return;
- ArrayRef<Elf_Dyn> Arr =
- CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec), this);
- for (const Elf_Dyn &Dyn : Arr) {
- if (Dyn.d_tag == DT_SONAME) {
- uint64_t Val = Dyn.getVal();
- if (Val >= this->StringTable.size())
- fatal(toString(this) + ": invalid DT_SONAME entry");
- SoName = this->StringTable.data() + Val;
- return;
- }
- }
-}
-
-// Parses ".gnu.version" section which is a parallel array for the symbol table.
-// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL.
-template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() {
- size_t Size = this->ELFSyms.size() - this->FirstGlobal;
- if (!VersymSec)
- return std::vector<uint32_t>(Size, VER_NDX_GLOBAL);
-
- const char *Base = this->MB.getBuffer().data();
- const Elf_Versym *Versym =
- reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
- this->FirstGlobal;
-
- std::vector<uint32_t> Ret(Size);
- for (size_t I = 0; I < Size; ++I)
- Ret[I] = Versym[I].vs_index;
- return Ret;
-}
+unsigned SharedFile::vernauxNum;
-// Parse the version definitions in the object file if present. Returns a vector
-// whose nth element contains a pointer to the Elf_Verdef for version identifier
-// n. Version identifiers that are not definitions map to nullptr.
-template <class ELFT>
-std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
- if (!VerdefSec)
+// Parse the version definitions in the object file if present, and return a
+// vector whose nth element contains a pointer to the Elf_Verdef for version
+// identifier n. Version identifiers that are not definitions map to nullptr.
+template <typename ELFT>
+static std::vector<const void *> parseVerdefs(const uint8_t *base,
+ const typename ELFT::Shdr *sec) {
+ if (!sec)
return {};
// We cannot determine the largest verdef identifier without inspecting
// every Elf_Verdef, but both bfd and gold assign verdef identifiers
// sequentially starting from 1, so we predict that the largest identifier
- // will be VerdefCount.
- unsigned VerdefCount = VerdefSec->sh_info;
- std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1);
+ // will be verdefCount.
+ unsigned verdefCount = sec->sh_info;
+ std::vector<const void *> verdefs(verdefCount + 1);
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
- const char *Base = this->MB.getBuffer().data();
- const char *Verdef = Base + VerdefSec->sh_offset;
- for (unsigned I = 0; I != VerdefCount; ++I) {
- auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
- Verdef += CurVerdef->vd_next;
- unsigned VerdefIndex = CurVerdef->vd_ndx;
- Verdefs.resize(VerdefIndex + 1);
- Verdefs[VerdefIndex] = CurVerdef;
+ const uint8_t *verdef = base + sec->sh_offset;
+ for (unsigned i = 0; i != verdefCount; ++i) {
+ auto *curVerdef = reinterpret_cast<const typename ELFT::Verdef *>(verdef);
+ verdef += curVerdef->vd_next;
+ unsigned verdefIndex = curVerdef->vd_ndx;
+ verdefs.resize(verdefIndex + 1);
+ verdefs[verdefIndex] = curVerdef;
}
-
- return Verdefs;
+ return verdefs;
}
// 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
// the original alignment requirements. We infer it in this function.
-template <class ELFT>
-uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections,
- const Elf_Sym &Sym) {
- uint64_t Ret = UINT64_MAX;
- if (Sym.st_value)
- Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
- if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size())
- Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign);
- return (Ret > UINT32_MAX) ? 0 : Ret;
+template <typename ELFT>
+static uint64_t getAlignment(ArrayRef<typename ELFT::Shdr> sections,
+ const typename ELFT::Sym &sym) {
+ uint64_t ret = UINT64_MAX;
+ if (sym.st_value)
+ ret = 1ULL << countTrailingZeros((uint64_t)sym.st_value);
+ if (0 < sym.st_shndx && sym.st_shndx < sections.size())
+ ret = std::min<uint64_t>(ret, sections[sym.st_shndx].sh_addralign);
+ return (ret > UINT32_MAX) ? 0 : ret;
}
-// Fully parse the shared object file. This must be called after parseSoName().
+// Fully parse the shared object file.
//
// This function parses symbol versions. If a DSO has version information,
// the file has a ".gnu.version_d" section which contains symbol version
@@ -992,80 +1225,163 @@ uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections,
// The file format for symbol versioning is perhaps a bit more complicated
// than necessary, but you can easily understand the code if you wrap your
// head around the data structure described above.
-template <class ELFT> void SharedFile<ELFT>::parseRest() {
- Verdefs = parseVerdefs(); // parse .gnu.version_d
- std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version
- ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
+template <class ELFT> void SharedFile::parse() {
+ using Elf_Dyn = typename ELFT::Dyn;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
+ using Elf_Verdef = typename ELFT::Verdef;
+ using Elf_Versym = typename ELFT::Versym;
+
+ ArrayRef<Elf_Dyn> dynamicTags;
+ const ELFFile<ELFT> obj = this->getObj<ELFT>();
+ ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this);
+
+ const Elf_Shdr *versymSec = nullptr;
+ const Elf_Shdr *verdefSec = nullptr;
+
+ // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
+ for (const Elf_Shdr &sec : sections) {
+ switch (sec.sh_type) {
+ default:
+ continue;
+ case SHT_DYNAMIC:
+ dynamicTags =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Dyn>(&sec), this);
+ break;
+ case SHT_GNU_versym:
+ versymSec = &sec;
+ break;
+ case SHT_GNU_verdef:
+ verdefSec = &sec;
+ break;
+ }
+ }
+
+ if (versymSec && numELFSyms == 0) {
+ error("SHT_GNU_versym should be associated with symbol table");
+ return;
+ }
+
+ // Search for a DT_SONAME tag to initialize this->soName.
+ for (const Elf_Dyn &dyn : dynamicTags) {
+ if (dyn.d_tag == DT_NEEDED) {
+ uint64_t val = dyn.getVal();
+ if (val >= this->stringTable.size())
+ fatal(toString(this) + ": invalid DT_NEEDED entry");
+ dtNeeded.push_back(this->stringTable.data() + val);
+ } else if (dyn.d_tag == DT_SONAME) {
+ uint64_t val = dyn.getVal();
+ if (val >= this->stringTable.size())
+ fatal(toString(this) + ": invalid DT_SONAME entry");
+ soName = this->stringTable.data() + val;
+ }
+ }
+
+ // DSOs are uniquified not by filename but by soname.
+ DenseMap<StringRef, SharedFile *>::iterator it;
+ bool wasInserted;
+ std::tie(it, wasInserted) = symtab->soNames.try_emplace(soName, this);
+
+ // If a DSO appears more than once on the command line with and without
+ // --as-needed, --no-as-needed takes precedence over --as-needed because a
+ // user can add an extra DSO with --no-as-needed to force it to be added to
+ // the dependency list.
+ it->second->isNeeded |= isNeeded;
+ if (!wasInserted)
+ return;
+
+ sharedFiles.push_back(this);
+
+ verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec);
+
+ // 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);
+ if (versymSec) {
+ ArrayRef<Elf_Versym> versym =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Versym>(versymSec),
+ this)
+ .slice(firstGlobal);
+ for (size_t i = 0; i < size; ++i)
+ versyms[i] = versym[i].vs_index;
+ }
// System libraries can have a lot of symbols with versions. Using a
// fixed buffer for computing the versions name (foo@ver) can save a
// lot of allocations.
- SmallString<0> VersionedNameBuffer;
+ SmallString<0> versionedNameBuffer;
// Add symbols to the symbol table.
- ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms();
- for (size_t I = 0; I < Syms.size(); ++I) {
- const Elf_Sym &Sym = Syms[I];
+ ArrayRef<Elf_Sym> syms = this->getGlobalELFSyms<ELFT>();
+ for (size_t i = 0; i < syms.size(); ++i) {
+ const Elf_Sym &sym = syms[i];
// ELF spec requires that all local symbols precede weak or global
// symbols in each symbol table, and the index of first non-local symbol
// is stored to sh_info. If a local symbol appears after some non-local
// symbol, that's a violation of the spec.
- StringRef Name = CHECK(Sym.getName(this->StringTable), this);
- if (Sym.getBinding() == STB_LOCAL) {
- warn("found local symbol '" + Name +
+ StringRef name = CHECK(sym.getName(this->stringTable), this);
+ if (sym.getBinding() == STB_LOCAL) {
+ warn("found local symbol '" + name +
"' in global part of symbol table in file " + toString(this));
continue;
}
- if (Sym.isUndefined()) {
- Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
- Sym.st_other, Sym.getType(),
- /*CanOmitFromDynSym=*/false, this);
- S->ExportDynamic = true;
+ if (sym.isUndefined()) {
+ Symbol *s = symtab->addSymbol(
+ Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
+ s->exportDynamic = true;
continue;
}
// 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")
+ uint32_t idx = versyms[i] & ~VERSYM_HIDDEN;
+ if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL &&
+ name == "_gp_disp")
continue;
- uint64_t Alignment = getAlignment(Sections, Sym);
- if (!(Versyms[I] & VERSYM_HIDDEN))
- Symtab->addShared(Name, *this, Sym, Alignment, Idx);
+ uint32_t alignment = getAlignment<ELFT>(sections, sym);
+ if (!(versyms[i] & VERSYM_HIDDEN)) {
+ symtab->addSymbol(SharedSymbol{*this, name, sym.getBinding(),
+ sym.st_other, sym.getType(), sym.st_value,
+ sym.st_size, alignment, idx});
+ }
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
- if (Idx == VER_NDX_GLOBAL)
+ if (idx == VER_NDX_GLOBAL)
continue;
- if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) {
- error("corrupt input file: version definition index " + Twine(Idx) +
- " for symbol " + Name + " is out of bounds\n>>> defined in " +
+ if (idx >= verdefs.size() || idx == VER_NDX_LOCAL) {
+ error("corrupt input file: version definition index " + Twine(idx) +
+ " for symbol " + name + " is out of bounds\n>>> defined in " +
toString(this));
continue;
}
- StringRef VerName =
- this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name;
- VersionedNameBuffer.clear();
- Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer);
- Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx);
+ StringRef verName =
+ this->stringTable.data() +
+ reinterpret_cast<const Elf_Verdef *>(verdefs[idx])->getAux()->vda_name;
+ versionedNameBuffer.clear();
+ name = (name + "@" + verName).toStringRef(versionedNameBuffer);
+ symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(),
+ sym.st_other, sym.getType(), sym.st_value,
+ sym.st_size, alignment, idx});
}
}
-static ELFKind getBitcodeELFKind(const Triple &T) {
- if (T.isLittleEndian())
- return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
- return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
+static ELFKind getBitcodeELFKind(const Triple &t) {
+ if (t.isLittleEndian())
+ return t.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
+ return t.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
}
-static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
- switch (T.getArch()) {
+static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) {
+ switch (t.getArch()) {
case Triple::aarch64:
return EM_AARCH64;
case Triple::amdgcn:
@@ -1088,25 +1404,28 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
case Triple::ppc64:
case Triple::ppc64le:
return EM_PPC64;
+ case Triple::riscv32:
+ case Triple::riscv64:
+ return EM_RISCV;
case Triple::x86:
- return T.isOSIAMCU() ? EM_IAMCU : EM_386;
+ return t.isOSIAMCU() ? EM_IAMCU : EM_386;
case Triple::x86_64:
return EM_X86_64;
default:
- error(Path + ": could not infer e_machine from bitcode target triple " +
- T.str());
+ error(path + ": could not infer e_machine from bitcode target triple " +
+ t.str());
return EM_NONE;
}
}
-BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
- uint64_t OffsetInArchive)
- : InputFile(BitcodeKind, MB) {
- this->ArchiveName = ArchiveName;
+BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive)
+ : InputFile(BitcodeKind, mb) {
+ this->archiveName = archiveName;
- std::string Path = MB.getBufferIdentifier().str();
- if (Config->ThinLTOIndexOnly)
- Path = replaceThinLTOSuffix(MB.getBufferIdentifier());
+ std::string path = mb.getBufferIdentifier().str();
+ if (config->thinLTOIndexOnly)
+ path = replaceThinLTOSuffix(mb.getBufferIdentifier());
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
// name. If two archives define two members with the same name, this
@@ -1114,20 +1433,21 @@ 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.
- MemoryBufferRef MBRef(
- MB.getBuffer(),
- Saver.save(ArchiveName + Path +
- (ArchiveName.empty() ? "" : utostr(OffsetInArchive))));
+ StringRef name = archiveName.empty()
+ ? saver.save(path)
+ : saver.save(archiveName + "(" + path + " at " +
+ utostr(offsetInArchive) + ")");
+ MemoryBufferRef mbref(mb.getBuffer(), name);
- Obj = CHECK(lto::InputFile::create(MBRef), this);
+ obj = CHECK(lto::InputFile::create(mbref), this);
- Triple T(Obj->getTargetTriple());
- EKind = getBitcodeELFKind(T);
- EMachine = getBitcodeMachineKind(MB.getBufferIdentifier(), T);
+ Triple t(obj->getTargetTriple());
+ ekind = getBitcodeELFKind(t);
+ emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t);
}
-static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
- switch (GvVisibility) {
+static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) {
+ switch (gvVisibility) {
case GlobalValue::DefaultVisibility:
return STV_DEFAULT;
case GlobalValue::HiddenVisibility:
@@ -1139,209 +1459,187 @@ static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
}
template <class ELFT>
-static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
- const lto::InputFile::Symbol &ObjSym,
- BitcodeFile &F) {
- StringRef Name = Saver.save(ObjSym.getName());
- uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL;
-
- uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE;
- uint8_t Visibility = mapVisibility(ObjSym.getVisibility());
- bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable();
-
- int C = ObjSym.getComdatIndex();
- if (C != -1 && !KeptComdats[C])
- return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
- CanOmitFromDynSym, &F);
-
- if (ObjSym.isUndefined())
- return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
- CanOmitFromDynSym, &F);
-
- if (ObjSym.isCommon())
- return Symtab->addCommon(Name, ObjSym.getCommonSize(),
- ObjSym.getCommonAlignment(), Binding, Visibility,
- STT_OBJECT, F);
-
- return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym,
- F);
-}
+static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
+ const lto::InputFile::Symbol &objSym,
+ BitcodeFile &f) {
+ StringRef name = saver.save(objSym.getName());
+ uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL;
+ uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE;
+ uint8_t visibility = mapVisibility(objSym.getVisibility());
+ bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable();
+
+ int c = objSym.getComdatIndex();
+ if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
+ Undefined New(&f, name, binding, visibility, type);
+ if (canOmitFromDynSym)
+ New.exportDynamic = false;
+ return symtab->addSymbol(New);
+ }
-template <class ELFT>
-void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
- std::vector<bool> KeptComdats;
- for (StringRef S : Obj->getComdatTable())
- KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second);
+ if (objSym.isCommon())
+ return symtab->addSymbol(
+ CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
+ objSym.getCommonAlignment(), objSym.getCommonSize()});
- for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
- Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, *this));
+ Defined New(&f, name, binding, visibility, type, 0, 0, nullptr);
+ if (canOmitFromDynSym)
+ New.exportDynamic = false;
+ return symtab->addSymbol(New);
}
-static ELFKind getELFKind(MemoryBufferRef MB) {
- unsigned char Size;
- unsigned char Endian;
- std::tie(Size, Endian) = getElfArchType(MB.getBuffer());
-
- if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB)
- fatal(MB.getBufferIdentifier() + ": invalid data encoding");
- if (Size != ELFCLASS32 && Size != ELFCLASS64)
- fatal(MB.getBufferIdentifier() + ": invalid file class");
+template <class ELFT> void BitcodeFile::parse() {
+ std::vector<bool> keptComdats;
+ for (StringRef s : obj->getComdatTable())
+ keptComdats.push_back(
+ symtab->comdatGroups.try_emplace(CachedHashStringRef(s), this).second);
- size_t BufSize = MB.getBuffer().size();
- if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) ||
- (Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr)))
- fatal(MB.getBufferIdentifier() + ": file is too short");
+ for (const lto::InputFile::Symbol &objSym : obj->symbols())
+ symbols.push_back(createBitcodeSymbol<ELFT>(keptComdats, objSym, *this));
- if (Size == ELFCLASS32)
- return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind;
- return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind;
+ for (auto l : obj->getDependentLibraries())
+ addDependentLibrary(l, this);
}
void BinaryFile::parse() {
- ArrayRef<uint8_t> Data = arrayRefFromStringRef(MB.getBuffer());
- auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- 8, Data, ".data");
- Sections.push_back(Section);
+ ArrayRef<uint8_t> data = arrayRefFromStringRef(mb.getBuffer());
+ auto *section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
+ 8, data, ".data");
+ sections.push_back(section);
// For each input file foo that is embedded to a result as a binary
// blob, we define _binary_foo_{start,end,size} symbols, so that
// user programs can access blobs by name. Non-alphanumeric
// characters in a filename are replaced with underscore.
- std::string S = "_binary_" + MB.getBufferIdentifier().str();
- for (size_t I = 0; I < S.size(); ++I)
- if (!isAlnum(S[I]))
- S[I] = '_';
-
- Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0,
- STB_GLOBAL, Section, nullptr);
- Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
- Data.size(), 0, STB_GLOBAL, Section, nullptr);
- Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
- Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
+ std::string s = "_binary_" + mb.getBufferIdentifier().str();
+ for (size_t i = 0; i < s.size(); ++i)
+ if (!isAlnum(s[i]))
+ s[i] = '_';
+
+ symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL,
+ STV_DEFAULT, STT_OBJECT, 0, 0, section});
+ symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL,
+ STV_DEFAULT, STT_OBJECT, data.size(), 0, section});
+ symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL,
+ STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
}
-InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
- uint64_t OffsetInArchive) {
- if (isBitcode(MB))
- return make<BitcodeFile>(MB, ArchiveName, OffsetInArchive);
+InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive) {
+ if (isBitcode(mb))
+ return make<BitcodeFile>(mb, archiveName, offsetInArchive);
- switch (getELFKind(MB)) {
+ switch (getELFKind(mb, archiveName)) {
case ELF32LEKind:
- return make<ObjFile<ELF32LE>>(MB, ArchiveName);
+ return make<ObjFile<ELF32LE>>(mb, archiveName);
case ELF32BEKind:
- return make<ObjFile<ELF32BE>>(MB, ArchiveName);
+ return make<ObjFile<ELF32BE>>(mb, archiveName);
case ELF64LEKind:
- return make<ObjFile<ELF64LE>>(MB, ArchiveName);
+ return make<ObjFile<ELF64LE>>(mb, archiveName);
case ELF64BEKind:
- return make<ObjFile<ELF64BE>>(MB, ArchiveName);
+ return make<ObjFile<ELF64BE>>(mb, archiveName);
default:
llvm_unreachable("getELFKind");
}
}
-InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
- switch (getELFKind(MB)) {
- case ELF32LEKind:
- return make<SharedFile<ELF32LE>>(MB, DefaultSoName);
- case ELF32BEKind:
- return make<SharedFile<ELF32BE>>(MB, DefaultSoName);
- case ELF64LEKind:
- return make<SharedFile<ELF64LE>>(MB, DefaultSoName);
- case ELF64BEKind:
- return make<SharedFile<ELF64BE>>(MB, DefaultSoName);
- default:
- llvm_unreachable("getELFKind");
- }
-}
+void LazyObjFile::fetch() {
+ if (mb.getBuffer().empty())
+ return;
-MemoryBufferRef LazyObjFile::getBuffer() {
- if (AddedToLink)
- return MemoryBufferRef();
- AddedToLink = true;
- return MB;
-}
+ InputFile *file = createObjectFile(mb, archiveName, offsetInArchive);
+ file->groupId = groupId;
-InputFile *LazyObjFile::fetch() {
- MemoryBufferRef MBRef = getBuffer();
- if (MBRef.getBuffer().empty())
- return nullptr;
+ 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);
- InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive);
- File->GroupId = GroupId;
- return File;
+ parseFile(file);
}
template <class ELFT> void LazyObjFile::parse() {
+ using Elf_Sym = typename ELFT::Sym;
+
// A lazy object file wraps either a bitcode file or an ELF file.
- if (isBitcode(this->MB)) {
- std::unique_ptr<lto::InputFile> Obj =
- CHECK(lto::InputFile::create(this->MB), this);
- for (const lto::InputFile::Symbol &Sym : Obj->symbols())
- if (!Sym.isUndefined())
- Symtab->addLazyObject<ELFT>(Saver.save(Sym.getName()), *this);
+ if (isBitcode(this->mb)) {
+ std::unique_ptr<lto::InputFile> obj =
+ CHECK(lto::InputFile::create(this->mb), this);
+ for (const lto::InputFile::Symbol &sym : obj->symbols()) {
+ if (sym.isUndefined())
+ continue;
+ symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())});
+ }
return;
}
- if (getELFKind(this->MB) != Config->EKind) {
- error("incompatible file: " + this->MB.getBufferIdentifier());
+ if (getELFKind(this->mb, archiveName) != config->ekind) {
+ error("incompatible file: " + this->mb.getBufferIdentifier());
return;
}
- ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer()));
- ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this);
+ // Find a symbol table.
+ ELFFile<ELFT> obj = check(ELFFile<ELFT>::create(mb.getBuffer()));
+ ArrayRef<typename ELFT::Shdr> sections = CHECK(obj.sections(), this);
- for (const typename ELFT::Shdr &Sec : Sections) {
- if (Sec.sh_type != SHT_SYMTAB)
+ for (const typename ELFT::Shdr &sec : sections) {
+ if (sec.sh_type != SHT_SYMTAB)
continue;
- typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this);
- uint32_t FirstGlobal = Sec.sh_info;
- StringRef StringTable =
- CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
+ // A symbol table is found.
+ ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(&sec), this);
+ uint32_t firstGlobal = sec.sh_info;
+ StringRef strtab = CHECK(obj.getStringTableForSymtab(sec, sections), this);
+ this->symbols.resize(eSyms.size());
+
+ // Get existing symbols or insert placeholder symbols.
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i)
+ if (eSyms[i].st_shndx != SHN_UNDEF)
+ this->symbols[i] = symtab->insert(CHECK(eSyms[i].getName(strtab), this));
+
+ // Replace existing symbols with LazyObject symbols.
+ //
+ // resolve() may trigger this->fetch() if an existing symbol is an
+ // undefined symbol. If that happens, this LazyObjFile has served
+ // its purpose, and we can exit from the loop early.
+ for (Symbol *sym : this->symbols) {
+ if (!sym)
+ continue;
+ sym->resolve(LazyObject{*this, sym->getName()});
- for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal))
- if (Sym.st_shndx != SHN_UNDEF)
- Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this),
- *this);
+ // MemoryBuffer is emptied if this file is instantiated as ObjFile.
+ if (mb.getBuffer().empty())
+ return;
+ }
return;
}
}
-std::string elf::replaceThinLTOSuffix(StringRef Path) {
- StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first;
- StringRef Repl = Config->ThinLTOObjectSuffixReplace.second;
+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;
+ if (path.consume_back(suffix))
+ return (path + repl).str();
+ return path;
}
-template void ArchiveFile::parse<ELF32LE>();
-template void ArchiveFile::parse<ELF32BE>();
-template void ArchiveFile::parse<ELF64LE>();
-template void ArchiveFile::parse<ELF64BE>();
-
-template void BitcodeFile::parse<ELF32LE>(DenseSet<CachedHashStringRef> &);
-template void BitcodeFile::parse<ELF32BE>(DenseSet<CachedHashStringRef> &);
-template void BitcodeFile::parse<ELF64LE>(DenseSet<CachedHashStringRef> &);
-template void BitcodeFile::parse<ELF64BE>(DenseSet<CachedHashStringRef> &);
+template void BitcodeFile::parse<ELF32LE>();
+template void BitcodeFile::parse<ELF32BE>();
+template void BitcodeFile::parse<ELF64LE>();
+template void BitcodeFile::parse<ELF64BE>();
template void LazyObjFile::parse<ELF32LE>();
template void LazyObjFile::parse<ELF32BE>();
template void LazyObjFile::parse<ELF64LE>();
template void LazyObjFile::parse<ELF64BE>();
-template class elf::ELFFileBase<ELF32LE>;
-template class elf::ELFFileBase<ELF32BE>;
-template class elf::ELFFileBase<ELF64LE>;
-template class elf::ELFFileBase<ELF64BE>;
-
template class elf::ObjFile<ELF32LE>;
template class elf::ObjFile<ELF32BE>;
template class elf::ObjFile<ELF64LE>;
template class elf::ObjFile<ELF64BE>;
-template class elf::SharedFile<ELF32LE>;
-template class elf::SharedFile<ELF32BE>;
-template class elf::SharedFile<ELF64LE>;
-template class elf::SharedFile<ELF64BE>;
+template void SharedFile::parse<ELF32LE>();
+template void SharedFile::parse<ELF32BE>();
+template void SharedFile::parse<ELF64LE>();
+template void SharedFile::parse<ELF64BE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 5094ddd804a5..5ccc3d402b37 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -1,9 +1,8 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -40,7 +39,7 @@ class InputSectionBase;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
-std::string toString(const elf::InputFile *F);
+std::string toString(const elf::InputFile *f);
namespace elf {
@@ -50,10 +49,13 @@ class Symbol;
// If -reproduce option is given, all input files are written
// to this tar archive.
-extern std::unique_ptr<llvm::TarWriter> Tar;
+extern std::unique_ptr<llvm::TarWriter> tar;
// Opens a given file.
-llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
+llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+
+// Add symbols in File to the symbol table.
+void parseFile(InputFile *file);
// The root class of input files.
class InputFile {
@@ -67,192 +69,230 @@ public:
BinaryKind,
};
- Kind kind() const { return FileKind; }
+ Kind kind() const { return fileKind; }
bool isElf() const {
- Kind K = kind();
- return K == ObjKind || K == SharedKind;
+ Kind k = kind();
+ return k == ObjKind || k == SharedKind;
}
- StringRef getName() const { return MB.getBufferIdentifier(); }
- MemoryBufferRef MB;
+ StringRef getName() const { return mb.getBufferIdentifier(); }
+ MemoryBufferRef mb;
// Returns sections. It is a runtime error to call this function
// on files that don't have the notion of sections.
ArrayRef<InputSectionBase *> getSections() const {
- assert(FileKind == ObjKind || FileKind == BinaryKind);
- return Sections;
+ assert(fileKind == ObjKind || fileKind == BinaryKind);
+ return sections;
}
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
- std::vector<Symbol *> &getMutableSymbols() {
- assert(FileKind == BinaryKind || FileKind == ObjKind ||
- FileKind == BitcodeKind);
- return Symbols;
+ MutableArrayRef<Symbol *> getMutableSymbols() {
+ assert(fileKind == BinaryKind || fileKind == ObjKind ||
+ fileKind == BitcodeKind);
+ return symbols;
}
// Filename of .a which contained this file. If this file was
// not in an archive file, it is the empty string. We use this
// string for creating error messages.
- std::string ArchiveName;
+ std::string archiveName;
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
- ELFKind EKind = ELFNoneKind;
- uint16_t EMachine = llvm::ELF::EM_NONE;
- uint8_t OSABI = 0;
+ ELFKind ekind = ELFNoneKind;
+ uint16_t emachine = llvm::ELF::EM_NONE;
+ uint8_t osabi = 0;
+ uint8_t abiVersion = 0;
// Cache for toString(). Only toString() should use this member.
- mutable std::string ToStringCache;
+ mutable std::string toStringCache;
- std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
- uint64_t Offset);
+ std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec,
+ uint64_t offset);
// True if this is an argument for --just-symbols. Usually false.
- bool JustSymbols = false;
-
- // GroupId is used for --warn-backrefs which is an optional error
+ bool justSymbols = false;
+
+ // outSecOff of .got2 in the current file. This is used by PPC32 -fPIC/-fPIE
+ // to compute offsets in PLT call stubs.
+ uint32_t ppc32Got2OutSecOff = 0;
+
+ // On PPC64 we need to keep track of which files contain small code model
+ // relocations that access the .toc section. To minimize the chance of a
+ // relocation overflow, files that do contain said relocations should have
+ // their .toc sections sorted closer to the .got section than files that do
+ // not contain any small code model relocations. Thats because the toc-pointer
+ // is defined to point at .got + 0x8000 and the instructions used with small
+ // code model relocations support immediates in the range [-0x8000, 0x7FFC],
+ // making the addressable range relative to the toc pointer
+ // [.got, .got + 0xFFFC].
+ bool ppc64SmallCodeModelTocRelocs = false;
+
+ // groupId is used for --warn-backrefs which is an optional error
// checking feature. All files within the same --{start,end}-group or
// --{start,end}-lib get the same group ID. Otherwise, each file gets a new
// group ID. For more info, see checkDependency() in SymbolTable.cpp.
- uint32_t GroupId;
- static bool IsInGroup;
- static uint32_t NextGroupId;
+ uint32_t groupId;
+ static bool isInGroup;
+ static uint32_t nextGroupId;
// Index of MIPS GOT built for this file.
- llvm::Optional<size_t> MipsGotIndex;
+ llvm::Optional<size_t> mipsGotIndex;
+
+ std::vector<Symbol *> symbols;
protected:
- InputFile(Kind K, MemoryBufferRef M);
- std::vector<InputSectionBase *> Sections;
- std::vector<Symbol *> Symbols;
+ InputFile(Kind k, MemoryBufferRef m);
+ std::vector<InputSectionBase *> sections;
private:
- const Kind FileKind;
+ const Kind fileKind;
};
-template <typename ELFT> class ELFFileBase : public InputFile {
+class ELFFileBase : public InputFile {
public:
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::Word Elf_Word;
- typedef typename ELFT::SymRange Elf_Sym_Range;
+ ELFFileBase(Kind k, MemoryBufferRef m);
+ static bool classof(const InputFile *f) { return f->isElf(); }
- ELFFileBase(Kind K, MemoryBufferRef M);
- static bool classof(const InputFile *F) { return F->isElf(); }
-
- llvm::object::ELFFile<ELFT> getObj() const {
- return check(llvm::object::ELFFile<ELFT>::create(MB.getBuffer()));
+ template <typename ELFT> llvm::object::ELFFile<ELFT> getObj() const {
+ return check(llvm::object::ELFFile<ELFT>::create(mb.getBuffer()));
}
- StringRef getStringTable() const { return StringTable; }
-
- uint32_t getSectionIndex(const Elf_Sym &Sym) const;
+ StringRef getStringTable() const { return stringTable; }
- Elf_Sym_Range getGlobalELFSyms();
- Elf_Sym_Range getELFSyms() const { return ELFSyms; }
+ template <typename ELFT> typename ELFT::SymRange getELFSyms() const {
+ return typename ELFT::SymRange(
+ reinterpret_cast<const typename ELFT::Sym *>(elfSyms), numELFSyms);
+ }
+ template <typename ELFT> typename ELFT::SymRange getGlobalELFSyms() const {
+ return getELFSyms<ELFT>().slice(firstGlobal);
+ }
protected:
- ArrayRef<Elf_Sym> ELFSyms;
- uint32_t FirstGlobal = 0;
- ArrayRef<Elf_Word> SymtabSHNDX;
- StringRef StringTable;
- void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
+ // Initializes this class's member variables.
+ template <typename ELFT> void init();
+
+ const void *elfSyms = nullptr;
+ size_t numELFSyms = 0;
+ uint32_t firstGlobal = 0;
+ StringRef stringTable;
};
// .o file.
-template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
- typedef ELFFileBase<ELFT> Base;
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Word Elf_Word;
- typedef typename ELFT::CGProfile Elf_CGProfile;
-
- StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr &Sec);
- ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
+template <class ELFT> class ObjFile : public ELFFileBase {
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
+ using Elf_Sym = typename ELFT::Sym;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Word = typename ELFT::Word;
+ using Elf_CGProfile = typename ELFT::CGProfile;
public:
- static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
+ static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
+
+ llvm::object::ELFFile<ELFT> getObj() const {
+ return this->ELFFileBase::getObj<ELFT>();
+ }
ArrayRef<Symbol *> getLocalSymbols();
ArrayRef<Symbol *> getGlobalSymbols();
- ObjFile(MemoryBufferRef M, StringRef ArchiveName);
- void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
+ ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) {
+ this->archiveName = archiveName;
+ }
+
+ void parse(bool ignoreComdats = false);
- Symbol &getSymbol(uint32_t SymbolIndex) const {
- if (SymbolIndex >= this->Symbols.size())
+ StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
+ const Elf_Shdr &sec);
+
+ Symbol &getSymbol(uint32_t symbolIndex) const {
+ if (symbolIndex >= this->symbols.size())
fatal(toString(this) + ": invalid symbol index");
- return *this->Symbols[SymbolIndex];
+ return *this->symbols[symbolIndex];
}
- template <typename RelT> Symbol &getRelocTargetSym(const RelT &Rel) const {
- uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
- return getSymbol(SymIndex);
+ uint32_t getSectionIndex(const Elf_Sym &sym) const;
+
+ template <typename RelT> Symbol &getRelocTargetSym(const RelT &rel) const {
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
+ return getSymbol(symIndex);
}
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
- llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
+ llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef name);
// MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
- uint32_t MipsGp0 = 0;
+ uint32_t mipsGp0 = 0;
+
+ uint32_t andFeatures = 0;
// Name of source file obtained from STT_FILE symbol value,
// or empty string if there is no such symbol in object file
// symbol table.
- StringRef SourceFile;
+ StringRef sourceFile;
// True if the file defines functions compiled with
// -fsplit-stack. Usually false.
- bool SplitStack = false;
+ bool splitStack = false;
// True if the file defines functions compiled with -fsplit-stack,
// but had one or more functions with the no_split_stack attribute.
- bool SomeNoSplitStack = false;
+ bool someNoSplitStack = false;
// Pointer to this input file's .llvm_addrsig section, if it has one.
- const Elf_Shdr *AddrsigSec = nullptr;
+ const Elf_Shdr *addrsigSec = nullptr;
// SHT_LLVM_CALL_GRAPH_PROFILE table
- ArrayRef<Elf_CGProfile> CGProfile;
+ ArrayRef<Elf_CGProfile> cgProfile;
private:
- void
- initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
+ 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);
-
- bool shouldMerge(const Elf_Shdr &Sec);
- Symbol *createSymbol(const Elf_Sym *Sym);
+ InputSectionBase *getRelocTarget(const Elf_Shdr &sec);
+ InputSectionBase *createInputSection(const Elf_Shdr &sec);
+ StringRef getSectionName(const Elf_Shdr &sec);
+
+ bool shouldMerge(const Elf_Shdr &sec);
+
+ // Each ELF symbol contains a section index which the symbol belongs to.
+ // However, because the number of bits dedicated for that is limited, a
+ // symbol can directly point to a section only when the section index is
+ // equal to or smaller than 65280.
+ //
+ // If an object file contains more than 65280 sections, the file must
+ // contain .symtab_shndx section. The section contains an array of
+ // 32-bit integers whose size is the same as the number of symbols.
+ // Nth symbol's section index is in the Nth entry of .symtab_shndx.
+ //
+ // The following variable contains the contents of .symtab_shndx.
+ // If the section does not exist (which is common), the array is empty.
+ ArrayRef<Elf_Word> shndxTable;
// .shstrtab contents.
- StringRef SectionStringTable;
+ StringRef sectionStringTable;
// Debugging information to retrieve source file and line for error
// 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.
- std::unique_ptr<llvm::DWARFContext> Dwarf;
- std::vector<const llvm::DWARFDebugLine::LineTable *> LineTables;
+ std::unique_ptr<llvm::DWARFContext> dwarf;
+ std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
struct VarLoc {
- const llvm::DWARFDebugLine::LineTable *LT;
- unsigned File;
- unsigned Line;
+ const llvm::DWARFDebugLine::LineTable *lt;
+ unsigned file;
+ unsigned line;
};
- llvm::DenseMap<StringRef, VarLoc> VariableLoc;
- llvm::once_flag InitDwarfLine;
+ llvm::DenseMap<StringRef, VarLoc> variableLoc;
+ llvm::once_flag initDwarfLine;
};
// LazyObjFile is analogous to ArchiveFile in the sense that
@@ -264,118 +304,100 @@ private:
// archive file semantics.
class LazyObjFile : public InputFile {
public:
- LazyObjFile(MemoryBufferRef M, StringRef ArchiveName,
- uint64_t OffsetInArchive)
- : InputFile(LazyObjKind, M), OffsetInArchive(OffsetInArchive) {
- this->ArchiveName = ArchiveName;
+ LazyObjFile(MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive)
+ : InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) {
+ this->archiveName = archiveName;
}
- static bool classof(const InputFile *F) { return F->kind() == LazyObjKind; }
+ static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; }
template <class ELFT> void parse();
- MemoryBufferRef getBuffer();
- InputFile *fetch();
- bool AddedToLink = false;
+ void fetch();
private:
- uint64_t OffsetInArchive;
+ uint64_t offsetInArchive;
};
// An ArchiveFile object represents a .a file.
class ArchiveFile : public InputFile {
public:
- explicit ArchiveFile(std::unique_ptr<Archive> &&File);
- static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
- template <class ELFT> void parse();
+ explicit ArchiveFile(std::unique_ptr<Archive> &&file);
+ static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
+ void parse();
// Pulls out an object file that contains a definition for Sym and
// returns it. If the same file was instantiated before, this
- // function returns a nullptr (so we don't instantiate the same file
+ // function does nothing (so we don't instantiate the same file
// more than once.)
- InputFile *fetch(const Archive::Symbol &Sym);
+ void fetch(const Archive::Symbol &sym);
private:
- std::unique_ptr<Archive> File;
- llvm::DenseSet<uint64_t> Seen;
+ std::unique_ptr<Archive> file;
+ llvm::DenseSet<uint64_t> seen;
};
class BitcodeFile : public InputFile {
public:
- BitcodeFile(MemoryBufferRef M, StringRef ArchiveName,
- uint64_t OffsetInArchive);
- static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- template <class ELFT>
- void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
- std::unique_ptr<llvm::lto::InputFile> Obj;
+ BitcodeFile(MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive);
+ static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
+ template <class ELFT> void parse();
+ std::unique_ptr<llvm::lto::InputFile> obj;
};
// .so file.
-template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
- typedef ELFFileBase<ELFT> Base;
- typedef typename ELFT::Dyn Elf_Dyn;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::SymRange Elf_Sym_Range;
- typedef typename ELFT::Verdef Elf_Verdef;
- typedef typename ELFT::Versym Elf_Versym;
-
- const Elf_Shdr *VersymSec = nullptr;
- const Elf_Shdr *VerdefSec = nullptr;
-
+class SharedFile : public ELFFileBase {
public:
- std::vector<const Elf_Verdef *> Verdefs;
- std::string SoName;
+ SharedFile(MemoryBufferRef m, StringRef defaultSoName)
+ : ELFFileBase(SharedKind, m), soName(defaultSoName),
+ isNeeded(!config->asNeeded) {}
- static bool classof(const InputFile *F) {
- return F->kind() == Base::SharedKind;
- }
+ // This is actually a vector of Elf_Verdef pointers.
+ std::vector<const void *> verdefs;
- SharedFile(MemoryBufferRef M, StringRef DefaultSoName);
+ // If the output file needs Elf_Verneed data structures for this file, this is
+ // a vector of Elf_Vernaux version identifiers that map onto the entries in
+ // Verdefs, otherwise it is empty.
+ std::vector<unsigned> vernauxs;
- void parseSoName();
- void parseRest();
- uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym);
- std::vector<const Elf_Verdef *> parseVerdefs();
- std::vector<uint32_t> parseVersyms();
+ static unsigned vernauxNum;
- struct NeededVer {
- // The string table offset of the version name in the output file.
- size_t StrTab;
+ std::vector<StringRef> dtNeeded;
+ std::string soName;
- // The version identifier for this version name.
- uint16_t Index;
- };
+ static bool classof(const InputFile *f) { return f->kind() == SharedKind; }
+
+ template <typename ELFT> void parse();
- // Mapping from Elf_Verdef data structures to information about Elf_Vernaux
- // data structures in the output file.
- std::map<const Elf_Verdef *, NeededVer> VerdefMap;
+ // Used for --no-allow-shlib-undefined.
+ bool allNeededIsKnown;
// Used for --as-needed
- bool IsNeeded;
+ bool isNeeded;
};
class BinaryFile : public InputFile {
public:
- explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
- static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
+ explicit BinaryFile(MemoryBufferRef m) : InputFile(BinaryKind, m) {}
+ static bool classof(const InputFile *f) { return f->kind() == BinaryKind; }
void parse();
};
-InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
- uint64_t OffsetInArchive = 0);
-InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
+InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "",
+ uint64_t offsetInArchive = 0);
-inline bool isBitcode(MemoryBufferRef MB) {
- return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode;
+inline bool isBitcode(MemoryBufferRef mb) {
+ return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
}
-std::string replaceThinLTOSuffix(StringRef Path);
+std::string replaceThinLTOSuffix(StringRef path);
-extern std::vector<BinaryFile *> BinaryFiles;
-extern std::vector<BitcodeFile *> BitcodeFiles;
-extern std::vector<LazyObjFile *> LazyObjFiles;
-extern std::vector<InputFile *> ObjectFiles;
-extern std::vector<InputFile *> SharedFiles;
+extern std::vector<BinaryFile *> binaryFiles;
+extern std::vector<BitcodeFile *> bitcodeFiles;
+extern std::vector<LazyObjFile *> lazyObjFiles;
+extern std::vector<InputFile *> objectFiles;
+extern std::vector<SharedFile *> sharedFiles;
} // namespace elf
} // namespace lld
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 839bff7011eb..a024ac307b0a 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -1,9 +1,8 @@
//===- InputSection.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -41,52 +40,52 @@ using namespace llvm::sys;
using namespace lld;
using namespace lld::elf;
-std::vector<InputSectionBase *> elf::InputSections;
+std::vector<InputSectionBase *> elf::inputSections;
// Returns a string to construct an error message.
-std::string lld::toString(const InputSectionBase *Sec) {
- return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
+std::string lld::toString(const InputSectionBase *sec) {
+ return (toString(sec->file) + ":(" + sec->name + ")").str();
}
template <class ELFT>
-static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &File,
- const typename ELFT::Shdr &Hdr) {
- if (Hdr.sh_type == SHT_NOBITS)
- return makeArrayRef<uint8_t>(nullptr, Hdr.sh_size);
- return check(File.getObj().getSectionContents(&Hdr));
+static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
+ const typename ELFT::Shdr &hdr) {
+ if (hdr.sh_type == SHT_NOBITS)
+ return makeArrayRef<uint8_t>(nullptr, hdr.sh_size);
+ return check(file.getObj().getSectionContents(&hdr));
}
-InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
- uint32_t Type, uint64_t Entsize,
- uint32_t Link, uint32_t Info,
- uint32_t Alignment, ArrayRef<uint8_t> Data,
- StringRef Name, Kind SectionKind)
- : SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
- Link),
- File(File), RawData(Data) {
+InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags,
+ uint32_t type, uint64_t entsize,
+ uint32_t link, uint32_t info,
+ uint32_t alignment, ArrayRef<uint8_t> data,
+ StringRef name, Kind sectionKind)
+ : SectionBase(sectionKind, name, flags, entsize, alignment, type, info,
+ link),
+ file(file), rawData(data) {
// In order to reduce memory allocation, we assume that mergeable
// sections are smaller than 4 GiB, which is not an unreasonable
// assumption as of 2017.
- if (SectionKind == SectionBase::Merge && RawData.size() > UINT32_MAX)
+ if (sectionKind == SectionBase::Merge && rawData.size() > UINT32_MAX)
error(toString(this) + ": section too large");
- NumRelocations = 0;
- AreRelocsRela = false;
+ numRelocations = 0;
+ areRelocsRela = false;
// The ELF spec states that a value of 0 means the section has
// no alignment constraits.
- uint32_t V = std::max<uint64_t>(Alignment, 1);
- if (!isPowerOf2_64(V))
- fatal(toString(File) + ": section sh_addralign is not a power of 2");
- this->Alignment = V;
+ uint32_t v = std::max<uint32_t>(alignment, 1);
+ if (!isPowerOf2_64(v))
+ fatal(toString(this) + ": sh_addralign is not a power of 2");
+ this->alignment = v;
// In ELF, each section can be compressed by zlib, and if compressed,
// section name may be mangled by appending "z" (e.g. ".zdebug_info").
// If that's the case, demangle section name so that we can handle a
// section as if it weren't compressed.
- if ((Flags & SHF_COMPRESSED) || Name.startswith(".zdebug")) {
+ if ((flags & SHF_COMPRESSED) || name.startswith(".zdebug")) {
if (!zlib::isAvailable())
- error(toString(File) + ": contains a compressed section, " +
+ error(toString(file) + ": contains a compressed section, " +
"but zlib is not available");
parseCompressedHeader();
}
@@ -95,11 +94,11 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
// Drop SHF_GROUP bit unless we are producing a re-linkable object file.
// SHF_GROUP is a marker that a section belongs to some comdat group.
// That flag doesn't make sense in an executable.
-static uint64_t getFlags(uint64_t Flags) {
- Flags &= ~(uint64_t)SHF_INFO_LINK;
- if (!Config->Relocatable)
- Flags &= ~(uint64_t)SHF_GROUP;
- return Flags;
+static uint64_t getFlags(uint64_t flags) {
+ flags &= ~(uint64_t)SHF_INFO_LINK;
+ if (!config->relocatable)
+ flags &= ~(uint64_t)SHF_GROUP;
+ return flags;
}
// GNU assembler 2.24 and LLVM 4.0.0's MC (the newest release as of
@@ -112,205 +111,212 @@ static uint64_t getFlags(uint64_t Flags) {
//
// This function forces SHT_{INIT,FINI}_ARRAY so that we can handle
// incorrect inputs as if they were correct from the beginning.
-static uint64_t getType(uint64_t Type, StringRef Name) {
- if (Type == SHT_PROGBITS && Name.startswith(".init_array."))
+static uint64_t getType(uint64_t type, StringRef name) {
+ if (type == SHT_PROGBITS && name.startswith(".init_array."))
return SHT_INIT_ARRAY;
- if (Type == SHT_PROGBITS && Name.startswith(".fini_array."))
+ if (type == SHT_PROGBITS && name.startswith(".fini_array."))
return SHT_FINI_ARRAY;
- return Type;
+ return type;
}
template <class ELFT>
-InputSectionBase::InputSectionBase(ObjFile<ELFT> &File,
- const typename ELFT::Shdr &Hdr,
- StringRef Name, Kind SectionKind)
- : InputSectionBase(&File, getFlags(Hdr.sh_flags),
- getType(Hdr.sh_type, Name), Hdr.sh_entsize, Hdr.sh_link,
- Hdr.sh_info, Hdr.sh_addralign,
- getSectionContents(File, Hdr), Name, SectionKind) {
+InputSectionBase::InputSectionBase(ObjFile<ELFT> &file,
+ const typename ELFT::Shdr &hdr,
+ StringRef name, Kind sectionKind)
+ : InputSectionBase(&file, getFlags(hdr.sh_flags),
+ getType(hdr.sh_type, name), hdr.sh_entsize, hdr.sh_link,
+ hdr.sh_info, hdr.sh_addralign,
+ getSectionContents(file, hdr), name, sectionKind) {
// We reject object files having insanely large alignments even though
// they are allowed by the spec. I think 4GB is a reasonable limitation.
// We might want to relax this in the future.
- if (Hdr.sh_addralign > UINT32_MAX)
- fatal(toString(&File) + ": section sh_addralign is too large");
+ if (hdr.sh_addralign > UINT32_MAX)
+ fatal(toString(&file) + ": section sh_addralign is too large");
}
size_t InputSectionBase::getSize() const {
- if (auto *S = dyn_cast<SyntheticSection>(this))
- return S->getSize();
- if (UncompressedSize >= 0)
- return UncompressedSize;
- return RawData.size();
+ if (auto *s = dyn_cast<SyntheticSection>(this))
+ return s->getSize();
+ if (uncompressedSize >= 0)
+ return uncompressedSize;
+ return rawData.size();
}
void InputSectionBase::uncompress() const {
- size_t Size = UncompressedSize;
- UncompressedBuf.reset(new char[Size]);
+ size_t size = uncompressedSize;
+ char *uncompressedBuf;
+ {
+ static std::mutex mu;
+ std::lock_guard<std::mutex> lock(mu);
+ uncompressedBuf = bAlloc.Allocate<char>(size);
+ }
- if (Error E =
- zlib::uncompress(toStringRef(RawData), UncompressedBuf.get(), Size))
+ if (Error e = zlib::uncompress(toStringRef(rawData), uncompressedBuf, size))
fatal(toString(this) +
- ": uncompress failed: " + llvm::toString(std::move(E)));
- RawData = makeArrayRef((uint8_t *)UncompressedBuf.get(), Size);
+ ": uncompress failed: " + llvm::toString(std::move(e)));
+ rawData = makeArrayRef((uint8_t *)uncompressedBuf, size);
+ uncompressedSize = -1;
}
uint64_t InputSectionBase::getOffsetInFile() const {
- const uint8_t *FileStart = (const uint8_t *)File->MB.getBufferStart();
- const uint8_t *SecStart = data().begin();
- return SecStart - FileStart;
+ const uint8_t *fileStart = (const uint8_t *)file->mb.getBufferStart();
+ const uint8_t *secStart = data().begin();
+ return secStart - fileStart;
}
-uint64_t SectionBase::getOffset(uint64_t Offset) const {
+uint64_t SectionBase::getOffset(uint64_t offset) const {
switch (kind()) {
case Output: {
- auto *OS = cast<OutputSection>(this);
+ auto *os = cast<OutputSection>(this);
// For output sections we treat offset -1 as the end of the section.
- return Offset == uint64_t(-1) ? OS->Size : Offset;
+ return offset == uint64_t(-1) ? os->size : offset;
}
case Regular:
case Synthetic:
- return cast<InputSection>(this)->getOffset(Offset);
+ return cast<InputSection>(this)->getOffset(offset);
case EHFrame:
// The file crtbeginT.o has relocations pointing to the start of an empty
// .eh_frame that is known to be the first in the link. It does that to
// identify the start of the output .eh_frame.
- return Offset;
+ return offset;
case Merge:
- const MergeInputSection *MS = cast<MergeInputSection>(this);
- if (InputSection *IS = MS->getParent())
- return IS->getOffset(MS->getParentOffset(Offset));
- return MS->getParentOffset(Offset);
+ const MergeInputSection *ms = cast<MergeInputSection>(this);
+ if (InputSection *isec = ms->getParent())
+ return isec->getOffset(ms->getParentOffset(offset));
+ return ms->getParentOffset(offset);
}
llvm_unreachable("invalid section kind");
}
-uint64_t SectionBase::getVA(uint64_t Offset) const {
- const OutputSection *Out = getOutputSection();
- return (Out ? Out->Addr : 0) + getOffset(Offset);
+uint64_t SectionBase::getVA(uint64_t offset) const {
+ const OutputSection *out = getOutputSection();
+ return (out ? out->addr : 0) + getOffset(offset);
}
OutputSection *SectionBase::getOutputSection() {
- InputSection *Sec;
- if (auto *IS = dyn_cast<InputSection>(this))
- Sec = IS;
- else if (auto *MS = dyn_cast<MergeInputSection>(this))
- Sec = MS->getParent();
- else if (auto *EH = dyn_cast<EhInputSection>(this))
- Sec = EH->getParent();
+ InputSection *sec;
+ if (auto *isec = dyn_cast<InputSection>(this))
+ sec = isec;
+ else if (auto *ms = dyn_cast<MergeInputSection>(this))
+ sec = ms->getParent();
+ else if (auto *eh = dyn_cast<EhInputSection>(this))
+ sec = eh->getParent();
else
return cast<OutputSection>(this);
- return Sec ? Sec->getParent() : nullptr;
+ return sec ? sec->getParent() : nullptr;
}
-// When a section is compressed, `RawData` consists with a header followed
+// When a section is compressed, `rawData` consists with a header followed
// by zlib-compressed data. This function parses a header to initialize
-// `UncompressedSize` member and remove the header from `RawData`.
+// `uncompressedSize` member and remove the header from `rawData`.
void InputSectionBase::parseCompressedHeader() {
- typedef typename ELF64LE::Chdr Chdr64;
- typedef typename ELF32LE::Chdr Chdr32;
+ using Chdr64 = typename ELF64LE::Chdr;
+ using Chdr32 = typename ELF32LE::Chdr;
// Old-style header
- if (Name.startswith(".zdebug")) {
- if (!toStringRef(RawData).startswith("ZLIB")) {
+ if (name.startswith(".zdebug")) {
+ if (!toStringRef(rawData).startswith("ZLIB")) {
error(toString(this) + ": corrupted compressed section header");
return;
}
- RawData = RawData.slice(4);
+ rawData = rawData.slice(4);
- if (RawData.size() < 8) {
+ if (rawData.size() < 8) {
error(toString(this) + ": corrupted compressed section header");
return;
}
- UncompressedSize = read64be(RawData.data());
- RawData = RawData.slice(8);
+ uncompressedSize = read64be(rawData.data());
+ rawData = rawData.slice(8);
// Restore the original section name.
// (e.g. ".zdebug_info" -> ".debug_info")
- Name = Saver.save("." + Name.substr(2));
+ name = saver.save("." + name.substr(2));
return;
}
- assert(Flags & SHF_COMPRESSED);
- Flags &= ~(uint64_t)SHF_COMPRESSED;
+ assert(flags & SHF_COMPRESSED);
+ flags &= ~(uint64_t)SHF_COMPRESSED;
// New-style 64-bit header
- if (Config->Is64) {
- if (RawData.size() < sizeof(Chdr64)) {
+ if (config->is64) {
+ if (rawData.size() < sizeof(Chdr64)) {
error(toString(this) + ": corrupted compressed section");
return;
}
- auto *Hdr = reinterpret_cast<const Chdr64 *>(RawData.data());
- if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
+ auto *hdr = reinterpret_cast<const Chdr64 *>(rawData.data());
+ if (hdr->ch_type != ELFCOMPRESS_ZLIB) {
error(toString(this) + ": unsupported compression type");
return;
}
- UncompressedSize = Hdr->ch_size;
- RawData = RawData.slice(sizeof(*Hdr));
+ uncompressedSize = hdr->ch_size;
+ alignment = std::max<uint32_t>(hdr->ch_addralign, 1);
+ rawData = rawData.slice(sizeof(*hdr));
return;
}
// New-style 32-bit header
- if (RawData.size() < sizeof(Chdr32)) {
+ if (rawData.size() < sizeof(Chdr32)) {
error(toString(this) + ": corrupted compressed section");
return;
}
- auto *Hdr = reinterpret_cast<const Chdr32 *>(RawData.data());
- if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
+ auto *hdr = reinterpret_cast<const Chdr32 *>(rawData.data());
+ if (hdr->ch_type != ELFCOMPRESS_ZLIB) {
error(toString(this) + ": unsupported compression type");
return;
}
- UncompressedSize = Hdr->ch_size;
- RawData = RawData.slice(sizeof(*Hdr));
+ uncompressedSize = hdr->ch_size;
+ alignment = std::max<uint32_t>(hdr->ch_addralign, 1);
+ rawData = rawData.slice(sizeof(*hdr));
}
InputSection *InputSectionBase::getLinkOrderDep() const {
- assert(Link);
- assert(Flags & SHF_LINK_ORDER);
- return cast<InputSection>(File->getSections()[Link]);
+ assert(link);
+ assert(flags & SHF_LINK_ORDER);
+ return cast<InputSection>(file->getSections()[link]);
}
// Find a function symbol that encloses a given location.
template <class ELFT>
-Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) {
- for (Symbol *B : File->getSymbols())
- if (Defined *D = dyn_cast<Defined>(B))
- if (D->Section == this && D->Type == STT_FUNC && D->Value <= Offset &&
- Offset < D->Value + D->Size)
- return D;
+Defined *InputSectionBase::getEnclosingFunction(uint64_t offset) {
+ for (Symbol *b : file->getSymbols())
+ if (Defined *d = dyn_cast<Defined>(b))
+ if (d->section == this && d->type == STT_FUNC && d->value <= offset &&
+ offset < d->value + d->size)
+ return d;
return nullptr;
}
// Returns a source location string. Used to construct an error message.
template <class ELFT>
-std::string InputSectionBase::getLocation(uint64_t Offset) {
- std::string SecAndOffset = (Name + "+0x" + utohexstr(Offset)).str();
+std::string InputSectionBase::getLocation(uint64_t offset) {
+ std::string secAndOffset = (name + "+0x" + utohexstr(offset)).str();
// We don't have file for synthetic sections.
if (getFile<ELFT>() == nullptr)
- return (Config->OutputFile + ":(" + SecAndOffset + ")")
+ return (config->outputFile + ":(" + secAndOffset + ")")
.str();
// First check if we can get desired values from debugging information.
- if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset))
- return Info->FileName + ":" + std::to_string(Info->Line) + ":(" +
- SecAndOffset + ")";
+ if (Optional<DILineInfo> info = getFile<ELFT>()->getDILineInfo(this, offset))
+ return info->FileName + ":" + std::to_string(info->Line) + ":(" +
+ secAndOffset + ")";
- // File->SourceFile contains STT_FILE symbol that contains a
+ // 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;
- if (SrcFile.empty())
- SrcFile = toString(File);
+ std::string srcFile = getFile<ELFT>()->sourceFile;
+ if (srcFile.empty())
+ srcFile = toString(file);
- if (Defined *D = getEnclosingFunction<ELFT>(Offset))
- return SrcFile + ":(function " + toString(*D) + ": " + SecAndOffset + ")";
+ if (Defined *d = getEnclosingFunction<ELFT>(offset))
+ return srcFile + ":(function " + toString(*d) + ": " + secAndOffset + ")";
// If there's no symbol, print out the offset in the section.
- return (SrcFile + ":(" + SecAndOffset + ")");
+ return (srcFile + ":(" + secAndOffset + ")");
}
// This function is intended to be used for constructing an error message.
@@ -319,8 +325,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
// foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42)
//
// Returns an empty string if there's no way to get line info.
-std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
- return File->getSrcMsg(Sym, *this, Offset);
+std::string InputSectionBase::getSrcMsg(const Symbol &sym, uint64_t offset) {
+ return file->getSrcMsg(sym, *this, offset);
}
// Returns a filename string along with an optional section name. This
@@ -332,95 +338,96 @@ std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
// or
//
// 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 InputSectionBase::getObjMsg(uint64_t off) {
+ std::string filename = file->getName();
- std::string Archive;
- if (!File->ArchiveName.empty())
- Archive = " in archive " + File->ArchiveName;
+ std::string archive;
+ if (!file->archiveName.empty())
+ archive = " in archive " + file->archiveName;
// Find a symbol that encloses a given location.
- for (Symbol *B : File->getSymbols())
- if (auto *D = dyn_cast<Defined>(B))
- if (D->Section == this && D->Value <= Off && Off < D->Value + D->Size)
- return Filename + ":(" + toString(*D) + ")" + Archive;
+ for (Symbol *b : file->getSymbols())
+ if (auto *d = dyn_cast<Defined>(b))
+ if (d->section == this && d->value <= off && off < d->value + d->size)
+ return filename + ":(" + toString(*d) + ")" + archive;
// If there's no symbol, print out the offset in the section.
- return (Filename + ":(" + Name + "+0x" + utohexstr(Off) + ")" + Archive)
+ return (filename + ":(" + name + "+0x" + utohexstr(off) + ")" + archive)
.str();
}
-InputSection InputSection::Discarded(nullptr, 0, 0, 0, ArrayRef<uint8_t>(), "");
+InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef<uint8_t>(), "");
-InputSection::InputSection(InputFile *F, uint64_t Flags, uint32_t Type,
- uint32_t Alignment, ArrayRef<uint8_t> Data,
- StringRef Name, Kind K)
- : InputSectionBase(F, Flags, Type,
- /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, Alignment, Data,
- Name, K) {}
+InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type,
+ uint32_t alignment, ArrayRef<uint8_t> data,
+ StringRef name, Kind k)
+ : InputSectionBase(f, flags, type,
+ /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, alignment, data,
+ name, k) {}
template <class ELFT>
-InputSection::InputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name)
- : InputSectionBase(F, Header, Name, InputSectionBase::Regular) {}
+InputSection::InputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name)
+ : InputSectionBase(f, header, name, InputSectionBase::Regular) {}
-bool InputSection::classof(const SectionBase *S) {
- return S->kind() == SectionBase::Regular ||
- S->kind() == SectionBase::Synthetic;
+bool InputSection::classof(const SectionBase *s) {
+ return s->kind() == SectionBase::Regular ||
+ s->kind() == SectionBase::Synthetic;
}
OutputSection *InputSection::getParent() const {
- return cast_or_null<OutputSection>(Parent);
+ return cast_or_null<OutputSection>(parent);
}
// Copy SHT_GROUP section contents. Used only for the -r option.
-template <class ELFT> void InputSection::copyShtGroup(uint8_t *Buf) {
+template <class ELFT> void InputSection::copyShtGroup(uint8_t *buf) {
// ELFT::Word is the 32-bit integral type in the target endianness.
- typedef typename ELFT::Word u32;
- ArrayRef<u32> From = getDataAs<u32>();
- auto *To = reinterpret_cast<u32 *>(Buf);
+ using u32 = typename ELFT::Word;
+ ArrayRef<u32> from = getDataAs<u32>();
+ auto *to = reinterpret_cast<u32 *>(buf);
// The first entry is not a section number but a flag.
- *To++ = From[0];
+ *to++ = from[0];
// Adjust section numbers because section numbers in an input object
// files are different in the output.
- ArrayRef<InputSectionBase *> Sections = File->getSections();
- for (uint32_t Idx : From.slice(1))
- *To++ = Sections[Idx]->getOutputSection()->SectionIndex;
+ ArrayRef<InputSectionBase *> sections = file->getSections();
+ for (uint32_t idx : from.slice(1))
+ *to++ = sections[idx]->getOutputSection()->sectionIndex;
}
InputSectionBase *InputSection::getRelocatedSection() const {
- if (!File || (Type != SHT_RELA && Type != SHT_REL))
+ if (!file || (type != SHT_RELA && type != SHT_REL))
return nullptr;
- ArrayRef<InputSectionBase *> Sections = File->getSections();
- return Sections[Info];
+ ArrayRef<InputSectionBase *> sections = file->getSections();
+ return sections[info];
}
// This is used for -r and --emit-relocs. We can't use memcpy to copy
// relocations because we need to update symbol table offset and section index
// for each relocation. So we copy relocations one by one.
template <class ELFT, class RelTy>
-void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
- InputSectionBase *Sec = getRelocatedSection();
+void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
+ InputSectionBase *sec = getRelocatedSection();
- for (const RelTy &Rel : Rels) {
- RelType Type = Rel.getType(Config->IsMips64EL);
- Symbol &Sym = getFile<ELFT>()->getRelocTargetSym(Rel);
+ for (const RelTy &rel : rels) {
+ RelType type = rel.getType(config->isMips64EL);
+ const ObjFile<ELFT> *file = getFile<ELFT>();
+ Symbol &sym = file->getRelocTargetSym(rel);
- auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
- Buf += sizeof(RelTy);
+ auto *p = reinterpret_cast<typename ELFT::Rela *>(buf);
+ buf += sizeof(RelTy);
if (RelTy::IsRela)
- P->r_addend = getAddend<ELFT>(Rel);
+ p->r_addend = getAddend<ELFT>(rel);
// Output section VA is zero for -r, so r_offset is an offset within the
// section, but for --emit-relocs it is an virtual address.
- P->r_offset = Sec->getVA(Rel.r_offset);
- P->setSymbolAndType(In.SymTab->getSymbolIndex(&Sym), Type,
- Config->IsMips64EL);
+ p->r_offset = sec->getVA(rel.r_offset);
+ p->setSymbolAndType(in.symTab->getSymbolIndex(&sym), type,
+ config->isMips64EL);
- if (Sym.Type == STT_SECTION) {
+ if (sym.type == STT_SECTION) {
// We combine multiple section symbols into only one per
// section. This means we have to update the addend. That is
// trivial for Elf_Rela, but for Elf_Rel we have to write to the
@@ -429,25 +436,38 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// .eh_frame is horribly special and can reference discarded sections. To
// avoid having to parse and recreate .eh_frame, we just replace any
// relocation in it pointing to discarded sections with R_*_NONE, which
- // hopefully creates a frame that is ignored at runtime.
- auto *D = dyn_cast<Defined>(&Sym);
- if (!D) {
- error("STT_SECTION symbol should be defined");
+ // hopefully creates a frame that is ignored at runtime. Also, don't warn
+ // on .gcc_except_table and debug sections.
+ //
+ // See the comment in maybeReportUndefined for PPC64 .toc .
+ auto *d = dyn_cast<Defined>(&sym);
+ if (!d) {
+ if (!sec->name.startswith(".debug") &&
+ !sec->name.startswith(".zdebug") && sec->name != ".eh_frame" &&
+ sec->name != ".gcc_except_table" && sec->name != ".toc") {
+ uint32_t secIdx = cast<Undefined>(sym).discardedSecIdx;
+ Elf_Shdr_Impl<ELFT> sec =
+ CHECK(file->getObj().sections(), file)[secIdx];
+ warn("relocation refers to a discarded section: " +
+ CHECK(file->getObj().getSectionName(&sec), file) +
+ "\n>>> referenced by " + getObjMsg(p->r_offset));
+ }
+ p->setSymbolAndType(0, 0, false);
continue;
}
- SectionBase *Section = D->Section->Repl;
- if (!Section->Live) {
- P->setSymbolAndType(0, 0, false);
+ SectionBase *section = d->section->repl;
+ if (!section->isLive()) {
+ p->setSymbolAndType(0, 0, false);
continue;
}
- int64_t Addend = getAddend<ELFT>(Rel);
- const uint8_t *BufLoc = Sec->data().begin() + Rel.r_offset;
+ int64_t addend = getAddend<ELFT>(rel);
+ const uint8_t *bufLoc = sec->data().begin() + rel.r_offset;
if (!RelTy::IsRela)
- Addend = Target->getImplicitAddend(BufLoc, Type);
+ addend = target->getImplicitAddend(bufLoc, type);
- if (Config->EMachine == EM_MIPS && Config->Relocatable &&
- Target->getRelExpr(Type, Sym, BufLoc) == R_MIPS_GOTREL) {
+ if (config->emachine == EM_MIPS && config->relocatable &&
+ 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
// relocatable files produced by a complier or a linker
@@ -459,13 +479,13 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// individual "gp" values used by each input object file.
// As a workaround we add the "gp" value to the relocation
// addend and save it back to the file.
- Addend += Sec->getFile<ELFT>()->MipsGp0;
+ addend += sec->getFile<ELFT>()->mipsGp0;
}
if (RelTy::IsRela)
- P->r_addend = Sym.getVA(Addend) - Section->getOutputSection()->Addr;
- else if (Config->Relocatable)
- Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset, Addend, &Sym});
+ p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr;
+ else if (config->relocatable && type != target->noneRel)
+ sec->relocations.push_back({R_ABS, type, rel.r_offset, addend, &sym});
}
}
}
@@ -475,13 +495,13 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// this context is the address of the place P. A further special case is that
// branch relocations to an undefined weak reference resolve to the next
// instruction.
-static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
- uint32_t P) {
- switch (Type) {
+static uint32_t getARMUndefinedRelativeWeakVA(RelType type, uint32_t a,
+ uint32_t p) {
+ switch (type) {
// Unresolved branch relocations to weak references resolve to next
// instruction, this will be either 2 or 4 bytes on from P.
case R_ARM_THM_JUMP11:
- return P + 2 + A;
+ return p + 2 + a;
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PC24:
@@ -489,10 +509,10 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
case R_ARM_PREL31:
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
- return P + 4 + A;
+ return p + 4 + a;
case R_ARM_THM_CALL:
// We don't want an interworking BLX to ARM
- return P + 5 + A;
+ return p + 5 + a;
// Unresolved non branch pc-relative relocations
// R_ARM_TARGET2 which can be resolved relatively is not present as it never
// targets a weak-reference.
@@ -501,29 +521,29 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
case R_ARM_REL32:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
- return P + A;
+ return p + a;
}
llvm_unreachable("ARM pc-relative relocation expected\n");
}
// The comment above getARMUndefinedRelativeWeakVA applies to this function.
-static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
- uint64_t P) {
- switch (Type) {
+static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t type, uint64_t a,
+ uint64_t p) {
+ switch (type) {
// Unresolved branch relocations to weak references resolve to next
// instruction, this is 4 bytes on from P.
case R_AARCH64_CALL26:
case R_AARCH64_CONDBR19:
case R_AARCH64_JUMP26:
case R_AARCH64_TSTBR14:
- return P + 4 + A;
+ return p + 4 + a;
// Unresolved non branch pc-relative relocations
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
case R_AARCH64_PREL64:
case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_LD_PREL_LO19:
- return P + A;
+ return p + a;
}
llvm_unreachable("AArch64 pc-relative relocation expected\n");
}
@@ -535,11 +555,11 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
// The procedure call standard only defines a Read Write Position Independent
// RWPI variant so in practice we should expect the static base to be the base
// of the RW segment.
-static uint64_t getARMStaticBase(const Symbol &Sym) {
- OutputSection *OS = Sym.getOutputSection();
- if (!OS || !OS->PtLoad || !OS->PtLoad->FirstSec)
- fatal("SBREL relocation to " + Sym.getName() + " without static base");
- return OS->PtLoad->FirstSec->Addr;
+static uint64_t getARMStaticBase(const Symbol &sym) {
+ OutputSection *os = sym.getOutputSection();
+ if (!os || !os->ptLoad || !os->ptLoad->firstSec)
+ fatal("SBREL relocation to " + sym.getName() + " without static base");
+ return os->ptLoad->firstSec->addr;
}
// For R_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually
@@ -548,101 +568,115 @@ static uint64_t getARMStaticBase(const Symbol &Sym) {
//
// This function returns the R_RISCV_PCREL_HI20 relocation from
// R_RISCV_PCREL_LO12's symbol and addend.
-static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) {
- const Defined *D = cast<Defined>(Sym);
- InputSection *IS = cast<InputSection>(D->Section);
+static Relocation *getRISCVPCRelHi20(const Symbol *sym, uint64_t addend) {
+ const Defined *d = cast<Defined>(sym);
+ if (!d->section) {
+ error("R_RISCV_PCREL_LO12 relocation points to an absolute symbol: " +
+ sym->getName());
+ return nullptr;
+ }
+ InputSection *isec = cast<InputSection>(d->section);
- if (Addend != 0)
+ if (addend != 0)
warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " +
- IS->getObjMsg(D->Value) + " is ignored");
+ isec->getObjMsg(d->value) + " is ignored");
// Relocations are sorted by offset, so we can use std::equal_range to do
// binary search.
- auto Range = std::equal_range(IS->Relocations.begin(), IS->Relocations.end(),
- D->Value, RelocationOffsetComparator{});
- for (auto It = std::get<0>(Range); It != std::get<1>(Range); ++It)
- if (isRelExprOneOf<R_PC>(It->Expr))
- return &*It;
-
- error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) +
+ Relocation r;
+ r.offset = d->value;
+ auto range =
+ std::equal_range(isec->relocations.begin(), isec->relocations.end(), r,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
+
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->type == R_RISCV_PCREL_HI20 || it->type == R_RISCV_GOT_HI20 ||
+ it->type == R_RISCV_TLS_GD_HI20 || it->type == R_RISCV_TLS_GOT_HI20)
+ return &*it;
+
+ error("R_RISCV_PCREL_LO12 relocation points to " + isec->getObjMsg(d->value) +
" without an associated R_RISCV_PCREL_HI20 relocation");
return nullptr;
}
// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
-static int64_t getTlsTpOffset() {
- switch (Config->EMachine) {
+static int64_t getTlsTpOffset(const Symbol &s) {
+ // On targets that support TLSDESC, _TLS_MODULE_BASE_@tpoff = 0.
+ if (&s == ElfSym::tlsModuleBase)
+ return 0;
+
+ switch (config->emachine) {
case EM_ARM:
case EM_AARCH64:
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
// followed by a variable amount of alignment padding, followed by the TLS
// segment.
- //
- // NB: While the ARM/AArch64 ABI formally has a 2-word TCB size, lld
- // effectively increases the TCB size to 8 words for Android compatibility.
- // It accomplishes this by increasing the segment's alignment.
- return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align);
+ return s.getVA(0) + alignTo(config->wordsize * 2, Out::tlsPhdr->p_align);
case EM_386:
case EM_X86_64:
// Variant 2. The TLS segment is located just before the thread pointer.
- return -Out::TlsPhdr->p_memsz;
+ return s.getVA(0) - alignTo(Out::tlsPhdr->p_memsz, Out::tlsPhdr->p_align);
+ case EM_PPC:
case EM_PPC64:
// The thread pointer points to a fixed offset from the start of the
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
// program's TLS segment.
- return -0x7000;
+ return s.getVA(0) - 0x7000;
+ case EM_RISCV:
+ return s.getVA(0);
default:
llvm_unreachable("unhandled Config->EMachine");
}
}
-static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
- uint64_t P, const Symbol &Sym, RelExpr Expr) {
- switch (Expr) {
- case R_INVALID:
- return 0;
+static uint64_t 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:
case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
- return Sym.getVA(A);
+ case R_RISCV_ADD:
+ return sym.getVA(a);
case R_ADDEND:
- return A;
+ return a;
case R_ARM_SBREL:
- return Sym.getVA(A) - getARMStaticBase(Sym);
+ return sym.getVA(a) - getARMStaticBase(sym);
case R_GOT:
- case R_GOT_PLT:
case R_RELAX_TLS_GD_TO_IE_ABS:
- return Sym.getGotVA() + A;
+ return sym.getGotVA() + a;
case R_GOTONLY_PC:
- return In.Got->getVA() + A - P;
- case R_GOTONLY_PC_FROM_END:
- return In.Got->getVA() + A - P + In.Got->getSize();
+ return in.got->getVA() + a - p;
+ case R_GOTPLTONLY_PC:
+ return in.gotPlt->getVA() + a - p;
case R_GOTREL:
- return Sym.getVA(A) - In.Got->getVA();
- case R_GOTREL_FROM_END:
- return Sym.getVA(A) - In.Got->getVA() - In.Got->getSize();
- case R_GOT_FROM_END:
- case R_RELAX_TLS_GD_TO_IE_END:
- return Sym.getGotOffset() + A - In.Got->getSize();
+ case R_PPC64_RELAX_TOC:
+ return sym.getVA(a) - in.got->getVA();
+ case R_GOTPLTREL:
+ return sym.getVA(a) - in.gotPlt->getVA();
+ case R_GOTPLT:
+ case R_RELAX_TLS_GD_TO_IE_GOTPLT:
+ return sym.getGotVA() + a - in.gotPlt->getVA();
case R_TLSLD_GOT_OFF:
case R_GOT_OFF:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
- return Sym.getGotOffset() + A;
+ return sym.getGotOffset() + a;
case R_AARCH64_GOT_PAGE_PC:
- case R_AARCH64_GOT_PAGE_PC_PLT:
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
- return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
+ return getAArch64Page(sym.getGotVA() + a) - getAArch64Page(p);
case R_GOT_PC:
case R_RELAX_TLS_GD_TO_IE:
- return Sym.getGotVA() + A - P;
+ return sym.getGotVA() + a - p;
case R_HEXAGON_GOT:
- return Sym.getGotVA() - In.GotPlt->getVA();
+ return sym.getGotVA() - in.gotPlt->getVA();
case R_MIPS_GOTREL:
- return Sym.getVA(A) - In.MipsGot->getGp(File);
+ return sym.getVA(a) - in.mipsGot->getGp(file);
case R_MIPS_GOT_GP:
- return In.MipsGot->getGp(File) + A;
+ return in.mipsGot->getGp(file) + a;
case R_MIPS_GOT_GP_PC: {
// R_MIPS_LO16 expression has R_MIPS_GOT_GP_PC type iif the target
// is _gp_disp symbol. In that case we should use the following
@@ -651,73 +685,76 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// 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.
- uint64_t V = In.MipsGot->getGp(File) + A - P;
- if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
- V += 4;
- if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16)
- V -= 1;
- return V;
+ uint64_t v = in.mipsGot->getGp(file) + a - p;
+ if (type == R_MIPS_LO16 || type == R_MICROMIPS_LO16)
+ v += 4;
+ if (type == R_MICROMIPS_LO16 || type == R_MICROMIPS_HI16)
+ v -= 1;
+ return v;
}
case R_MIPS_GOT_LOCAL_PAGE:
// If relocation against MIPS local symbol requires GOT entry, this entry
// should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend.
- return In.MipsGot->getVA() + In.MipsGot->getPageEntryOffset(File, Sym, A) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getPageEntryOffset(file, sym, a) -
+ in.mipsGot->getGp(file);
case R_MIPS_GOT_OFF:
case R_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
- return In.MipsGot->getVA() + In.MipsGot->getSymEntryOffset(File, Sym, A) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getSymEntryOffset(file, sym, a) -
+ in.mipsGot->getGp(file);
case R_MIPS_TLSGD:
- return In.MipsGot->getVA() + In.MipsGot->getGlobalDynOffset(File, Sym) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getGlobalDynOffset(file, sym) -
+ in.mipsGot->getGp(file);
case R_MIPS_TLSLD:
- return In.MipsGot->getVA() + In.MipsGot->getTlsIndexOffset(File) -
- In.MipsGot->getGp(File);
+ return in.mipsGot->getVA() + in.mipsGot->getTlsIndexOffset(file) -
+ in.mipsGot->getGp(file);
case R_AARCH64_PAGE_PC: {
- uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A);
- return getAArch64Page(Val) - getAArch64Page(P);
- }
- case R_AARCH64_PLT_PAGE_PC: {
- uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A;
- return getAArch64Page(Val) - getAArch64Page(P);
+ uint64_t val = sym.isUndefWeak() ? p + a : sym.getVA(a);
+ return getAArch64Page(val) - getAArch64Page(p);
}
case R_RISCV_PC_INDIRECT: {
- if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A))
- return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(),
- *HiRel->Sym, HiRel->Expr);
+ if (const Relocation *hiRel = getRISCVPCRelHi20(&sym, a))
+ return getRelocTargetVA(file, hiRel->type, hiRel->addend, sym.getVA(),
+ *hiRel->sym, hiRel->expr);
return 0;
}
case R_PC: {
- uint64_t Dest;
- if (Sym.isUndefWeak()) {
+ uint64_t dest;
+ if (sym.isUndefWeak()) {
// On ARM and AArch64 a branch to an undefined weak resolves to the
// next instruction, otherwise the place.
- if (Config->EMachine == EM_ARM)
- Dest = getARMUndefinedRelativeWeakVA(Type, A, P);
- else if (Config->EMachine == EM_AARCH64)
- Dest = getAArch64UndefinedRelativeWeakVA(Type, A, P);
+ if (config->emachine == EM_ARM)
+ dest = getARMUndefinedRelativeWeakVA(type, a, p);
+ else if (config->emachine == EM_AARCH64)
+ dest = getAArch64UndefinedRelativeWeakVA(type, a, p);
+ else if (config->emachine == EM_PPC)
+ dest = p;
else
- Dest = Sym.getVA(A);
+ dest = sym.getVA(a);
} else {
- Dest = Sym.getVA(A);
+ dest = sym.getVA(a);
}
- return Dest - P;
+ return dest - p;
}
case R_PLT:
- return Sym.getPltVA() + A;
+ return sym.getPltVA() + a;
case R_PLT_PC:
- case R_PPC_CALL_PLT:
- return Sym.getPltVA() + A - P;
- case R_PPC_CALL: {
- uint64_t SymVA = Sym.getVA(A);
+ case R_PPC64_CALL_PLT:
+ return sym.getPltVA() + a - p;
+ case R_PPC32_PLTREL:
+ // R_PPC_PLTREL24 uses the addend (usually 0 or 0x8000) to indicate r30
+ // stores _GLOBAL_OFFSET_TABLE_ or .got2+0x8000. The addend is ignored for
+ // target VA compuation.
+ return sym.getPltVA() - p;
+ case R_PPC64_CALL: {
+ uint64_t symVA = sym.getVA(a);
// If we have an undefined weak symbol, we might get here with a symbol
// address of zero. That could overflow, but the code must be unreachable,
// so don't bother doing anything at all.
- if (!SymVA)
+ if (!symVA)
return 0;
// PPC64 V2 ABI describes two entry points to a function. The global entry
@@ -726,46 +763,49 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// the callee. For local calls the caller and callee share the same
// TOC base and so the TOC pointer initialization code should be skipped by
// branching to the local entry point.
- return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther);
+ return symVA - p + getPPC64GlobalEntryToLocalEntryOffset(sym.stOther);
}
- case R_PPC_TOC:
- return getPPC64TocBase() + A;
+ case R_PPC64_TOCBASE:
+ return getPPC64TocBase() + a;
case R_RELAX_GOT_PC:
- return Sym.getVA(A) - P;
+ return sym.getVA(a) - p;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_IE_TO_LE:
case R_RELAX_TLS_LD_TO_LE:
case R_TLS:
- // A weak undefined TLS symbol resolves to the base of the TLS
- // block, i.e. gets a value of zero. If we pass --gc-sections to
- // lld and .tbss is not referenced, it gets reclaimed and we don't
- // create a TLS program header. Therefore, we resolve this
- // statically to zero.
- if (Sym.isTls() && Sym.isUndefWeak())
- return 0;
- return Sym.getVA(A) + getTlsTpOffset();
+ // It is not very clear what to return if the symbol is undefined. With
+ // --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())
+ return a;
+ return getTlsTpOffset(sym) + a;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
- return Out::TlsPhdr->p_memsz - Sym.getVA(A);
+ if (sym.isUndefined())
+ return a;
+ return -getTlsTpOffset(sym) + a;
case R_SIZE:
- return Sym.getSize() + A;
+ return sym.getSize() + a;
case R_TLSDESC:
- return In.Got->getGlobalDynAddr(Sym) + A;
+ return in.got->getGlobalDynAddr(sym) + a;
+ case R_TLSDESC_PC:
+ return in.got->getGlobalDynAddr(sym) + a - p;
case R_AARCH64_TLSDESC_PAGE:
- return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) -
- getAArch64Page(P);
+ return getAArch64Page(in.got->getGlobalDynAddr(sym) + a) -
+ getAArch64Page(p);
case R_TLSGD_GOT:
- return In.Got->getGlobalDynOffset(Sym) + A;
- case R_TLSGD_GOT_FROM_END:
- return In.Got->getGlobalDynOffset(Sym) + A - In.Got->getSize();
+ return in.got->getGlobalDynOffset(sym) + a;
+ case R_TLSGD_GOTPLT:
+ return in.got->getVA() + in.got->getGlobalDynOffset(sym) + a - in.gotPlt->getVA();
case R_TLSGD_PC:
- return In.Got->getGlobalDynAddr(Sym) + A - P;
- case R_TLSLD_GOT_FROM_END:
- return In.Got->getTlsIndexOff() + A - In.Got->getSize();
+ return in.got->getGlobalDynAddr(sym) + a - p;
+ case R_TLSLD_GOTPLT:
+ return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA();
case R_TLSLD_GOT:
- return In.Got->getTlsIndexOff() + A;
+ return in.got->getTlsIndexOff() + a;
case R_TLSLD_PC:
- return In.Got->getTlsIndexVA() + A - P;
+ return in.got->getTlsIndexVA() + a - p;
default:
llvm_unreachable("invalid expression");
}
@@ -779,36 +819,36 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// So, we handle relocations for non-alloc sections directly in this
// function as a performance optimization.
template <class ELFT, class RelTy>
-void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
- const unsigned Bits = sizeof(typename ELFT::uint) * 8;
+void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
+ const unsigned bits = sizeof(typename ELFT::uint) * 8;
- for (const RelTy &Rel : Rels) {
- RelType Type = Rel.getType(Config->IsMips64EL);
+ for (const RelTy &rel : rels) {
+ RelType type = rel.getType(config->isMips64EL);
// GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations
// against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed
// in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we
// need to keep this bug-compatible code for a while.
- if (Config->EMachine == EM_386 && Type == R_386_GOTPC)
+ if (config->emachine == EM_386 && type == R_386_GOTPC)
continue;
- uint64_t Offset = getOffset(Rel.r_offset);
- uint8_t *BufLoc = Buf + Offset;
- int64_t Addend = getAddend<ELFT>(Rel);
+ uint64_t offset = getOffset(rel.r_offset);
+ uint8_t *bufLoc = buf + offset;
+ int64_t addend = getAddend<ELFT>(rel);
if (!RelTy::IsRela)
- Addend += Target->getImplicitAddend(BufLoc, Type);
+ addend += target->getImplicitAddend(bufLoc, type);
- Symbol &Sym = getFile<ELFT>()->getRelocTargetSym(Rel);
- RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc);
- if (Expr == R_NONE)
+ Symbol &sym = getFile<ELFT>()->getRelocTargetSym(rel);
+ RelExpr expr = target->getRelExpr(type, sym, bufLoc);
+ if (expr == R_NONE)
continue;
- if (Expr != R_ABS) {
- std::string Msg = getLocation<ELFT>(Offset) +
- ": has non-ABS relocation " + toString(Type) +
- " against symbol '" + toString(Sym) + "'";
- if (Expr != R_PC) {
- error(Msg);
+ 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) {
+ error(msg);
return;
}
@@ -819,16 +859,16 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// relocations without any errors and relocate them as if they were at
// 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)));
+ warn(msg);
+ target->relocateOne(bufLoc, type,
+ SignExtend64<bits>(sym.getVA(addend - offset)));
continue;
}
- if (Sym.isTls() && !Out::TlsPhdr)
- Target->relocateOne(BufLoc, Type, 0);
+ if (sym.isTls() && !Out::tlsPhdr)
+ target->relocateOne(bufLoc, type, 0);
else
- Target->relocateOne(BufLoc, Type, SignExtend64<Bits>(Sym.getVA(Addend)));
+ target->relocateOne(bufLoc, type, SignExtend64<bits>(sym.getVA(addend)));
}
}
@@ -837,96 +877,100 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// relocations aimed to update addends. They are handled in relocateAlloc()
// for allocatable sections, and this function does the same for
// non-allocatable sections, such as sections with debug information.
-static void relocateNonAllocForRelocatable(InputSection *Sec, uint8_t *Buf) {
- const unsigned Bits = Config->Is64 ? 64 : 32;
+static void relocateNonAllocForRelocatable(InputSection *sec, uint8_t *buf) {
+ const unsigned bits = config->is64 ? 64 : 32;
- for (const Relocation &Rel : Sec->Relocations) {
+ for (const Relocation &rel : sec->relocations) {
// InputSection::copyRelocations() adds only R_ABS relocations.
- 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);
+ 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);
}
}
template <class ELFT>
-void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) {
- if (Flags & SHF_EXECINSTR)
- adjustSplitStackFunctionPrologues<ELFT>(Buf, BufEnd);
+void InputSectionBase::relocate(uint8_t *buf, uint8_t *bufEnd) {
+ if (flags & SHF_EXECINSTR)
+ adjustSplitStackFunctionPrologues<ELFT>(buf, bufEnd);
- if (Flags & SHF_ALLOC) {
- relocateAlloc(Buf, BufEnd);
+ if (flags & SHF_ALLOC) {
+ relocateAlloc(buf, bufEnd);
return;
}
- auto *Sec = cast<InputSection>(this);
- if (Config->Relocatable)
- relocateNonAllocForRelocatable(Sec, Buf);
- else if (Sec->AreRelocsRela)
- Sec->relocateNonAlloc<ELFT>(Buf, Sec->template relas<ELFT>());
+ auto *sec = cast<InputSection>(this);
+ if (config->relocatable)
+ relocateNonAllocForRelocatable(sec, buf);
+ else if (sec->areRelocsRela)
+ sec->relocateNonAlloc<ELFT>(buf, sec->template relas<ELFT>());
else
- Sec->relocateNonAlloc<ELFT>(Buf, Sec->template rels<ELFT>());
+ sec->relocateNonAlloc<ELFT>(buf, sec->template rels<ELFT>());
}
-void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
- assert(Flags & SHF_ALLOC);
- const unsigned Bits = Config->Wordsize * 8;
+void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
+ assert(flags & SHF_ALLOC);
+ const unsigned bits = config->wordsize * 8;
- for (const Relocation &Rel : Relocations) {
- uint64_t Offset = Rel.Offset;
- if (auto *Sec = dyn_cast<InputSection>(this))
- Offset += Sec->OutSecOff;
- uint8_t *BufLoc = Buf + Offset;
- RelType Type = Rel.Type;
+ for (const Relocation &rel : relocations) {
+ uint64_t offset = rel.offset;
+ if (auto *sec = dyn_cast<InputSection>(this))
+ offset += sec->outSecOff;
+ uint8_t *bufLoc = buf + offset;
+ RelType type = rel.type;
- uint64_t AddrLoc = getOutputSection()->Addr + Offset;
- RelExpr Expr = Rel.Expr;
- uint64_t TargetVA = SignExtend64(
- getRelocTargetVA(File, Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr),
- Bits);
+ uint64_t addrLoc = getOutputSection()->addr + offset;
+ RelExpr expr = rel.expr;
+ uint64_t targetVA = SignExtend64(
+ getRelocTargetVA(file, type, rel.addend, addrLoc, *rel.sym, expr),
+ bits);
- switch (Expr) {
+ switch (expr) {
case R_RELAX_GOT_PC:
case R_RELAX_GOT_PC_NOPIC:
- Target->relaxGot(BufLoc, TargetVA);
+ target->relaxGot(bufLoc, type, targetVA);
+ break;
+ case R_PPC64_RELAX_TOC:
+ if (!tryRelaxPPC64TocIndirection(type, rel, bufLoc))
+ target->relocateOne(bufLoc, type, targetVA);
break;
case R_RELAX_TLS_IE_TO_LE:
- Target->relaxTlsIeToLe(BufLoc, Type, TargetVA);
+ target->relaxTlsIeToLe(bufLoc, type, 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, type, 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, type, 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_END:
- Target->relaxTlsGdToIe(BufLoc, Type, TargetVA);
+ case R_RELAX_TLS_GD_TO_IE_GOTPLT:
+ target->relaxTlsGdToIe(bufLoc, type, targetVA);
break;
- case R_PPC_CALL:
+ case R_PPC64_CALL:
// If this is a call to __tls_get_addr, it may be part of a TLS
// sequence that has been relaxed and turned into a nop. In this
// case, we don't want to handle it as a call.
- if (read32(BufLoc) == 0x60000000) // nop
+ if (read32(bufLoc) == 0x60000000) // nop
break;
// Patch a nop (0x60000000) to a ld.
- if (Rel.Sym->NeedsTocRestore) {
- if (BufLoc + 8 > BufEnd || read32(BufLoc + 4) != 0x60000000) {
- error(getErrorLocation(BufLoc) + "call lacks nop, can't restore toc");
+ if (rel.sym->needsTocRestore) {
+ if (bufLoc + 8 > bufEnd || read32(bufLoc + 4) != 0x60000000) {
+ error(getErrorLocation(bufLoc) + "call lacks nop, can't restore toc");
break;
}
- write32(BufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
+ write32(bufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
}
- Target->relocateOne(BufLoc, Type, TargetVA);
+ target->relocateOne(bufLoc, type, targetVA);
break;
default:
- Target->relocateOne(BufLoc, Type, TargetVA);
+ target->relocateOne(bufLoc, type, targetVA);
break;
}
}
@@ -935,44 +979,44 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
// For each function-defining prologue, find any calls to __morestack,
// and replace them with calls to __morestack_non_split.
static void switchMorestackCallsToMorestackNonSplit(
- DenseSet<Defined *> &Prologues, std::vector<Relocation *> &MorestackCalls) {
+ DenseSet<Defined *> &prologues, std::vector<Relocation *> &morestackCalls) {
// If the target adjusted a function's prologue, all calls to
// __morestack inside that function should be switched to
// __morestack_non_split.
- Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split");
- if (!MoreStackNonSplit) {
+ Symbol *moreStackNonSplit = symtab->find("__morestack_non_split");
+ if (!moreStackNonSplit) {
error("Mixing split-stack objects requires a definition of "
"__morestack_non_split");
return;
}
// Sort both collections to compare addresses efficiently.
- llvm::sort(MorestackCalls, [](const Relocation *L, const Relocation *R) {
- return L->Offset < R->Offset;
+ llvm::sort(morestackCalls, [](const Relocation *l, const Relocation *r) {
+ return l->offset < r->offset;
});
- std::vector<Defined *> Functions(Prologues.begin(), Prologues.end());
- llvm::sort(Functions, [](const Defined *L, const Defined *R) {
- return L->Value < R->Value;
+ std::vector<Defined *> functions(prologues.begin(), prologues.end());
+ llvm::sort(functions, [](const Defined *l, const Defined *r) {
+ return l->value < r->value;
});
- auto It = MorestackCalls.begin();
- for (Defined *F : Functions) {
+ auto it = morestackCalls.begin();
+ for (Defined *f : functions) {
// Find the first call to __morestack within the function.
- while (It != MorestackCalls.end() && (*It)->Offset < F->Value)
- ++It;
+ while (it != morestackCalls.end() && (*it)->offset < f->value)
+ ++it;
// Adjust all calls inside the function.
- while (It != MorestackCalls.end() && (*It)->Offset < F->Value + F->Size) {
- (*It)->Sym = MoreStackNonSplit;
- ++It;
+ while (it != morestackCalls.end() && (*it)->offset < f->value + f->size) {
+ (*it)->sym = moreStackNonSplit;
+ ++it;
}
}
}
-static bool enclosingPrologueAttempted(uint64_t Offset,
- const DenseSet<Defined *> &Prologues) {
- for (Defined *F : Prologues)
- if (F->Value <= Offset && Offset < F->Value + F->Size)
+static bool enclosingPrologueAttempted(uint64_t offset,
+ const DenseSet<Defined *> &prologues) {
+ for (Defined *f : prologues)
+ if (f->value <= offset && offset < f->value + f->size)
return true;
return false;
}
@@ -982,30 +1026,30 @@ static bool enclosingPrologueAttempted(uint64_t Offset,
// adjusted to ensure that the called function will have enough stack
// available. Find those functions, and adjust their prologues.
template <class ELFT>
-void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
- uint8_t *End) {
- if (!getFile<ELFT>()->SplitStack)
+void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
+ uint8_t *end) {
+ if (!getFile<ELFT>()->splitStack)
return;
- DenseSet<Defined *> Prologues;
- std::vector<Relocation *> MorestackCalls;
+ DenseSet<Defined *> prologues;
+ std::vector<Relocation *> morestackCalls;
- for (Relocation &Rel : Relocations) {
+ for (Relocation &rel : relocations) {
// Local symbols can't possibly be cross-calls, and should have been
// resolved long before this line.
- if (Rel.Sym->isLocal())
+ if (rel.sym->isLocal())
continue;
// Ignore calls into the split-stack api.
- if (Rel.Sym->getName().startswith("__morestack")) {
- if (Rel.Sym->getName().equals("__morestack"))
- MorestackCalls.push_back(&Rel);
+ if (rel.sym->getName().startswith("__morestack")) {
+ if (rel.sym->getName().equals("__morestack"))
+ morestackCalls.push_back(&rel);
continue;
}
// A relocation to non-function isn't relevant. Sometimes
// __morestack is not marked as a function, so this check comes
// after the name check.
- if (Rel.Sym->Type != STT_FUNC)
+ if (rel.sym->type != STT_FUNC)
continue;
// If the callee's-file was compiled with split stack, nothing to do. In
@@ -1013,106 +1057,117 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
// being produced". So an "undefined" symbol might be provided by a shared
// library. It is not possible to tell how such symbols were compiled, so be
// conservative.
- if (Defined *D = dyn_cast<Defined>(Rel.Sym))
- if (InputSection *IS = cast_or_null<InputSection>(D->Section))
- if (!IS || !IS->getFile<ELFT>() || IS->getFile<ELFT>()->SplitStack)
+ if (Defined *d = dyn_cast<Defined>(rel.sym))
+ if (InputSection *isec = cast_or_null<InputSection>(d->section))
+ if (!isec || !isec->getFile<ELFT>() || isec->getFile<ELFT>()->splitStack)
continue;
- if (enclosingPrologueAttempted(Rel.Offset, Prologues))
+ if (enclosingPrologueAttempted(rel.offset, prologues))
continue;
- if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) {
- Prologues.insert(F);
- if (Target->adjustPrologueForCrossSplitStack(Buf + getOffset(F->Value),
- End, F->StOther))
+ if (Defined *f = getEnclosingFunction<ELFT>(rel.offset)) {
+ prologues.insert(f);
+ if (target->adjustPrologueForCrossSplitStack(buf + getOffset(f->value),
+ end, f->stOther))
continue;
- if (!getFile<ELFT>()->SomeNoSplitStack)
- error(lld::toString(this) + ": " + F->getName() +
- " (with -fsplit-stack) calls " + Rel.Sym->getName() +
+ if (!getFile<ELFT>()->someNoSplitStack)
+ error(lld::toString(this) + ": " + f->getName() +
+ " (with -fsplit-stack) calls " + rel.sym->getName() +
" (without -fsplit-stack), but couldn't adjust its prologue");
}
}
- if (Target->NeedsMoreStackNonSplit)
- switchMorestackCallsToMorestackNonSplit(Prologues, MorestackCalls);
+ if (target->needsMoreStackNonSplit)
+ switchMorestackCallsToMorestackNonSplit(prologues, morestackCalls);
}
-template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
- if (Type == SHT_NOBITS)
+template <class ELFT> void InputSection::writeTo(uint8_t *buf) {
+ if (type == SHT_NOBITS)
return;
- if (auto *S = dyn_cast<SyntheticSection>(this)) {
- S->writeTo(Buf + OutSecOff);
+ if (auto *s = dyn_cast<SyntheticSection>(this)) {
+ s->writeTo(buf + outSecOff);
return;
}
// If -r or --emit-relocs is given, then an InputSection
// may be a relocation section.
- if (Type == SHT_RELA) {
- copyRelocations<ELFT>(Buf + OutSecOff, getDataAs<typename ELFT::Rela>());
+ if (type == SHT_RELA) {
+ copyRelocations<ELFT>(buf + outSecOff, getDataAs<typename ELFT::Rela>());
return;
}
- if (Type == SHT_REL) {
- copyRelocations<ELFT>(Buf + OutSecOff, getDataAs<typename ELFT::Rel>());
+ if (type == SHT_REL) {
+ copyRelocations<ELFT>(buf + outSecOff, getDataAs<typename ELFT::Rel>());
return;
}
// If -r is given, we may have a SHT_GROUP section.
- if (Type == SHT_GROUP) {
- copyShtGroup<ELFT>(Buf + OutSecOff);
+ if (type == SHT_GROUP) {
+ copyShtGroup<ELFT>(buf + outSecOff);
return;
}
// If this is a compressed section, uncompress section contents directly
// to the buffer.
- if (UncompressedSize >= 0 && !UncompressedBuf) {
- size_t Size = UncompressedSize;
- if (Error E = zlib::uncompress(toStringRef(RawData),
- (char *)(Buf + OutSecOff), Size))
+ if (uncompressedSize >= 0) {
+ size_t size = uncompressedSize;
+ if (Error e = zlib::uncompress(toStringRef(rawData),
+ (char *)(buf + outSecOff), size))
fatal(toString(this) +
- ": uncompress failed: " + llvm::toString(std::move(E)));
- uint8_t *BufEnd = Buf + OutSecOff + Size;
- relocate<ELFT>(Buf, BufEnd);
+ ": uncompress failed: " + llvm::toString(std::move(e)));
+ uint8_t *bufEnd = buf + outSecOff + size;
+ relocate<ELFT>(buf, bufEnd);
return;
}
// Copy section contents from source object file to output file
// and then apply relocations.
- memcpy(Buf + OutSecOff, data().data(), data().size());
- uint8_t *BufEnd = Buf + OutSecOff + data().size();
- relocate<ELFT>(Buf, BufEnd);
+ memcpy(buf + outSecOff, data().data(), data().size());
+ uint8_t *bufEnd = buf + outSecOff + data().size();
+ relocate<ELFT>(buf, bufEnd);
}
-void InputSection::replace(InputSection *Other) {
- Alignment = std::max(Alignment, Other->Alignment);
- Other->Repl = Repl;
- Other->Live = false;
+void InputSection::replace(InputSection *other) {
+ alignment = std::max(alignment, other->alignment);
+
+ // When a section is replaced with another section that was allocated to
+ // another partition, the replacement section (and its associated sections)
+ // need to be placed in the main partition so that both partitions will be
+ // able to access it.
+ if (partition != other->partition) {
+ partition = 1;
+ for (InputSection *isec : dependentSections)
+ isec->partition = 1;
+ }
+
+ other->repl = repl;
+ other->markDead();
}
template <class ELFT>
-EhInputSection::EhInputSection(ObjFile<ELFT> &F,
- const typename ELFT::Shdr &Header,
- StringRef Name)
- : InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {}
+EhInputSection::EhInputSection(ObjFile<ELFT> &f,
+ const typename ELFT::Shdr &header,
+ StringRef name)
+ : InputSectionBase(f, header, name, InputSectionBase::EHFrame) {}
SyntheticSection *EhInputSection::getParent() const {
- return cast_or_null<SyntheticSection>(Parent);
+ return cast_or_null<SyntheticSection>(parent);
}
// Returns the index of the first relocation that points to a region between
// Begin and Begin+Size.
template <class IntTy, class RelTy>
-static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels,
- unsigned &RelocI) {
+static unsigned getReloc(IntTy begin, IntTy size, const ArrayRef<RelTy> &rels,
+ unsigned &relocI) {
// Start search from RelocI for fast access. That works because the
// relocations are sorted in .eh_frame.
- for (unsigned N = Rels.size(); RelocI < N; ++RelocI) {
- const RelTy &Rel = Rels[RelocI];
- if (Rel.r_offset < Begin)
+ for (unsigned n = rels.size(); relocI < n; ++relocI) {
+ const RelTy &rel = rels[relocI];
+ if (rel.r_offset < begin)
continue;
- if (Rel.r_offset < Begin + Size)
- return RelocI;
+ if (rel.r_offset < begin + size)
+ return relocI;
return -1;
}
return -1;
@@ -1121,84 +1176,84 @@ static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels,
// .eh_frame is a sequence of CIE or FDE records.
// This function splits an input section into records and returns them.
template <class ELFT> void EhInputSection::split() {
- if (AreRelocsRela)
+ if (areRelocsRela)
split<ELFT>(relas<ELFT>());
else
split<ELFT>(rels<ELFT>());
}
template <class ELFT, class RelTy>
-void EhInputSection::split(ArrayRef<RelTy> Rels) {
- unsigned RelI = 0;
- for (size_t Off = 0, End = data().size(); Off != End;) {
- size_t Size = readEhRecordSize(this, Off);
- Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
+void EhInputSection::split(ArrayRef<RelTy> rels) {
+ unsigned relI = 0;
+ for (size_t off = 0, end = data().size(); off != end;) {
+ size_t size = readEhRecordSize(this, off);
+ pieces.emplace_back(off, this, size, getReloc(off, size, rels, relI));
// The empty record is the end marker.
- if (Size == 4)
+ if (size == 4)
break;
- Off += Size;
+ off += size;
}
}
-static size_t findNull(StringRef S, size_t EntSize) {
+static size_t findNull(StringRef s, size_t entSize) {
// Optimize the common case.
- if (EntSize == 1)
- return S.find(0);
+ if (entSize == 1)
+ return s.find(0);
- for (unsigned I = 0, N = S.size(); I != N; I += EntSize) {
- const char *B = S.begin() + I;
- if (std::all_of(B, B + EntSize, [](char C) { return C == 0; }))
- return I;
+ for (unsigned i = 0, n = s.size(); i != n; i += entSize) {
+ const char *b = s.begin() + i;
+ if (std::all_of(b, b + entSize, [](char c) { return c == 0; }))
+ return i;
}
return StringRef::npos;
}
SyntheticSection *MergeInputSection::getParent() const {
- return cast_or_null<SyntheticSection>(Parent);
+ return cast_or_null<SyntheticSection>(parent);
}
// Split SHF_STRINGS section. Such section is a sequence of
// null-terminated strings.
-void MergeInputSection::splitStrings(ArrayRef<uint8_t> Data, size_t EntSize) {
- size_t Off = 0;
- bool IsAlloc = Flags & SHF_ALLOC;
- StringRef S = toStringRef(Data);
-
- while (!S.empty()) {
- size_t End = findNull(S, EntSize);
- if (End == StringRef::npos)
+void MergeInputSection::splitStrings(ArrayRef<uint8_t> data, size_t entSize) {
+ size_t off = 0;
+ bool isAlloc = flags & SHF_ALLOC;
+ StringRef s = toStringRef(data);
+
+ while (!s.empty()) {
+ size_t end = findNull(s, entSize);
+ if (end == StringRef::npos)
fatal(toString(this) + ": string is not null terminated");
- size_t Size = End + EntSize;
+ size_t size = end + entSize;
- Pieces.emplace_back(Off, xxHash64(S.substr(0, Size)), !IsAlloc);
- S = S.substr(Size);
- Off += Size;
+ pieces.emplace_back(off, xxHash64(s.substr(0, size)), !isAlloc);
+ s = s.substr(size);
+ off += size;
}
}
// Split non-SHF_STRINGS section. Such section is a sequence of
// fixed size records.
-void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> Data,
- size_t EntSize) {
- size_t Size = Data.size();
- assert((Size % EntSize) == 0);
- bool IsAlloc = Flags & SHF_ALLOC;
-
- for (size_t I = 0; I != Size; I += EntSize)
- Pieces.emplace_back(I, xxHash64(Data.slice(I, EntSize)), !IsAlloc);
+void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> data,
+ size_t entSize) {
+ size_t size = data.size();
+ assert((size % entSize) == 0);
+ bool isAlloc = flags & SHF_ALLOC;
+
+ for (size_t i = 0; i != size; i += entSize)
+ pieces.emplace_back(i, xxHash64(data.slice(i, entSize)), !isAlloc);
}
template <class ELFT>
-MergeInputSection::MergeInputSection(ObjFile<ELFT> &F,
- const typename ELFT::Shdr &Header,
- StringRef Name)
- : InputSectionBase(F, Header, Name, InputSectionBase::Merge) {}
+MergeInputSection::MergeInputSection(ObjFile<ELFT> &f,
+ const typename ELFT::Shdr &header,
+ StringRef name)
+ : InputSectionBase(f, header, name, InputSectionBase::Merge) {}
-MergeInputSection::MergeInputSection(uint64_t Flags, uint32_t Type,
- uint64_t Entsize, ArrayRef<uint8_t> Data,
- StringRef Name)
- : InputSectionBase(nullptr, Flags, Type, Entsize, /*Link*/ 0, /*Info*/ 0,
- /*Alignment*/ Entsize, Data, Name, SectionBase::Merge) {}
+MergeInputSection::MergeInputSection(uint64_t flags, uint32_t type,
+ uint64_t entsize, ArrayRef<uint8_t> data,
+ StringRef name)
+ : InputSectionBase(nullptr, flags, type, entsize, /*Link*/ 0, /*Info*/ 0,
+ /*Alignment*/ entsize, data, name, SectionBase::Merge) {}
// This function is called after we obtain a complete list of input sections
// that need to be linked. This is responsible to split section contents
@@ -1207,37 +1262,35 @@ MergeInputSection::MergeInputSection(uint64_t Flags, uint32_t Type,
// Note that this function is called from parallelForEach. This must be
// thread-safe (i.e. no memory allocation from the pools).
void MergeInputSection::splitIntoPieces() {
- assert(Pieces.empty());
+ assert(pieces.empty());
- if (Flags & SHF_STRINGS)
- splitStrings(data(), Entsize);
+ if (flags & SHF_STRINGS)
+ splitStrings(data(), entsize);
else
- splitNonStrings(data(), Entsize);
+ splitNonStrings(data(), entsize);
}
-SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
- if (this->data().size() <= Offset)
+SectionPiece *MergeInputSection::getSectionPiece(uint64_t offset) {
+ if (this->data().size() <= offset)
fatal(toString(this) + ": offset is outside the section");
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to do a binary search of the original section piece vector.
- auto It2 =
- llvm::upper_bound(Pieces, Offset, [](uint64_t Offset, SectionPiece P) {
- return Offset < P.InputOff;
- });
- return &It2[-1];
+ auto it = partition_point(
+ pieces, [=](SectionPiece p) { return p.inputOff <= offset; });
+ return &it[-1];
}
// Returns the offset in an output section for a given input offset.
// Because contents of a mergeable section is not contiguous in output,
// it is not just an addition to a base output offset.
-uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const {
+uint64_t MergeInputSection::getParentOffset(uint64_t offset) const {
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
- const SectionPiece &Piece =
- *(const_cast<MergeInputSection *>(this)->getSectionPiece (Offset));
- uint64_t Addend = Offset - Piece.InputOff;
- return Piece.OutputOff + Addend;
+ const SectionPiece &piece =
+ *(const_cast<MergeInputSection *>(this)->getSectionPiece (offset));
+ uint64_t addend = offset - piece.inputOff;
+ return piece.outputOff + addend;
}
template InputSection::InputSection(ObjFile<ELF32LE> &, const ELF32LE::Shdr &,
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index 34f411e87200..3a974074e0e5 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -1,9 +1,8 @@
//===- InputSection.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -26,11 +25,14 @@ class Symbol;
struct SectionPiece;
class Defined;
+struct Partition;
class SyntheticSection;
class MergeSyntheticSection;
template <class ELFT> class ObjFile;
class OutputSection;
+extern std::vector<Partition> partitions;
+
// This is the base class of all sections that lld handles. Some are sections in
// input files, some are sections in the produced output file and some exist
// just as a convenience for implementing special ways of combining some
@@ -39,38 +41,53 @@ class SectionBase {
public:
enum Kind { Regular, EHFrame, Merge, Synthetic, Output };
- Kind kind() const { return (Kind)SectionKind; }
+ Kind kind() const { return (Kind)sectionKind; }
- StringRef Name;
+ StringRef name;
// This pointer points to the "real" instance of this instance.
// Usually Repl == this. However, if ICF merges two sections,
// Repl pointer of one section points to another section. So,
// if you need to get a pointer to this instance, do not use
// this but instead this->Repl.
- SectionBase *Repl;
+ SectionBase *repl;
- unsigned SectionKind : 3;
+ unsigned sectionKind : 3;
- // The next two bit fields are only used by InputSectionBase, but we
+ // The next three bit fields are only used by InputSectionBase, but we
// put them here so the struct packs better.
- // The garbage collector sets sections' Live bits.
- // If GC is disabled, all sections are considered live by default.
- unsigned Live : 1;
+ // True if this section has already been placed to a linker script
+ // output section. This is needed because, in a linker script, you
+ // can refer to the same section more than once. For example, in
+ // the following linker script,
+ //
+ // .foo : { *(.text) }
+ // .bar : { *(.text) }
+ //
+ // .foo takes all .text sections, and .bar becomes empty. To achieve
+ // this, we need to memorize whether a section has been placed or
+ // not for each input section.
+ unsigned assigned : 1;
- unsigned Bss : 1;
+ unsigned bss : 1;
// Set for sections that should not be folded by ICF.
- unsigned KeepUnique : 1;
+ unsigned keepUnique : 1;
+
+ // The 1-indexed partition that this section is assigned to by the garbage
+ // collector, or 0 if this section is dead. Normally there is only one
+ // partition, so this will either be 0 or 1.
+ uint8_t partition;
+ elf::Partition &getPartition() const;
// These corresponds to the fields in Elf_Shdr.
- uint32_t Alignment;
- uint64_t Flags;
- uint64_t Entsize;
- uint32_t Type;
- uint32_t Link;
- uint32_t Info;
+ uint32_t alignment;
+ uint64_t flags;
+ uint64_t entsize;
+ uint32_t type;
+ uint32_t link;
+ uint32_t info;
OutputSection *getOutputSection();
const OutputSection *getOutputSection() const {
@@ -79,90 +96,81 @@ public:
// Translate an offset in the input section to an offset in the output
// section.
- uint64_t getOffset(uint64_t Offset) const;
+ uint64_t getOffset(uint64_t offset) const;
- uint64_t getVA(uint64_t Offset = 0) const;
+ uint64_t getVA(uint64_t offset = 0) const;
+
+ bool isLive() const { return partition != 0; }
+ void markLive() { partition = 1; }
+ void markDead() { partition = 0; }
protected:
- SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
- uint64_t Entsize, uint64_t Alignment, uint32_t Type,
- uint32_t Info, uint32_t Link)
- : Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
- Bss(false), KeepUnique(false), Alignment(Alignment), Flags(Flags),
- Entsize(Entsize), Type(Type), Link(Link), Info(Info) {}
+ SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
+ uint64_t entsize, uint64_t alignment, uint32_t type,
+ uint32_t info, uint32_t link)
+ : name(name), repl(this), sectionKind(sectionKind), assigned(false),
+ bss(false), keepUnique(false), partition(0), alignment(alignment),
+ flags(flags), entsize(entsize), type(type), link(link), info(info) {}
};
// This corresponds to a section of an input file.
class InputSectionBase : public SectionBase {
public:
template <class ELFT>
- InputSectionBase(ObjFile<ELFT> &File, const typename ELFT::Shdr &Header,
- StringRef Name, Kind SectionKind);
+ InputSectionBase(ObjFile<ELFT> &file, const typename ELFT::Shdr &header,
+ StringRef name, Kind sectionKind);
+
+ InputSectionBase(InputFile *file, uint64_t flags, uint32_t type,
+ uint64_t entsize, uint32_t link, uint32_t info,
+ uint32_t alignment, ArrayRef<uint8_t> data, StringRef name,
+ Kind sectionKind);
- InputSectionBase(InputFile *File, uint64_t Flags, uint32_t Type,
- uint64_t Entsize, uint32_t Link, uint32_t Info,
- uint32_t Alignment, ArrayRef<uint8_t> Data, StringRef Name,
- Kind SectionKind);
+ static bool classof(const SectionBase *s) { return s->kind() != Output; }
- static bool classof(const SectionBase *S) { return S->kind() != Output; }
+ // Relocations that refer to this section.
+ unsigned numRelocations : 31;
+ unsigned areRelocsRela : 1;
+ const void *firstRelocation = nullptr;
// The file which contains this section. Its dynamic type is always
// ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as
// its static type.
- InputFile *File;
+ InputFile *file;
template <class ELFT> ObjFile<ELFT> *getFile() const {
- return cast_or_null<ObjFile<ELFT>>(File);
+ return cast_or_null<ObjFile<ELFT>>(file);
}
ArrayRef<uint8_t> data() const {
- if (UncompressedSize >= 0 && !UncompressedBuf)
+ if (uncompressedSize >= 0)
uncompress();
- return RawData;
+ return rawData;
}
uint64_t getOffsetInFile() const;
- // True if this section has already been placed to a linker script
- // output section. This is needed because, in a linker script, you
- // can refer to the same section more than once. For example, in
- // the following linker script,
- //
- // .foo : { *(.text) }
- // .bar : { *(.text) }
- //
- // .foo takes all .text sections, and .bar becomes empty. To achieve
- // this, we need to memorize whether a section has been placed or
- // not for each input section.
- bool Assigned = false;
-
// Input sections are part of an output section. Special sections
// like .eh_frame and merge sections are first combined into a
// synthetic section that is then added to an output section. In all
// cases this points one level up.
- SectionBase *Parent = nullptr;
-
- // Relocations that refer to this section.
- const void *FirstRelocation = nullptr;
- unsigned NumRelocations : 31;
- unsigned AreRelocsRela : 1;
+ SectionBase *parent = nullptr;
template <class ELFT> ArrayRef<typename ELFT::Rel> rels() const {
- assert(!AreRelocsRela);
+ assert(!areRelocsRela);
return llvm::makeArrayRef(
- static_cast<const typename ELFT::Rel *>(FirstRelocation),
- NumRelocations);
+ static_cast<const typename ELFT::Rel *>(firstRelocation),
+ numRelocations);
}
template <class ELFT> ArrayRef<typename ELFT::Rela> relas() const {
- assert(AreRelocsRela);
+ assert(areRelocsRela);
return llvm::makeArrayRef(
- static_cast<const typename ELFT::Rela *>(FirstRelocation),
- NumRelocations);
+ static_cast<const typename ELFT::Rela *>(firstRelocation),
+ numRelocations);
}
// InputSections that are dependent on us (reverse dependency for GC)
- llvm::TinyPtrVector<InputSection *> DependentSections;
+ llvm::TinyPtrVector<InputSection *> dependentSections;
// Returns the size of this section (even if this is a common or BSS.)
size_t getSize() const;
@@ -172,23 +180,23 @@ public:
// Get the function symbol that encloses this offset from within the
// section.
template <class ELFT>
- Defined *getEnclosingFunction(uint64_t Offset);
+ Defined *getEnclosingFunction(uint64_t offset);
// Returns a source location string. Used to construct an error message.
- template <class ELFT> std::string getLocation(uint64_t Offset);
- std::string getSrcMsg(const Symbol &Sym, uint64_t Offset);
- std::string getObjMsg(uint64_t Offset);
+ template <class ELFT> std::string getLocation(uint64_t offset);
+ std::string getSrcMsg(const Symbol &sym, uint64_t offset);
+ std::string getObjMsg(uint64_t offset);
// Each section knows how to relocate itself. These functions apply
// relocations, assuming that Buf points to this section's copy in
// the mmap'ed output buffer.
- template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);
- void relocateAlloc(uint8_t *Buf, uint8_t *BufEnd);
+ template <class ELFT> void relocate(uint8_t *buf, uint8_t *bufEnd);
+ void relocateAlloc(uint8_t *buf, uint8_t *bufEnd);
// 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;
+ std::vector<Relocation> relocations;
// A function compiled with -fsplit-stack calling a function
// compiled without -fsplit-stack needs its prologue adjusted. Find
@@ -196,25 +204,26 @@ public:
// to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more
// information.
template <typename ELFT>
- void adjustSplitStackFunctionPrologues(uint8_t *Buf, uint8_t *End);
+ void adjustSplitStackFunctionPrologues(uint8_t *buf, uint8_t *end);
template <typename T> llvm::ArrayRef<T> getDataAs() const {
- size_t S = data().size();
- assert(S % sizeof(T) == 0);
- return llvm::makeArrayRef<T>((const T *)data().data(), S / sizeof(T));
+ size_t s = data().size();
+ assert(s % sizeof(T) == 0);
+ return llvm::makeArrayRef<T>((const T *)data().data(), s / sizeof(T));
}
protected:
void parseCompressedHeader();
void uncompress() const;
- mutable ArrayRef<uint8_t> RawData;
+ mutable ArrayRef<uint8_t> rawData;
- // A pointer that owns uncompressed data if a section is compressed by zlib.
- // Since the feature is not used often, this is usually a nullptr.
- mutable std::unique_ptr<char[]> UncompressedBuf;
- int64_t UncompressedSize = -1;
+ // This field stores the uncompressed size of the compressed data in rawData,
+ // or -1 if rawData is not compressed (either because the section wasn't
+ // compressed in the first place, or because we ended up uncompressing it).
+ // Since the feature is not used often, this is usually -1.
+ mutable int64_t uncompressedSize = -1;
};
// SectionPiece represents a piece of splittable section contents.
@@ -222,14 +231,13 @@ protected:
// have to be as compact as possible, which is why we don't store the size (can
// be found by looking at the next one).
struct SectionPiece {
- SectionPiece(size_t Off, uint32_t Hash, bool Live)
- : InputOff(Off), Hash(Hash), OutputOff(0),
- Live(Live || !Config->GcSections) {}
-
- uint32_t InputOff;
- uint32_t Hash;
- int64_t OutputOff : 63;
- uint64_t Live : 1;
+ SectionPiece(size_t off, uint32_t hash, bool live)
+ : inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {}
+
+ uint32_t inputOff;
+ uint32_t live : 1;
+ uint32_t hash : 31;
+ uint64_t outputOff = 0;
};
static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
@@ -238,74 +246,74 @@ static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
class MergeInputSection : public InputSectionBase {
public:
template <class ELFT>
- MergeInputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name);
- MergeInputSection(uint64_t Flags, uint32_t Type, uint64_t Entsize,
- ArrayRef<uint8_t> Data, StringRef Name);
+ MergeInputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name);
+ MergeInputSection(uint64_t flags, uint32_t type, uint64_t entsize,
+ ArrayRef<uint8_t> data, StringRef name);
- static bool classof(const SectionBase *S) { return S->kind() == Merge; }
+ static bool classof(const SectionBase *s) { return s->kind() == Merge; }
void splitIntoPieces();
// Translate an offset in the input section to an offset in the parent
// MergeSyntheticSection.
- uint64_t getParentOffset(uint64_t Offset) const;
+ uint64_t getParentOffset(uint64_t offset) const;
// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
- std::vector<SectionPiece> Pieces;
+ std::vector<SectionPiece> pieces;
// Returns I'th piece's data. This function is very hot when
// string merging is enabled, so we want to inline.
LLVM_ATTRIBUTE_ALWAYS_INLINE
- llvm::CachedHashStringRef getData(size_t I) const {
- size_t Begin = Pieces[I].InputOff;
- size_t End =
- (Pieces.size() - 1 == I) ? data().size() : Pieces[I + 1].InputOff;
- return {toStringRef(data().slice(Begin, End - Begin)), Pieces[I].Hash};
+ llvm::CachedHashStringRef getData(size_t i) const {
+ size_t begin = pieces[i].inputOff;
+ size_t end =
+ (pieces.size() - 1 == i) ? data().size() : pieces[i + 1].inputOff;
+ return {toStringRef(data().slice(begin, end - begin)), pieces[i].hash};
}
// Returns the SectionPiece at a given input section offset.
- SectionPiece *getSectionPiece(uint64_t Offset);
- const SectionPiece *getSectionPiece(uint64_t Offset) const {
- return const_cast<MergeInputSection *>(this)->getSectionPiece(Offset);
+ SectionPiece *getSectionPiece(uint64_t offset);
+ const SectionPiece *getSectionPiece(uint64_t offset) const {
+ return const_cast<MergeInputSection *>(this)->getSectionPiece(offset);
}
SyntheticSection *getParent() const;
private:
- void splitStrings(ArrayRef<uint8_t> A, size_t Size);
- void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
+ void splitStrings(ArrayRef<uint8_t> a, size_t size);
+ void splitNonStrings(ArrayRef<uint8_t> a, size_t size);
};
struct EhSectionPiece {
- EhSectionPiece(size_t Off, InputSectionBase *Sec, uint32_t Size,
- unsigned FirstRelocation)
- : InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {}
+ EhSectionPiece(size_t off, InputSectionBase *sec, uint32_t size,
+ unsigned firstRelocation)
+ : inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {}
ArrayRef<uint8_t> data() {
- return {Sec->data().data() + this->InputOff, Size};
+ return {sec->data().data() + this->inputOff, size};
}
- size_t InputOff;
- ssize_t OutputOff = -1;
- InputSectionBase *Sec;
- uint32_t Size;
- unsigned FirstRelocation;
+ size_t inputOff;
+ ssize_t outputOff = -1;
+ InputSectionBase *sec;
+ uint32_t size;
+ unsigned firstRelocation;
};
// This corresponds to a .eh_frame section of an input file.
class EhInputSection : public InputSectionBase {
public:
template <class ELFT>
- EhInputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name);
- static bool classof(const SectionBase *S) { return S->kind() == EHFrame; }
+ EhInputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name);
+ static bool classof(const SectionBase *s) { return s->kind() == EHFrame; }
template <class ELFT> void split();
- template <class ELFT, class RelTy> void split(ArrayRef<RelTy> Rels);
+ template <class ELFT, class RelTy> void split(ArrayRef<RelTy> rels);
// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
- std::vector<EhSectionPiece> Pieces;
+ std::vector<EhSectionPiece> pieces;
SyntheticSection *getParent() const;
};
@@ -316,17 +324,17 @@ public:
// .eh_frame. It also includes the synthetic sections themselves.
class InputSection : public InputSectionBase {
public:
- InputSection(InputFile *F, uint64_t Flags, uint32_t Type, uint32_t Alignment,
- ArrayRef<uint8_t> Data, StringRef Name, Kind K = Regular);
+ InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t alignment,
+ ArrayRef<uint8_t> data, StringRef name, Kind k = Regular);
template <class ELFT>
- InputSection(ObjFile<ELFT> &F, const typename ELFT::Shdr &Header,
- StringRef Name);
+ InputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
+ StringRef name);
// Write this section to a mmap'ed file, assuming Buf is pointing to
// beginning of the output section.
- template <class ELFT> void writeTo(uint8_t *Buf);
+ template <class ELFT> void writeTo(uint8_t *buf);
- uint64_t getOffset(uint64_t Offset) const { return OutSecOff + Offset; }
+ uint64_t getOffset(uint64_t offset) const { return outSecOff + offset; }
OutputSection *getParent() const;
@@ -334,32 +342,32 @@ public:
// OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER
// sections. After assignAddresses is called, it represents the offset from
// the beginning of the output section this section was assigned to.
- uint64_t OutSecOff = 0;
+ uint64_t outSecOff = 0;
- static bool classof(const SectionBase *S);
+ static bool classof(const SectionBase *s);
InputSectionBase *getRelocatedSection() const;
template <class ELFT, class RelTy>
- void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
+ void relocateNonAlloc(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
// Used by ICF.
- uint32_t Class[2] = {0, 0};
+ uint32_t eqClass[2] = {0, 0};
// Called by ICF to merge two input sections.
- void replace(InputSection *Other);
+ void replace(InputSection *other);
- static InputSection Discarded;
+ static InputSection discarded;
private:
template <class ELFT, class RelTy>
- void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
+ void copyRelocations(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
- template <class ELFT> void copyShtGroup(uint8_t *Buf);
+ template <class ELFT> void copyShtGroup(uint8_t *buf);
};
// The list of all input sections.
-extern std::vector<InputSectionBase *> InputSections;
+extern std::vector<InputSectionBase *> inputSections;
} // namespace elf
diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp
index ca44581780e4..28d4bfe77c5d 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -1,9 +1,8 @@
//===- LTO.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -13,6 +12,7 @@
#include "LinkerScript.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/ADT/STLExtras.h"
@@ -47,134 +47,135 @@ using namespace lld::elf;
// Creates an empty file to store a list of object files for final
// linking of distributed ThinLTO.
-static std::unique_ptr<raw_fd_ostream> openFile(StringRef File) {
- std::error_code EC;
- auto Ret =
- llvm::make_unique<raw_fd_ostream>(File, EC, sys::fs::OpenFlags::F_None);
- if (EC) {
- error("cannot open " + File + ": " + EC.message());
+static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
+ std::error_code ec;
+ auto ret =
+ llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
+ if (ec) {
+ error("cannot open " + file + ": " + ec.message());
return nullptr;
}
- return Ret;
+ return ret;
}
-static std::string getThinLTOOutputFile(StringRef ModulePath) {
- return lto::getThinLTOOutputFile(ModulePath,
- Config->ThinLTOPrefixReplace.first,
- Config->ThinLTOPrefixReplace.second);
+static std::string getThinLTOOutputFile(StringRef modulePath) {
+ return lto::getThinLTOOutputFile(modulePath,
+ config->thinLTOPrefixReplace.first,
+ config->thinLTOPrefixReplace.second);
}
static lto::Config createConfig() {
- lto::Config C;
+ lto::Config c;
// LLD supports the new relocations and address-significance tables.
- C.Options = InitTargetOptionsFromCodeGenFlags();
- C.Options.RelaxELFRelocations = true;
- C.Options.EmitAddrsig = true;
+ c.Options = initTargetOptionsFromCodeGenFlags();
+ c.Options.RelaxELFRelocations = true;
+ c.Options.EmitAddrsig = true;
// Always emit a section per function/datum with LTO.
- C.Options.FunctionSections = true;
- C.Options.DataSections = true;
+ c.Options.FunctionSections = true;
+ c.Options.DataSections = true;
- if (Config->Relocatable)
- C.RelocModel = None;
- else if (Config->Pic)
- C.RelocModel = Reloc::PIC_;
+ if (config->relocatable)
+ c.RelocModel = None;
+ else if (config->isPic)
+ c.RelocModel = Reloc::PIC_;
else
- C.RelocModel = Reloc::Static;
+ c.RelocModel = Reloc::Static;
- C.CodeModel = GetCodeModelFromCMModel();
- C.DisableVerify = Config->DisableVerify;
- C.DiagHandler = diagnosticHandler;
- C.OptLevel = Config->LTOO;
- C.CPU = GetCPUStr();
- C.MAttrs = GetMAttrs();
+ c.CodeModel = getCodeModelFromCMModel();
+ c.DisableVerify = config->disableVerify;
+ c.DiagHandler = diagnosticHandler;
+ c.OptLevel = config->ltoo;
+ c.CPU = getCPUStr();
+ c.MAttrs = getMAttrs();
+ c.CGOptLevel = args::getCGOptLevel(config->ltoo);
// Set up a custom pipeline if we've been asked to.
- C.OptPipeline = Config->LTONewPmPasses;
- C.AAPipeline = Config->LTOAAPipeline;
+ c.OptPipeline = config->ltoNewPmPasses;
+ c.AAPipeline = config->ltoAAPipeline;
// Set up optimization remarks if we've been asked to.
- C.RemarksFilename = Config->OptRemarksFilename;
- C.RemarksWithHotness = Config->OptRemarksWithHotness;
-
- C.SampleProfile = Config->LTOSampleProfile;
- C.UseNewPM = Config->LTONewPassManager;
- C.DebugPassManager = Config->LTODebugPassManager;
- C.DwoDir = Config->DwoDir;
-
- if (Config->EmitLLVM) {
- C.PostInternalizeModuleHook = [](size_t Task, const Module &M) {
- if (std::unique_ptr<raw_fd_ostream> OS = openFile(Config->OutputFile))
- WriteBitcodeToFile(M, *OS, false);
+ c.RemarksFilename = config->optRemarksFilename;
+ c.RemarksPasses = config->optRemarksPasses;
+ c.RemarksWithHotness = config->optRemarksWithHotness;
+ c.RemarksFormat = config->optRemarksFormat;
+
+ c.SampleProfile = config->ltoSampleProfile;
+ c.UseNewPM = config->ltoNewPassManager;
+ c.DebugPassManager = config->ltoDebugPassManager;
+ c.DwoDir = config->dwoDir;
+
+ c.CSIRProfile = config->ltoCSProfileFile;
+ c.RunCSIRInstr = config->ltoCSProfileGenerate;
+
+ if (config->emitLLVM) {
+ c.PostInternalizeModuleHook = [](size_t task, const Module &m) {
+ if (std::unique_ptr<raw_fd_ostream> os = openFile(config->outputFile))
+ WriteBitcodeToFile(m, *os, false);
return false;
};
}
- if (Config->SaveTemps)
- checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
+ if (config->saveTemps)
+ checkError(c.addSaveTemps(config->outputFile.str() + ".",
/*UseInputModulePath*/ true));
- return C;
+ return c;
}
BitcodeCompiler::BitcodeCompiler() {
- // Initialize IndexFile.
- if (!Config->ThinLTOIndexOnlyArg.empty())
- IndexFile = openFile(Config->ThinLTOIndexOnlyArg);
-
- // Initialize LTOObj.
- lto::ThinBackend Backend;
- if (Config->ThinLTOIndexOnly) {
- auto OnIndexWrite = [&](StringRef S) { ThinIndices.erase(S); };
- Backend = lto::createWriteIndexesThinBackend(
- Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second,
- Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite);
- } else if (Config->ThinLTOJobs != -1U) {
- Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
+ // Initialize indexFile.
+ if (!config->thinLTOIndexOnlyArg.empty())
+ indexFile = openFile(config->thinLTOIndexOnlyArg);
+
+ // Initialize ltoObj.
+ lto::ThinBackend backend;
+ if (config->thinLTOIndexOnly) {
+ auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
+ backend = lto::createWriteIndexesThinBackend(
+ config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
+ } else if (config->thinLTOJobs != -1U) {
+ backend = lto::createInProcessThinBackend(config->thinLTOJobs);
}
- LTOObj = llvm::make_unique<lto::LTO>(createConfig(), Backend,
- Config->LTOPartitions);
+ ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
+ config->ltoPartitions);
- // Initialize UsedStartStop.
- for (Symbol *Sym : Symtab->getSymbols()) {
- StringRef S = Sym->getName();
- for (StringRef Prefix : {"__start_", "__stop_"})
- if (S.startswith(Prefix))
- UsedStartStop.insert(S.substr(Prefix.size()));
- }
+ // Initialize usedStartStop.
+ symtab->forEachSymbol([&](Symbol *sym) {
+ StringRef s = sym->getName();
+ for (StringRef prefix : {"__start_", "__stop_"})
+ if (s.startswith(prefix))
+ usedStartStop.insert(s.substr(prefix.size()));
+ });
}
BitcodeCompiler::~BitcodeCompiler() = default;
-static void undefine(Symbol *S) {
- replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_GLOBAL, STV_DEFAULT,
- S->Type);
-}
-
-void BitcodeCompiler::add(BitcodeFile &F) {
- lto::InputFile &Obj = *F.Obj;
- bool IsExec = !Config->Shared && !Config->Relocatable;
+void BitcodeCompiler::add(BitcodeFile &f) {
+ lto::InputFile &obj = *f.obj;
+ bool isExec = !config->shared && !config->relocatable;
- if (Config->ThinLTOIndexOnly)
- ThinIndices.insert(Obj.getName());
+ if (config->thinLTOIndexOnly)
+ thinIndices.insert(obj.getName());
- ArrayRef<Symbol *> Syms = F.getSymbols();
- ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols();
- std::vector<lto::SymbolResolution> Resols(Syms.size());
+ ArrayRef<Symbol *> syms = f.getSymbols();
+ ArrayRef<lto::InputFile::Symbol> objSyms = obj.symbols();
+ std::vector<lto::SymbolResolution> resols(syms.size());
// Provide a resolution to the LTO API for each symbol.
- for (size_t I = 0, E = Syms.size(); I != E; ++I) {
- Symbol *Sym = Syms[I];
- const lto::InputFile::Symbol &ObjSym = ObjSyms[I];
- lto::SymbolResolution &R = Resols[I];
+ for (size_t i = 0, e = syms.size(); i != e; ++i) {
+ Symbol *sym = syms[i];
+ const lto::InputFile::Symbol &objSym = objSyms[i];
+ lto::SymbolResolution &r = resols[i];
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
// reports two symbols for module ASM defined. Without this check, lld
// flags an undefined in IR with a definition in ASM as prevailing.
// Once IRObjectFile is fixed to report only one symbol this hack can
// be removed.
- R.Prevailing = !ObjSym.isUndefined() && Sym->File == &F;
+ r.Prevailing = !objSym.isUndefined() && sym->file == &f;
// We ask LTO to preserve following global symbols:
// 1) All symbols when doing relocatable link, so that them can be used
@@ -182,115 +183,121 @@ void BitcodeCompiler::add(BitcodeFile &F) {
// 2) Symbols that are used in regular objects.
// 3) C named sections if we have corresponding __start_/__stop_ symbol.
// 4) Symbols that are defined in bitcode files and used for dynamic linking.
- R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj ||
- (R.Prevailing && Sym->includeInDynsym()) ||
- UsedStartStop.count(ObjSym.getSectionName());
- const auto *DR = dyn_cast<Defined>(Sym);
- R.FinalDefinitionInLinkageUnit =
- (IsExec || Sym->Visibility != STV_DEFAULT) && DR &&
+ r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
+ (r.Prevailing && sym->includeInDynsym()) ||
+ usedStartStop.count(objSym.getSectionName());
+ const auto *dr = dyn_cast<Defined>(sym);
+ r.FinalDefinitionInLinkageUnit =
+ (isExec || sym->visibility != STV_DEFAULT) && dr &&
// Skip absolute symbols from ELF objects, otherwise PC-rel relocations
// will be generated by for them, triggering linker errors.
// Symbol section is always null for bitcode symbols, hence the check
// for isElf(). Skip linker script defined symbols as well: they have
// no File defined.
- !(DR->Section == nullptr && (!Sym->File || Sym->File->isElf()));
+ !(dr->section == nullptr && (!sym->file || sym->file->isElf()));
- if (R.Prevailing)
- undefine(Sym);
+ if (r.Prevailing)
+ sym->replace(Undefined{nullptr, sym->getName(), STB_GLOBAL, STV_DEFAULT,
+ sym->type});
// We tell LTO to not apply interprocedural optimization for wrapped
// (with --wrap) symbols because otherwise LTO would inline them while
// their values are still not final.
- R.LinkerRedefined = !Sym->CanInline;
+ r.LinkerRedefined = !sym->canInline;
}
- checkError(LTOObj->add(std::move(F.Obj), Resols));
+ checkError(ltoObj->add(std::move(f.obj), resols));
}
-static void createEmptyIndex(StringRef ModulePath) {
- std::string Path = replaceThinLTOSuffix(getThinLTOOutputFile(ModulePath));
- std::unique_ptr<raw_fd_ostream> OS = openFile(Path + ".thinlto.bc");
- if (!OS)
- return;
-
- ModuleSummaryIndex M(/*HaveGVs*/ false);
- M.setSkipModuleByDistributedBackend();
- WriteIndexToFile(M, *OS);
+// If LazyObjFile has not been added to link, emit empty index files.
+// This is needed because this is what GNU gold plugin does and we have a
+// distributed build system that depends on that behavior.
+static void thinLTOCreateEmptyIndexFiles() {
+ for (LazyObjFile *f : lazyObjFiles) {
+ if (!isBitcode(f->mb))
+ continue;
+ std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName()));
+ std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
+ if (!os)
+ continue;
- if (Config->ThinLTOEmitImportsFiles)
- openFile(Path + ".imports");
+ ModuleSummaryIndex m(/*HaveGVs*/ false);
+ m.setSkipModuleByDistributedBackend();
+ WriteIndexToFile(m, *os);
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
+ }
}
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile(s).
std::vector<InputFile *> BitcodeCompiler::compile() {
- unsigned MaxTasks = LTOObj->getMaxTasks();
- Buf.resize(MaxTasks);
- Files.resize(MaxTasks);
+ unsigned maxTasks = ltoObj->getMaxTasks();
+ buf.resize(maxTasks);
+ files.resize(maxTasks);
// The --thinlto-cache-dir option specifies the path to a directory in which
// to cache native object files for ThinLTO incremental builds. If a path was
// specified, configure LTO to use it as the cache directory.
- lto::NativeObjectCache Cache;
- if (!Config->ThinLTOCacheDir.empty())
- Cache = check(
- lto::localCache(Config->ThinLTOCacheDir,
- [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- Files[Task] = std::move(MB);
+ lto::NativeObjectCache cache;
+ if (!config->thinLTOCacheDir.empty())
+ cache = check(
+ lto::localCache(config->thinLTOCacheDir,
+ [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
}));
- checkError(LTOObj->run(
- [&](size_t Task) {
- return llvm::make_unique<lto::NativeObjectStream>(
- llvm::make_unique<raw_svector_ostream>(Buf[Task]));
- },
- Cache));
+ if (!bitcodeFiles.empty())
+ checkError(ltoObj->run(
+ [&](size_t task) {
+ return llvm::make_unique<lto::NativeObjectStream>(
+ llvm::make_unique<raw_svector_ostream>(buf[task]));
+ },
+ 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");
+ for (StringRef s : thinIndices) {
+ std::string path = getThinLTOOutputFile(s);
+ openFile(path + ".thinlto.bc");
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
}
- // If LazyObjFile has not been added to link, emit empty index files.
- // This is needed because this is what GNU gold plugin does and we have a
- // distributed build system that depends on that behavior.
- if (Config->ThinLTOIndexOnly) {
- for (LazyObjFile *F : LazyObjFiles)
- if (!F->AddedToLink && isBitcode(F->MB))
- createEmptyIndex(F->getName());
+ if (config->thinLTOIndexOnly) {
+ thinLTOCreateEmptyIndexFiles();
- if (!Config->LTOObjPath.empty())
- saveBuffer(Buf[0], Config->LTOObjPath);
+ if (!config->ltoObjPath.empty())
+ saveBuffer(buf[0], config->ltoObjPath);
// ThinLTO with index only option is required to generate only the index
// files. After that, we exit from linker and ThinLTO backend runs in a
// distributed environment.
- if (IndexFile)
- IndexFile->close();
+ if (indexFile)
+ indexFile->close();
return {};
}
- if (!Config->ThinLTOCacheDir.empty())
- pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);
+ if (!config->thinLTOCacheDir.empty())
+ pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy);
- std::vector<InputFile *> Ret;
- for (unsigned I = 0; I != MaxTasks; ++I) {
- if (Buf[I].empty())
- continue;
- if (Config->SaveTemps) {
- if (I == 0)
- saveBuffer(Buf[I], Config->OutputFile + ".lto.o");
- else
- saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o");
- }
- InputFile *Obj = createObjectFile(MemoryBufferRef(Buf[I], "lto.tmp"));
- Ret.push_back(Obj);
+ if (!config->ltoObjPath.empty()) {
+ saveBuffer(buf[0], config->ltoObjPath);
+ for (unsigned i = 1; i != maxTasks; ++i)
+ saveBuffer(buf[i], config->ltoObjPath + Twine(i));
}
- for (std::unique_ptr<MemoryBuffer> &File : Files)
- if (File)
- Ret.push_back(createObjectFile(*File));
- return Ret;
+ if (config->saveTemps) {
+ saveBuffer(buf[0], config->outputFile + ".lto.o");
+ for (unsigned i = 1; i != maxTasks; ++i)
+ saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
+ }
+
+ std::vector<InputFile *> ret;
+ for (unsigned i = 0; i != maxTasks; ++i)
+ if (!buf[i].empty())
+ ret.push_back(createObjectFile(MemoryBufferRef(buf[i], "lto.tmp")));
+
+ for (std::unique_ptr<MemoryBuffer> &file : files)
+ if (file)
+ ret.push_back(createObjectFile(*file));
+ return ret;
}
diff --git a/ELF/LTO.h b/ELF/LTO.h
index a190da3e5996..4cb42d84d919 100644
--- a/ELF/LTO.h
+++ b/ELF/LTO.h
@@ -1,9 +1,8 @@
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -46,16 +45,16 @@ public:
BitcodeCompiler();
~BitcodeCompiler();
- void add(BitcodeFile &F);
+ void add(BitcodeFile &f);
std::vector<InputFile *> compile();
private:
- std::unique_ptr<llvm::lto::LTO> LTOObj;
- std::vector<SmallString<0>> Buf;
- std::vector<std::unique_ptr<MemoryBuffer>> Files;
- llvm::DenseSet<StringRef> UsedStartStop;
- std::unique_ptr<llvm::raw_fd_ostream> IndexFile;
- llvm::DenseSet<StringRef> ThinIndices;
+ std::unique_ptr<llvm::lto::LTO> ltoObj;
+ std::vector<SmallString<0>> buf;
+ std::vector<std::unique_ptr<MemoryBuffer>> files;
+ llvm::DenseSet<StringRef> usedStartStop;
+ std::unique_ptr<llvm::raw_fd_ostream> indexFile;
+ llvm::DenseSet<StringRef> thinIndices;
};
} // namespace elf
} // namespace lld
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index fbc025416205..49e44d780476 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -1,9 +1,8 @@
//===- LinkerScript.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -47,26 +46,26 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-LinkerScript *elf::Script;
+LinkerScript *elf::script;
-static uint64_t getOutputSectionVA(SectionBase *InputSec, StringRef Loc) {
- if (OutputSection *OS = InputSec->getOutputSection())
- return OS->Addr;
- error(Loc + ": unable to evaluate expression: input section " +
- InputSec->Name + " has no output section assigned");
+static uint64_t getOutputSectionVA(SectionBase *inputSec, StringRef loc) {
+ if (OutputSection *os = inputSec->getOutputSection())
+ return os->addr;
+ error(loc + ": unable to evaluate expression: input section " +
+ inputSec->name + " has no output section assigned");
return 0;
}
uint64_t ExprValue::getValue() const {
- if (Sec)
- return alignTo(Sec->getOffset(Val) + getOutputSectionVA(Sec, Loc),
- Alignment);
- return alignTo(Val, Alignment);
+ if (sec)
+ return alignTo(sec->getOffset(val) + getOutputSectionVA(sec, loc),
+ alignment);
+ return alignTo(val, alignment);
}
uint64_t ExprValue::getSecAddr() const {
- if (Sec)
- return Sec->getOffset(0) + getOutputSectionVA(Sec, Loc);
+ if (sec)
+ return sec->getOffset(0) + getOutputSectionVA(sec, loc);
return 0;
}
@@ -74,106 +73,100 @@ uint64_t ExprValue::getSectionOffset() const {
// If the alignment is trivial, we don't have to compute the full
// value to know the offset. This allows this function to succeed in
// cases where the output section is not yet known.
- if (Alignment == 1 && (!Sec || !Sec->getOutputSection()))
- return Val;
+ if (alignment == 1 && (!sec || !sec->getOutputSection()))
+ return val;
return getValue() - getSecAddr();
}
-OutputSection *LinkerScript::createOutputSection(StringRef Name,
- StringRef Location) {
- OutputSection *&SecRef = NameToOutputSection[Name];
- OutputSection *Sec;
- if (SecRef && SecRef->Location.empty()) {
+OutputSection *LinkerScript::createOutputSection(StringRef name,
+ StringRef location) {
+ OutputSection *&secRef = nameToOutputSection[name];
+ OutputSection *sec;
+ if (secRef && secRef->location.empty()) {
// There was a forward reference.
- Sec = SecRef;
+ sec = secRef;
} else {
- Sec = make<OutputSection>(Name, SHT_NOBITS, 0);
- if (!SecRef)
- SecRef = Sec;
+ sec = make<OutputSection>(name, SHT_PROGBITS, 0);
+ if (!secRef)
+ secRef = sec;
}
- Sec->Location = Location;
- return Sec;
+ sec->location = location;
+ return sec;
}
-OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) {
- OutputSection *&CmdRef = NameToOutputSection[Name];
- if (!CmdRef)
- CmdRef = make<OutputSection>(Name, SHT_PROGBITS, 0);
- return CmdRef;
+OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) {
+ OutputSection *&cmdRef = nameToOutputSection[name];
+ if (!cmdRef)
+ cmdRef = make<OutputSection>(name, SHT_PROGBITS, 0);
+ return cmdRef;
}
// Expands the memory region by the specified size.
-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)
- error("section '" + SecName + "' will not fit in region '" + RegionName +
- "': overflowed by " + Twine(NewSize - MemRegion->Length) + " bytes");
+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)
+ error("section '" + secName + "' will not fit in region '" + regionName +
+ "': overflowed by " + Twine(newSize - memRegion->length) + " bytes");
}
-void LinkerScript::expandMemoryRegions(uint64_t Size) {
- if (Ctx->MemRegion)
- expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
- Ctx->OutSec->Name);
- // Only expand the LMARegion if it is different from MemRegion.
- if (Ctx->LMARegion && Ctx->MemRegion != Ctx->LMARegion)
- expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name,
- Ctx->OutSec->Name);
+void LinkerScript::expandMemoryRegions(uint64_t size) {
+ if (ctx->memRegion)
+ expandMemoryRegion(ctx->memRegion, size, ctx->memRegion->name,
+ ctx->outSec->name);
+ // Only expand the LMARegion if it is different from memRegion.
+ if (ctx->lmaRegion && ctx->memRegion != ctx->lmaRegion)
+ expandMemoryRegion(ctx->lmaRegion, size, ctx->lmaRegion->name,
+ ctx->outSec->name);
}
-void LinkerScript::expandOutputSection(uint64_t Size) {
- Ctx->OutSec->Size += Size;
- expandMemoryRegions(Size);
+void LinkerScript::expandOutputSection(uint64_t size) {
+ ctx->outSec->size += size;
+ expandMemoryRegions(size);
}
-void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
- uint64_t Val = E().getValue();
- if (Val < Dot && InSec)
- error(Loc + ": unable to move location counter backward for: " +
- Ctx->OutSec->Name);
+void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) {
+ uint64_t val = e().getValue();
+ if (val < dot && inSec)
+ error(loc + ": unable to move location counter backward for: " +
+ ctx->outSec->name);
// Update to location counter means update to section size.
- if (InSec)
- expandOutputSection(Val - Dot);
- else
- expandMemoryRegions(Val - Dot);
+ if (inSec)
+ expandOutputSection(val - dot);
- Dot = Val;
+ dot = val;
}
// Used for handling linker symbol assignments, for both finalizing
// their values and doing early declarations. Returns true if symbol
// should be defined from linker script.
-static bool shouldDefineSym(SymbolAssignment *Cmd) {
- if (Cmd->Name == ".")
+static bool shouldDefineSym(SymbolAssignment *cmd) {
+ if (cmd->name == ".")
return false;
- if (!Cmd->Provide)
+ if (!cmd->provide)
return true;
// If a symbol was in PROVIDE(), we need to define it only
// when it is a referenced undefined symbol.
- Symbol *B = Symtab->find(Cmd->Name);
- if (B && !B->isDefined())
+ Symbol *b = symtab->find(cmd->name);
+ if (b && !b->isDefined())
return true;
return false;
}
// This function is called from processSectionCommands,
// while we are fixing the output section layout.
-void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
- if (!shouldDefineSym(Cmd))
+void LinkerScript::addSymbol(SymbolAssignment *cmd) {
+ if (!shouldDefineSym(cmd))
return;
// Define a symbol.
- Symbol *Sym;
- uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
- /*CanOmitFromDynSym*/ false,
- /*File*/ nullptr);
- ExprValue Value = Cmd->Expression();
- SectionBase *Sec = Value.isAbsolute() ? nullptr : Value.Sec;
+ ExprValue value = cmd->expression();
+ SectionBase *sec = value.isAbsolute() ? nullptr : value.sec;
+ uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
// When this function is called, section addresses have not been
// fixed yet. So, we may or may not know the value of the RHS
@@ -186,68 +179,73 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
// We want to set symbol values early if we can. This allows us to
// use symbols as variables in linker scripts. Doing so allows us to
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
- uint64_t SymValue = Value.Sec ? 0 : Value.getValue();
+ uint64_t symValue = value.sec ? 0 : value.getValue();
+
+ Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, symValue,
+ 0, sec);
- replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
- STT_NOTYPE, SymValue, 0, Sec);
- Cmd->Sym = cast<Defined>(Sym);
+ Symbol *sym = symtab->insert(cmd->name);
+ sym->mergeProperties(New);
+ sym->replace(New);
+ cmd->sym = cast<Defined>(sym);
}
// This function is called from LinkerScript::declareSymbols.
// It creates a placeholder symbol if needed.
-static void declareSymbol(SymbolAssignment *Cmd) {
- if (!shouldDefineSym(Cmd))
+static void declareSymbol(SymbolAssignment *cmd) {
+ if (!shouldDefineSym(cmd))
return;
+ uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
+ Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
+ nullptr);
+
// We can't calculate final value right now.
- Symbol *Sym;
- uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
- /*CanOmitFromDynSym*/ false,
- /*File*/ nullptr);
- replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
- STT_NOTYPE, 0, 0, nullptr);
- Cmd->Sym = cast<Defined>(Sym);
- Cmd->Provide = false;
- Sym->ScriptDefined = true;
+ Symbol *sym = symtab->insert(cmd->name);
+ sym->mergeProperties(New);
+ sym->replace(New);
+
+ cmd->sym = cast<Defined>(sym);
+ cmd->provide = false;
+ sym->scriptDefined = true;
}
// This method is used to handle INSERT AFTER statement. Here we rebuild
// the list of script commands to mix sections inserted into.
void LinkerScript::processInsertCommands() {
- std::vector<BaseCommand *> V;
- auto Insert = [&](std::vector<BaseCommand *> &From) {
- V.insert(V.end(), From.begin(), From.end());
- From.clear();
+ 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 (BaseCommand *base : sectionCommands) {
+ if (auto *os = dyn_cast<OutputSection>(base)) {
+ insert(insertBeforeCommands[os->name]);
+ v.push_back(base);
+ insert(insertAfterCommands[os->name]);
continue;
}
- V.push_back(Base);
+ 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 +
+ 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);
+ sectionCommands = std::move(v);
}
// Symbols defined in script should not be inlined by LTO. At the same time
// we don't know their final values until late stages of link. Here we scan
// over symbol assignment commands and create placeholder symbols if needed.
void LinkerScript::declareSymbols() {
- assert(!Ctx);
- for (BaseCommand *Base : SectionCommands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- declareSymbol(Cmd);
+ assert(!ctx);
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ declareSymbol(cmd);
continue;
}
@@ -255,75 +253,75 @@ void LinkerScript::declareSymbols() {
// we can't say for sure if it is going to be included or not.
// Skip such sections for now. Improve the checks if we ever
// need symbols from that sections to be declared early.
- auto *Sec = cast<OutputSection>(Base);
- if (Sec->Constraint != ConstraintKind::NoConstraint)
+ auto *sec = cast<OutputSection>(base);
+ if (sec->constraint != ConstraintKind::NoConstraint)
continue;
- for (BaseCommand *Base2 : Sec->SectionCommands)
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base2))
- declareSymbol(Cmd);
+ for (BaseCommand *base2 : sec->sectionCommands)
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base2))
+ declareSymbol(cmd);
}
}
// This function is called from assignAddresses, while we are
// fixing the output section addresses. This function is supposed
// to set the final value for a given symbol assignment.
-void LinkerScript::assignSymbol(SymbolAssignment *Cmd, bool InSec) {
- if (Cmd->Name == ".") {
- setDot(Cmd->Expression, Cmd->Location, InSec);
+void LinkerScript::assignSymbol(SymbolAssignment *cmd, bool inSec) {
+ if (cmd->name == ".") {
+ setDot(cmd->expression, cmd->location, inSec);
return;
}
- if (!Cmd->Sym)
+ if (!cmd->sym)
return;
- ExprValue V = Cmd->Expression();
- if (V.isAbsolute()) {
- Cmd->Sym->Section = nullptr;
- Cmd->Sym->Value = V.getValue();
+ ExprValue v = cmd->expression();
+ if (v.isAbsolute()) {
+ cmd->sym->section = nullptr;
+ cmd->sym->value = v.getValue();
} else {
- Cmd->Sym->Section = V.Sec;
- Cmd->Sym->Value = V.getSectionOffset();
+ cmd->sym->section = v.sec;
+ cmd->sym->value = v.getSectionOffset();
}
}
-static std::string getFilename(InputFile *File) {
- if (!File)
+static std::string getFilename(InputFile *file) {
+ if (!file)
return "";
- if (File->ArchiveName.empty())
- return File->getName();
- return (File->ArchiveName + "(" + File->getName() + ")").str();
+ if (file->archiveName.empty())
+ return file->getName();
+ return (file->archiveName + "(" + file->getName() + ")").str();
}
-bool LinkerScript::shouldKeep(InputSectionBase *S) {
- if (KeptSections.empty())
+bool LinkerScript::shouldKeep(InputSectionBase *s) {
+ if (keptSections.empty())
return false;
- std::string Filename = getFilename(S->File);
- for (InputSectionDescription *ID : KeptSections)
- if (ID->FilePat.match(Filename))
- for (SectionPattern &P : ID->SectionPatterns)
- if (P.SectionPat.match(S->Name))
+ std::string filename = getFilename(s->file);
+ for (InputSectionDescription *id : keptSections)
+ if (id->filePat.match(filename))
+ for (SectionPattern &p : id->sectionPatterns)
+ if (p.sectionPat.match(s->name))
return true;
return false;
}
// A helper function for the SORT() command.
static std::function<bool(InputSectionBase *, InputSectionBase *)>
-getComparator(SortSectionPolicy K) {
- switch (K) {
+getComparator(SortSectionPolicy k) {
+ switch (k) {
case SortSectionPolicy::Alignment:
- return [](InputSectionBase *A, InputSectionBase *B) {
+ return [](InputSectionBase *a, InputSectionBase *b) {
// ">" is not a mistake. Sections with larger alignments are placed
// before sections with smaller alignments in order to reduce the
// amount of padding necessary. This is compatible with GNU.
- return A->Alignment > B->Alignment;
+ return a->alignment > b->alignment;
};
case SortSectionPolicy::Name:
- return [](InputSectionBase *A, InputSectionBase *B) {
- return A->Name < B->Name;
+ return [](InputSectionBase *a, InputSectionBase *b) {
+ return a->name < b->name;
};
case SortSectionPolicy::Priority:
- return [](InputSectionBase *A, InputSectionBase *B) {
- return getPriority(A->Name) < getPriority(B->Name);
+ return [](InputSectionBase *a, InputSectionBase *b) {
+ return getPriority(a->name) < getPriority(b->name);
};
default:
llvm_unreachable("unknown sort policy");
@@ -331,22 +329,22 @@ getComparator(SortSectionPolicy K) {
}
// A helper function for the SORT() command.
-static bool matchConstraints(ArrayRef<InputSection *> Sections,
- ConstraintKind Kind) {
- if (Kind == ConstraintKind::NoConstraint)
+static bool matchConstraints(ArrayRef<InputSection *> sections,
+ ConstraintKind kind) {
+ if (kind == ConstraintKind::NoConstraint)
return true;
- bool IsRW = llvm::any_of(
- Sections, [](InputSection *Sec) { return Sec->Flags & SHF_WRITE; });
+ bool isRW = llvm::any_of(
+ sections, [](InputSection *sec) { return sec->flags & SHF_WRITE; });
- return (IsRW && Kind == ConstraintKind::ReadWrite) ||
- (!IsRW && Kind == ConstraintKind::ReadOnly);
+ return (isRW && kind == ConstraintKind::ReadWrite) ||
+ (!isRW && kind == ConstraintKind::ReadOnly);
}
-static void sortSections(MutableArrayRef<InputSection *> Vec,
- SortSectionPolicy K) {
- if (K != SortSectionPolicy::Default && K != SortSectionPolicy::None)
- std::stable_sort(Vec.begin(), Vec.end(), getComparator(K));
+static void sortSections(MutableArrayRef<InputSection *> vec,
+ SortSectionPolicy k) {
+ if (k != SortSectionPolicy::Default && k != SortSectionPolicy::None)
+ llvm::stable_sort(vec, getComparator(k));
}
// Sort sections as instructed by SORT-family commands and --sort-section
@@ -360,29 +358,29 @@ static void sortSections(MutableArrayRef<InputSection *> Vec,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
-static void sortInputSections(MutableArrayRef<InputSection *> Vec,
- const SectionPattern &Pat) {
- if (Pat.SortOuter == SortSectionPolicy::None)
+static void sortInputSections(MutableArrayRef<InputSection *> vec,
+ const SectionPattern &pat) {
+ if (pat.sortOuter == SortSectionPolicy::None)
return;
- if (Pat.SortInner == SortSectionPolicy::Default)
- sortSections(Vec, Config->SortSection);
+ if (pat.sortInner == SortSectionPolicy::Default)
+ sortSections(vec, config->sortSection);
else
- sortSections(Vec, Pat.SortInner);
- sortSections(Vec, Pat.SortOuter);
+ sortSections(vec, pat.sortInner);
+ sortSections(vec, pat.sortOuter);
}
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSection *>
-LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
- std::vector<InputSection *> Ret;
+LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
+ std::vector<InputSection *> ret;
// Collects all sections that satisfy constraints of Cmd.
- for (const SectionPattern &Pat : Cmd->SectionPatterns) {
- size_t SizeBefore = Ret.size();
+ for (const SectionPattern &pat : cmd->sectionPatterns) {
+ size_t sizeBefore = ret.size();
- for (InputSectionBase *Sec : InputSections) {
- if (!Sec->Live || Sec->Assigned)
+ for (InputSectionBase *sec : inputSections) {
+ if (!sec->isLive() || sec->assigned)
continue;
// For -emit-relocs we have to ignore entries like
@@ -390,59 +388,59 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
// which are common because they are in the default bfd script.
// We do not ignore SHT_REL[A] linker-synthesized sections here because
// want to support scripts that do custom layout for them.
- if (auto *IS = dyn_cast<InputSection>(Sec))
- if (IS->getRelocatedSection())
+ if (auto *isec = dyn_cast<InputSection>(sec))
+ if (isec->getRelocatedSection())
continue;
- std::string Filename = getFilename(Sec->File);
- if (!Cmd->FilePat.match(Filename) ||
- Pat.ExcludedFilePat.match(Filename) ||
- !Pat.SectionPat.match(Sec->Name))
+ std::string filename = getFilename(sec->file);
+ if (!cmd->filePat.match(filename) ||
+ pat.excludedFilePat.match(filename) ||
+ !pat.sectionPat.match(sec->name))
continue;
// It is safe to assume that Sec is an InputSection
// because mergeable or EH input sections have already been
// handled and eliminated.
- Ret.push_back(cast<InputSection>(Sec));
- Sec->Assigned = true;
+ ret.push_back(cast<InputSection>(sec));
+ sec->assigned = true;
}
- sortInputSections(MutableArrayRef<InputSection *>(Ret).slice(SizeBefore),
- Pat);
+ sortInputSections(MutableArrayRef<InputSection *>(ret).slice(sizeBefore),
+ pat);
}
- return Ret;
+ return ret;
}
-void LinkerScript::discard(ArrayRef<InputSection *> V) {
- for (InputSection *S : V) {
- if (S == In.ShStrTab || S == In.RelaDyn || S == In.RelrDyn)
- error("discarding " + S->Name + " section is not allowed");
+void LinkerScript::discard(ArrayRef<InputSection *> v) {
+ for (InputSection *s : v) {
+ if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
+ error("discarding " + s->name + " section is not allowed");
// You can discard .hash and .gnu.hash sections by linker scripts. Since
// they are synthesized sections, we need to handle them differently than
// other regular sections.
- if (S == In.GnuHashTab)
- In.GnuHashTab = nullptr;
- if (S == In.HashTab)
- In.HashTab = nullptr;
-
- S->Assigned = false;
- S->Live = false;
- discard(S->DependentSections);
+ if (s == mainPart->gnuHashTab)
+ mainPart->gnuHashTab = nullptr;
+ if (s == mainPart->hashTab)
+ mainPart->hashTab = nullptr;
+
+ s->assigned = false;
+ s->markDead();
+ discard(s->dependentSections);
}
}
std::vector<InputSection *>
-LinkerScript::createInputSectionList(OutputSection &OutCmd) {
- std::vector<InputSection *> Ret;
+LinkerScript::createInputSectionList(OutputSection &outCmd) {
+ std::vector<InputSection *> ret;
- for (BaseCommand *Base : OutCmd.SectionCommands) {
- if (auto *Cmd = dyn_cast<InputSectionDescription>(Base)) {
- Cmd->Sections = computeInputSections(Cmd);
- Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
+ for (BaseCommand *base : outCmd.sectionCommands) {
+ if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
+ cmd->sections = computeInputSections(cmd);
+ ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end());
}
}
- return Ret;
+ return ret;
}
void LinkerScript::processSectionCommands() {
@@ -455,34 +453,34 @@ void LinkerScript::processSectionCommands() {
// To handle that, create a dummy aether section that fills the void before
// the linker scripts switches to another section. It has an index of one
// which will map to whatever the first actual section is.
- Aether = make<OutputSection>("", 0, SHF_ALLOC);
- Aether->SectionIndex = 1;
+ aether = make<OutputSection>("", 0, SHF_ALLOC);
+ aether->sectionIndex = 1;
// Ctx captures the local AddressState and makes it accessible deliberately.
// This is needed as there are some cases where we cannot just
// thread the current state through to a lambda function created by the
// script parser.
- auto Deleter = make_unique<AddressState>();
- Ctx = Deleter.get();
- Ctx->OutSec = Aether;
+ auto deleter = make_unique<AddressState>();
+ ctx = deleter.get();
+ ctx->outSec = aether;
- size_t I = 0;
+ size_t i = 0;
// Add input sections to output sections.
- for (BaseCommand *Base : SectionCommands) {
+ for (BaseCommand *base : sectionCommands) {
// Handle symbol assignments outside of any output section.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- addSymbol(Cmd);
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ addSymbol(cmd);
continue;
}
- if (auto *Sec = dyn_cast<OutputSection>(Base)) {
- std::vector<InputSection *> V = createInputSectionList(*Sec);
+ if (auto *sec = dyn_cast<OutputSection>(base)) {
+ std::vector<InputSection *> v = createInputSectionList(*sec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
- if (Sec->Name == "/DISCARD/") {
- discard(V);
- Sec->SectionCommands.clear();
+ if (sec->name == "/DISCARD/") {
+ discard(v);
+ sec->sectionCommands.clear();
continue;
}
@@ -493,60 +491,61 @@ void LinkerScript::processSectionCommands() {
//
// Because we'll iterate over SectionCommands many more times, the easy
// way to "make it as if it wasn't present" is to make it empty.
- if (!matchConstraints(V, Sec->Constraint)) {
- for (InputSectionBase *S : V)
- S->Assigned = false;
- Sec->SectionCommands.clear();
+ if (!matchConstraints(v, sec->constraint)) {
+ for (InputSectionBase *s : v)
+ s->assigned = false;
+ sec->sectionCommands.clear();
continue;
}
// A directive may contain symbol definitions like this:
// ".foo : { ...; bar = .; }". Handle them.
- for (BaseCommand *Base : Sec->SectionCommands)
- if (auto *OutCmd = dyn_cast<SymbolAssignment>(Base))
- addSymbol(OutCmd);
+ for (BaseCommand *base : sec->sectionCommands)
+ if (auto *outCmd = dyn_cast<SymbolAssignment>(base))
+ addSymbol(outCmd);
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
// is given, input sections are aligned to that value, whether the
// given value is larger or smaller than the original section alignment.
- if (Sec->SubalignExpr) {
- uint32_t Subalign = Sec->SubalignExpr().getValue();
- for (InputSectionBase *S : V)
- S->Alignment = Subalign;
+ if (sec->subalignExpr) {
+ uint32_t subalign = sec->subalignExpr().getValue();
+ for (InputSectionBase *s : v)
+ s->alignment = subalign;
}
// Add input sections to an output section.
- for (InputSection *S : V)
- Sec->addSection(S);
-
- Sec->SectionIndex = I++;
- if (Sec->Noload)
- Sec->Type = SHT_NOBITS;
- if (Sec->NonAlloc)
- Sec->Flags &= ~(uint64_t)SHF_ALLOC;
+ for (InputSection *s : v)
+ sec->addSection(s);
+
+ sec->sectionIndex = i++;
+ if (sec->noload)
+ sec->type = SHT_NOBITS;
+ if (sec->nonAlloc)
+ sec->flags &= ~(uint64_t)SHF_ALLOC;
}
}
- Ctx = nullptr;
+ ctx = nullptr;
}
-static OutputSection *findByName(ArrayRef<BaseCommand *> Vec,
- StringRef Name) {
- for (BaseCommand *Base : Vec)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- if (Sec->Name == Name)
- return Sec;
+static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
+ StringRef name) {
+ for (BaseCommand *base : vec)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ if (sec->name == name)
+ return sec;
return nullptr;
}
-static OutputSection *createSection(InputSectionBase *IS,
- StringRef OutsecName) {
- OutputSection *Sec = Script->createOutputSection(OutsecName, "<internal>");
- Sec->addSection(cast<InputSection>(IS));
- return Sec;
+static OutputSection *createSection(InputSectionBase *isec,
+ StringRef outsecName) {
+ OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
+ sec->addSection(cast<InputSection>(isec));
+ return sec;
}
-static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
- InputSectionBase *IS, StringRef OutsecName) {
+static OutputSection *
+addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
+ InputSectionBase *isec, StringRef outsecName) {
// Sections with SHT_GROUP or SHF_GROUP attributes reach here only when the -r
// option is given. A section with SHT_GROUP defines a "section group", and
// its members have SHF_GROUP attribute. Usually these flags have already been
@@ -554,8 +553,8 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
// However, for the -r option, we want to pass through all section groups
// as-is because adding/removing members or merging them with other groups
// change their semantics.
- if (IS->Type == SHT_GROUP || (IS->Flags & SHF_GROUP))
- return createSection(IS, OutsecName);
+ if (isec->type == SHT_GROUP || (isec->flags & SHF_GROUP))
+ return createSection(isec, outsecName);
// Imagine .zed : { *(.foo) *(.bar) } script. Both foo and bar may have
// relocation sections .rela.foo and .rela.bar for example. Most tools do
@@ -563,25 +562,25 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
// should combine these relocation sections into single output.
// We skip synthetic sections because it can be .rela.dyn/.rela.plt or any
// other REL[A] sections created by linker itself.
- if (!isa<SyntheticSection>(IS) &&
- (IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
- auto *Sec = cast<InputSection>(IS);
- OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
+ if (!isa<SyntheticSection>(isec) &&
+ (isec->type == SHT_REL || isec->type == SHT_RELA)) {
+ auto *sec = cast<InputSection>(isec);
+ OutputSection *out = sec->getRelocatedSection()->getOutputSection();
- if (Out->RelocationSection) {
- Out->RelocationSection->addSection(Sec);
+ if (out->relocationSection) {
+ out->relocationSection->addSection(sec);
return nullptr;
}
- Out->RelocationSection = createSection(IS, OutsecName);
- return Out->RelocationSection;
+ out->relocationSection = createSection(isec, outsecName);
+ return out->relocationSection;
}
// When control reaches here, mergeable sections have already been merged into
// synthetic sections. For relocatable case we want to create one output
// section per syntetic section so that they have a valid sh_entsize.
- if (Config->Relocatable && (IS->Flags & SHF_MERGE))
- return createSection(IS, OutsecName);
+ if (config->relocatable && (isec->flags & SHF_MERGE))
+ return createSection(isec, outsecName);
// The ELF spec just says
// ----------------------------------------------------------------
@@ -625,159 +624,161 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
//
// Given the above issues, we instead merge sections by name and error on
// incompatible types and flags.
- OutputSection *&Sec = Map[OutsecName];
- if (Sec) {
- Sec->addSection(cast<InputSection>(IS));
+ TinyPtrVector<OutputSection *> &v = map[outsecName];
+ for (OutputSection *sec : v) {
+ if (sec->partition != isec->partition)
+ continue;
+ sec->addSection(cast<InputSection>(isec));
return nullptr;
}
- Sec = createSection(IS, OutsecName);
- return Sec;
+ OutputSection *sec = createSection(isec, outsecName);
+ v.push_back(sec);
+ return sec;
}
// Add sections that didn't match any sections command.
void LinkerScript::addOrphanSections() {
- unsigned End = SectionCommands.size();
- StringMap<OutputSection *> Map;
- std::vector<OutputSection *> V;
+ StringMap<TinyPtrVector<OutputSection *>> map;
+ std::vector<OutputSection *> v;
- auto Add = [&](InputSectionBase *S) {
- if (!S->Live || S->Parent)
+ auto add = [&](InputSectionBase *s) {
+ if (!s->isLive() || s->parent)
return;
- StringRef Name = getOutputSectionName(S);
+ 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 + "'");
+ 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 + "'");
- if (OutputSection *Sec =
- findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
- Sec->addSection(cast<InputSection>(S));
+ if (OutputSection *sec = findByName(sectionCommands, name)) {
+ sec->addSection(cast<InputSection>(s));
return;
}
- if (OutputSection *OS = addInputSec(Map, S, Name))
- V.push_back(OS);
- assert(S->getOutputSection()->SectionIndex == UINT32_MAX);
+ if (OutputSection *os = addInputSec(map, s, name))
+ v.push_back(os);
+ assert(s->getOutputSection()->sectionIndex == UINT32_MAX);
};
// For futher --emit-reloc handling code we need target output section
// to be created before we create relocation output section, so we want
// to create target sections first. We do not want priority handling
// for synthetic sections because them are special.
- for (InputSectionBase *IS : InputSections) {
- if (auto *Sec = dyn_cast<InputSection>(IS))
- if (InputSectionBase *Rel = Sec->getRelocatedSection())
- if (auto *RelIS = dyn_cast_or_null<InputSectionBase>(Rel->Parent))
- Add(RelIS);
- Add(IS);
+ for (InputSectionBase *isec : inputSections) {
+ if (auto *sec = dyn_cast<InputSection>(isec))
+ if (InputSectionBase *rel = sec->getRelocatedSection())
+ if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent))
+ add(relIS);
+ add(isec);
}
// If no SECTIONS command was given, we should insert sections commands
// before others, so that we can handle scripts which refers them,
// for example: "foo = ABSOLUTE(ADDR(.text)));".
// When SECTIONS command is present we just add all orphans to the end.
- if (HasSectionsCommand)
- SectionCommands.insert(SectionCommands.end(), V.begin(), V.end());
+ if (hasSectionsCommand)
+ sectionCommands.insert(sectionCommands.end(), v.begin(), v.end());
else
- SectionCommands.insert(SectionCommands.begin(), V.begin(), V.end());
+ sectionCommands.insert(sectionCommands.begin(), v.begin(), v.end());
}
-uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) {
- bool IsTbss =
- (Ctx->OutSec->Flags & SHF_TLS) && Ctx->OutSec->Type == SHT_NOBITS;
- uint64_t Start = IsTbss ? Dot + Ctx->ThreadBssOffset : Dot;
- Start = alignTo(Start, Alignment);
- uint64_t End = Start + Size;
+uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) {
+ bool isTbss =
+ (ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS;
+ uint64_t start = isTbss ? dot + ctx->threadBssOffset : dot;
+ start = alignTo(start, alignment);
+ uint64_t end = start + size;
- if (IsTbss)
- Ctx->ThreadBssOffset = End - Dot;
+ if (isTbss)
+ ctx->threadBssOffset = end - dot;
else
- Dot = End;
- return End;
+ dot = end;
+ return end;
}
-void LinkerScript::output(InputSection *S) {
- assert(Ctx->OutSec == S->getParent());
- uint64_t Before = advance(0, 1);
- uint64_t Pos = advance(S->getSize(), S->Alignment);
- S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr;
+void LinkerScript::output(InputSection *s) {
+ assert(ctx->outSec == s->getParent());
+ uint64_t before = advance(0, 1);
+ uint64_t pos = advance(s->getSize(), s->alignment);
+ s->outSecOff = pos - s->getSize() - ctx->outSec->addr;
// Update output section size after adding each section. This is so that
// SIZEOF works correctly in the case below:
// .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
- expandOutputSection(Pos - Before);
+ expandOutputSection(pos - before);
}
-void LinkerScript::switchTo(OutputSection *Sec) {
- Ctx->OutSec = Sec;
+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 before = advance(0, 1);
+ ctx->outSec->addr = advance(0, ctx->outSec->alignment);
+ expandMemoryRegions(ctx->outSec->addr - before);
}
// This function searches for a memory region to place the given output
// section in. If found, a pointer to the appropriate memory region is
// returned. Otherwise, a nullptr is returned.
-MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
+MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *sec) {
// If a memory region name was specified in the output section command,
// then try to find that region first.
- if (!Sec->MemoryRegionName.empty()) {
- if (MemoryRegion *M = MemoryRegions.lookup(Sec->MemoryRegionName))
- return M;
- error("memory region '" + Sec->MemoryRegionName + "' not declared");
+ if (!sec->memoryRegionName.empty()) {
+ if (MemoryRegion *m = memoryRegions.lookup(sec->memoryRegionName))
+ return m;
+ error("memory region '" + sec->memoryRegionName + "' not declared");
return nullptr;
}
// If at least one memory region is defined, all sections must
// belong to some memory region. Otherwise, we don't need to do
// anything for memory regions.
- if (MemoryRegions.empty())
+ if (memoryRegions.empty())
return nullptr;
// See if a region can be found by matching section flags.
- for (auto &Pair : MemoryRegions) {
- MemoryRegion *M = Pair.second;
- if ((M->Flags & Sec->Flags) && (M->NegFlags & Sec->Flags) == 0)
- return M;
+ for (auto &pair : memoryRegions) {
+ MemoryRegion *m = pair.second;
+ if ((m->flags & sec->flags) && (m->negFlags & sec->flags) == 0)
+ return m;
}
// Otherwise, no suitable region was found.
- if (Sec->Flags & SHF_ALLOC)
- error("no memory region specified for section '" + Sec->Name + "'");
+ if (sec->flags & SHF_ALLOC)
+ error("no memory region specified for section '" + sec->name + "'");
return nullptr;
}
-static OutputSection *findFirstSection(PhdrEntry *Load) {
- for (OutputSection *Sec : OutputSections)
- if (Sec->PtLoad == Load)
- return Sec;
+static OutputSection *findFirstSection(PhdrEntry *load) {
+ for (OutputSection *sec : outputSections)
+ if (sec->ptLoad == load)
+ return sec;
return nullptr;
}
// This function assigns offsets to input sections and an output section
// for a single sections command (e.g. ".text { *(.text); }").
-void LinkerScript::assignOffsets(OutputSection *Sec) {
- if (!(Sec->Flags & SHF_ALLOC))
- Dot = 0;
- else if (Sec->AddrExpr)
- setDot(Sec->AddrExpr, Sec->Location, false);
+void LinkerScript::assignOffsets(OutputSection *sec) {
+ if (!(sec->flags & SHF_ALLOC))
+ dot = 0;
- Ctx->MemRegion = Sec->MemRegion;
- Ctx->LMARegion = Sec->LMARegion;
- if (Ctx->MemRegion)
- Dot = Ctx->MemRegion->CurPos;
+ ctx->memRegion = sec->memRegion;
+ ctx->lmaRegion = sec->lmaRegion;
+ if (ctx->memRegion)
+ dot = ctx->memRegion->curPos;
- switchTo(Sec);
+ if ((sec->flags & SHF_ALLOC) && sec->addrExpr)
+ setDot(sec->addrExpr, sec->location, false);
- if (Sec->LMAExpr)
- Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot;
+ switchTo(sec);
- if (MemoryRegion *MR = Sec->LMARegion)
- Ctx->LMAOffset = MR->CurPos - Dot;
+ if (sec->lmaExpr)
+ ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
+
+ 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
@@ -785,62 +786,71 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
// 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.
- if (PhdrEntry *L = Ctx->OutSec->PtLoad)
- if (Sec == findFirstSection(L))
- L->LMAOffset = Ctx->LMAOffset;
+ if (PhdrEntry *l = ctx->outSec->ptLoad)
+ if (sec == findFirstSection(l))
+ l->lmaOffset = ctx->lmaOffset;
// We can call this method multiple times during the creation of
// thunks and want to start over calculation each time.
- Sec->Size = 0;
+ sec->size = 0;
// We visited SectionsCommands from processSectionCommands to
// layout sections. Now, we visit SectionsCommands again to fix
// section offsets.
- for (BaseCommand *Base : Sec->SectionCommands) {
+ for (BaseCommand *base : sec->sectionCommands) {
// This handles the assignments to symbol or to the dot.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- Cmd->Addr = Dot;
- assignSymbol(Cmd, true);
- Cmd->Size = Dot - Cmd->Addr;
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ cmd->addr = dot;
+ assignSymbol(cmd, true);
+ cmd->size = dot - cmd->addr;
continue;
}
// Handle BYTE(), SHORT(), LONG(), or QUAD().
- if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
- Cmd->Offset = Dot - Ctx->OutSec->Addr;
- Dot += Cmd->Size;
- expandOutputSection(Cmd->Size);
+ if (auto *cmd = dyn_cast<ByteCommand>(base)) {
+ cmd->offset = dot - ctx->outSec->addr;
+ dot += cmd->size;
+ expandOutputSection(cmd->size);
continue;
}
// Handle a single input section description command.
// It calculates and assigns the offsets for each section and also
// updates the output section size.
- for (InputSection *Sec : cast<InputSectionDescription>(Base)->Sections)
- output(Sec);
+ for (InputSection *sec : cast<InputSectionDescription>(base)->sections)
+ output(sec);
}
}
-static bool isDiscardable(OutputSection &Sec) {
+static bool isDiscardable(OutputSection &sec) {
+ if (sec.name == "/DISCARD/")
+ return true;
+
// We do not remove empty sections that are explicitly
// assigned to any segment.
- if (!Sec.Phdrs.empty())
+ if (!sec.phdrs.empty())
return false;
- // We do not want to remove sections that reference symbols in address and
- // other expressions. We add script symbols as undefined, and want to ensure
- // all of them are defined in the output, hence have to keep them.
- if (Sec.ExpressionsUseSymbols)
+ // We do not want to remove OutputSections with expressions that reference
+ // symbols even if the OutputSection is empty. We want to ensure that the
+ // expressions can be evaluated and report an error if they cannot.
+ if (sec.expressionsUseSymbols)
return false;
- for (BaseCommand *Base : Sec.SectionCommands) {
- if (auto Cmd = dyn_cast<SymbolAssignment>(Base))
+ // OutputSections may be referenced by name in ADDR and LOADADDR expressions,
+ // as an empty Section can has a valid VMA and LMA we keep the OutputSection
+ // to maintain the integrity of the other Expression.
+ if (sec.usedInExpression)
+ return false;
+
+ for (BaseCommand *base : sec.sectionCommands) {
+ if (auto cmd = dyn_cast<SymbolAssignment>(base))
// Don't create empty output sections just for unreferenced PROVIDE
// symbols.
- if (Cmd->Name != "." && !Cmd->Sym)
+ if (cmd->name != "." && !cmd->sym)
continue;
- if (!isa<InputSectionDescription>(*Base))
+ if (!isa<InputSectionDescription>(*base))
return false;
}
return true;
@@ -867,33 +877,35 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// The other option is to pick flags that minimize the impact the section
// will have on the rest of the linker. That is why we copy the flags from
// the previous sections. Only a few flags are needed to keep the impact low.
- uint64_t Flags = SHF_ALLOC;
+ uint64_t flags = SHF_ALLOC;
- for (BaseCommand *&Cmd : SectionCommands) {
- auto *Sec = dyn_cast<OutputSection>(Cmd);
- if (!Sec)
+ for (BaseCommand *&cmd : sectionCommands) {
+ auto *sec = dyn_cast<OutputSection>(cmd);
+ if (!sec)
continue;
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
- if (Sec->AlignExpr)
- Sec->Alignment =
- std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue());
+ if (sec->alignExpr)
+ sec->alignment =
+ std::max<uint32_t>(sec->alignment, sec->alignExpr().getValue());
- // A live output section means that some input section was added to it. It
- // might have been removed (if it was empty synthetic section), but we at
- // least know the flags.
- if (Sec->Live)
- Flags = Sec->Flags;
+ // The input section might have been removed (if it was an empty synthetic
+ // section), but we at least know the flags.
+ if (sec->hasInputSections)
+ flags = sec->flags;
// We do not want to keep any special flags for output section
// in case it is empty.
- bool IsEmpty = getInputSections(Sec).empty();
- if (IsEmpty)
- Sec->Flags = Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
-
- if (IsEmpty && isDiscardable(*Sec)) {
- Sec->Live = false;
- Cmd = nullptr;
+ bool isEmpty = getInputSections(sec).empty();
+ if (isEmpty)
+ sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) |
+ SHF_WRITE | SHF_EXECINSTR);
+
+ if (isEmpty && isDiscardable(*sec)) {
+ sec->markDead();
+ cmd = nullptr;
+ } else if (!sec->isLive()) {
+ sec->markLive();
}
}
@@ -903,20 +915,20 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// clutter the output.
// We instead remove trivially empty sections. The bfd linker seems even
// more aggressive at removing them.
- llvm::erase_if(SectionCommands, [&](BaseCommand *Base) { return !Base; });
+ llvm::erase_if(sectionCommands, [&](BaseCommand *base) { return !base; });
}
void LinkerScript::adjustSectionsAfterSorting() {
// Try and find an appropriate memory region to assign offsets in.
- for (BaseCommand *Base : SectionCommands) {
- if (auto *Sec = dyn_cast<OutputSection>(Base)) {
- if (!Sec->LMARegionName.empty()) {
- if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName))
- Sec->LMARegion = M;
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *sec = dyn_cast<OutputSection>(base)) {
+ if (!sec->lmaRegionName.empty()) {
+ if (MemoryRegion *m = memoryRegions.lookup(sec->lmaRegionName))
+ sec->lmaRegion = m;
else
- error("memory region '" + Sec->LMARegionName + "' not declared");
+ error("memory region '" + sec->lmaRegionName + "' not declared");
}
- Sec->MemRegion = findMemoryRegion(Sec);
+ sec->memRegion = findMemoryRegion(sec);
}
}
@@ -926,38 +938,38 @@ void LinkerScript::adjustSectionsAfterSorting() {
// Below is an example of such linker script:
// PHDRS { seg PT_LOAD; }
// SECTIONS { .aaa : { *(.aaa) } }
- std::vector<StringRef> DefPhdrs;
- auto FirstPtLoad = llvm::find_if(PhdrsCommands, [](const PhdrsCommand &Cmd) {
- return Cmd.Type == PT_LOAD;
+ std::vector<StringRef> defPhdrs;
+ auto firstPtLoad = llvm::find_if(phdrsCommands, [](const PhdrsCommand &cmd) {
+ return cmd.type == PT_LOAD;
});
- if (FirstPtLoad != PhdrsCommands.end())
- DefPhdrs.push_back(FirstPtLoad->Name);
+ if (firstPtLoad != phdrsCommands.end())
+ defPhdrs.push_back(firstPtLoad->name);
// Walk the commands and propagate the program headers to commands that don't
// explicitly specify them.
- for (BaseCommand *Base : SectionCommands) {
- auto *Sec = dyn_cast<OutputSection>(Base);
- if (!Sec)
+ for (BaseCommand *base : sectionCommands) {
+ auto *sec = dyn_cast<OutputSection>(base);
+ if (!sec)
continue;
- if (Sec->Phdrs.empty()) {
+ if (sec->phdrs.empty()) {
// To match the bfd linker script behaviour, only propagate program
// headers to sections that are allocated.
- if (Sec->Flags & SHF_ALLOC)
- Sec->Phdrs = DefPhdrs;
+ if (sec->flags & SHF_ALLOC)
+ sec->phdrs = defPhdrs;
} else {
- DefPhdrs = Sec->Phdrs;
+ defPhdrs = sec->phdrs;
}
}
}
-static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
+static uint64_t computeBase(uint64_t min, bool allocateHeaders) {
// If there is no SECTIONS or if the linkerscript is explicit about program
// headers, do our best to allocate them.
- if (!Script->HasSectionsCommand || AllocateHeaders)
+ if (!script->hasSectionsCommand || allocateHeaders)
return 0;
// Otherwise only allocate program headers if that would not add a page.
- return alignDown(Min, Config->MaxPageSize);
+ return alignDown(min, config->maxPageSize);
}
// Try to find an address for the file and program headers output sections,
@@ -971,118 +983,120 @@ static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
//
// If there isn't enough space for these sections, we'll remove them from the
// PT_LOAD segment, and we'll also remove the PT_PHDR segment.
-void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
- uint64_t Min = std::numeric_limits<uint64_t>::max();
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_ALLOC)
- Min = std::min<uint64_t>(Min, Sec->Addr);
-
- auto It = llvm::find_if(
- Phdrs, [](const PhdrEntry *E) { return E->p_type == PT_LOAD; });
- if (It == Phdrs.end())
+void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
+ uint64_t min = std::numeric_limits<uint64_t>::max();
+ for (OutputSection *sec : outputSections)
+ if (sec->flags & SHF_ALLOC)
+ min = std::min<uint64_t>(min, sec->addr);
+
+ auto it = llvm::find_if(
+ phdrs, [](const PhdrEntry *e) { return e->p_type == PT_LOAD; });
+ if (it == phdrs.end())
return;
- PhdrEntry *FirstPTLoad = *It;
+ PhdrEntry *firstPTLoad = *it;
- bool HasExplicitHeaders =
- llvm::any_of(PhdrsCommands, [](const PhdrsCommand &Cmd) {
- return Cmd.HasPhdrs || Cmd.HasFilehdr;
+ bool hasExplicitHeaders =
+ llvm::any_of(phdrsCommands, [](const PhdrsCommand &cmd) {
+ return cmd.hasPhdrs || cmd.hasFilehdr;
});
- uint64_t HeaderSize = getHeaderSize();
- if (HeaderSize <= Min - computeBase(Min, HasExplicitHeaders)) {
- Min = alignDown(Min - HeaderSize, Config->MaxPageSize);
- Out::ElfHeader->Addr = Min;
- Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size;
+ bool paged = !config->omagic && !config->nmagic;
+ uint64_t headerSize = getHeaderSize();
+ if ((paged || hasExplicitHeaders) &&
+ headerSize <= min - computeBase(min, hasExplicitHeaders)) {
+ min = alignDown(min - headerSize, config->maxPageSize);
+ Out::elfHeader->addr = min;
+ Out::programHeaders->addr = min + Out::elfHeader->size;
return;
}
// Error if we were explicitly asked to allocate headers.
- if (HasExplicitHeaders)
+ if (hasExplicitHeaders)
error("could not allocate headers");
- Out::ElfHeader->PtLoad = nullptr;
- Out::ProgramHeaders->PtLoad = nullptr;
- FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad);
+ Out::elfHeader->ptLoad = nullptr;
+ Out::programHeaders->ptLoad = nullptr;
+ firstPTLoad->firstSec = findFirstSection(firstPTLoad);
- llvm::erase_if(Phdrs,
- [](const PhdrEntry *E) { return E->p_type == PT_PHDR; });
+ llvm::erase_if(phdrs,
+ [](const PhdrEntry *e) { return e->p_type == PT_PHDR; });
}
LinkerScript::AddressState::AddressState() {
- for (auto &MRI : Script->MemoryRegions) {
- MemoryRegion *MR = MRI.second;
- MR->CurPos = MR->Origin;
+ for (auto &mri : script->memoryRegions) {
+ MemoryRegion *mr = mri.second;
+ mr->curPos = mr->origin;
}
}
static uint64_t getInitialDot() {
// By default linker scripts use an initial value of 0 for '.',
// but prefer -image-base if set.
- if (Script->HasSectionsCommand)
- return Config->ImageBase ? *Config->ImageBase : 0;
+ if (script->hasSectionsCommand)
+ return config->imageBase ? *config->imageBase : 0;
- uint64_t StartAddr = UINT64_MAX;
- // The Sections with -T<section> have been sorted in order of ascending
- // address. We must lower StartAddr if the lowest -T<section address> as
+ uint64_t startAddr = UINT64_MAX;
+ // The sections with -T<section> have been sorted in order of ascending
+ // address. We must lower startAddr if the lowest -T<section address> as
// calls to setDot() must be monotonically increasing.
- for (auto &KV : Config->SectionStartMap)
- StartAddr = std::min(StartAddr, KV.second);
- return std::min(StartAddr, Target->getImageBase() + elf::getHeaderSize());
+ for (auto &kv : config->sectionStartMap)
+ startAddr = std::min(startAddr, kv.second);
+ return std::min(startAddr, target->getImageBase() + elf::getHeaderSize());
}
// Here we assign addresses as instructed by linker script SECTIONS
// sub-commands. Doing that allows us to use final VA values, so here
// we also handle rest commands like symbol assignments and ASSERTs.
void LinkerScript::assignAddresses() {
- Dot = getInitialDot();
-
- auto Deleter = make_unique<AddressState>();
- Ctx = Deleter.get();
- ErrorOnMissingSection = true;
- switchTo(Aether);
-
- for (BaseCommand *Base : SectionCommands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- Cmd->Addr = Dot;
- assignSymbol(Cmd, false);
- Cmd->Size = Dot - Cmd->Addr;
+ dot = getInitialDot();
+
+ auto deleter = make_unique<AddressState>();
+ ctx = deleter.get();
+ errorOnMissingSection = true;
+ switchTo(aether);
+
+ for (BaseCommand *base : sectionCommands) {
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ cmd->addr = dot;
+ assignSymbol(cmd, false);
+ cmd->size = dot - cmd->addr;
continue;
}
- assignOffsets(cast<OutputSection>(Base));
+ assignOffsets(cast<OutputSection>(base));
}
- Ctx = nullptr;
+ ctx = nullptr;
}
// Creates program headers as instructed by PHDRS linker script command.
std::vector<PhdrEntry *> LinkerScript::createPhdrs() {
- std::vector<PhdrEntry *> Ret;
+ std::vector<PhdrEntry *> ret;
// Process PHDRS and FILEHDR keywords because they are not
// real output sections and cannot be added in the following loop.
- for (const PhdrsCommand &Cmd : PhdrsCommands) {
- PhdrEntry *Phdr = make<PhdrEntry>(Cmd.Type, Cmd.Flags ? *Cmd.Flags : PF_R);
+ for (const PhdrsCommand &cmd : phdrsCommands) {
+ PhdrEntry *phdr = make<PhdrEntry>(cmd.type, cmd.flags ? *cmd.flags : PF_R);
- if (Cmd.HasFilehdr)
- Phdr->add(Out::ElfHeader);
- if (Cmd.HasPhdrs)
- Phdr->add(Out::ProgramHeaders);
+ if (cmd.hasFilehdr)
+ phdr->add(Out::elfHeader);
+ if (cmd.hasPhdrs)
+ phdr->add(Out::programHeaders);
- if (Cmd.LMAExpr) {
- Phdr->p_paddr = Cmd.LMAExpr().getValue();
- Phdr->HasLMA = true;
+ if (cmd.lmaExpr) {
+ phdr->p_paddr = cmd.lmaExpr().getValue();
+ phdr->hasLMA = true;
}
- Ret.push_back(Phdr);
+ ret.push_back(phdr);
}
// Add output sections to program headers.
- for (OutputSection *Sec : OutputSections) {
+ for (OutputSection *sec : outputSections) {
// Assign headers specified by linker script
- for (size_t Id : getPhdrIndices(Sec)) {
- Ret[Id]->add(Sec);
- if (!PhdrsCommands[Id].Flags.hasValue())
- Ret[Id]->p_flags |= Sec->getPhdrFlags();
+ for (size_t id : getPhdrIndices(sec)) {
+ ret[id]->add(sec);
+ if (!phdrsCommands[id].flags.hasValue())
+ ret[id]->p_flags |= sec->getPhdrFlags();
}
}
- return Ret;
+ return ret;
}
// Returns true if we should emit an .interp section.
@@ -1091,54 +1105,54 @@ std::vector<PhdrEntry *> LinkerScript::createPhdrs() {
// no PT_INTERP is there, there's no place to emit an
// .interp, so we don't do that in that case.
bool LinkerScript::needsInterpSection() {
- if (PhdrsCommands.empty())
+ if (phdrsCommands.empty())
return true;
- for (PhdrsCommand &Cmd : PhdrsCommands)
- if (Cmd.Type == PT_INTERP)
+ for (PhdrsCommand &cmd : phdrsCommands)
+ if (cmd.type == PT_INTERP)
return true;
return false;
}
-ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) {
- if (Name == ".") {
- if (Ctx)
- return {Ctx->OutSec, false, Dot - Ctx->OutSec->Addr, Loc};
- error(Loc + ": unable to get location counter value");
+ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) {
+ if (name == ".") {
+ if (ctx)
+ return {ctx->outSec, false, dot - ctx->outSec->addr, loc};
+ error(loc + ": unable to get location counter value");
return 0;
}
- if (Symbol *Sym = Symtab->find(Name)) {
- if (auto *DS = dyn_cast<Defined>(Sym))
- return {DS->Section, false, DS->Value, Loc};
- if (isa<SharedSymbol>(Sym))
- if (!ErrorOnMissingSection)
- return {nullptr, false, 0, Loc};
+ if (Symbol *sym = symtab->find(name)) {
+ if (auto *ds = dyn_cast<Defined>(sym))
+ return {ds->section, false, ds->value, loc};
+ if (isa<SharedSymbol>(sym))
+ if (!errorOnMissingSection)
+ return {nullptr, false, 0, loc};
}
- error(Loc + ": symbol not found: " + Name);
+ error(loc + ": symbol not found: " + name);
return 0;
}
// Returns the index of the segment named Name.
-static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> Vec,
- StringRef Name) {
- for (size_t I = 0; I < Vec.size(); ++I)
- if (Vec[I].Name == Name)
- return I;
+static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> vec,
+ StringRef name) {
+ for (size_t i = 0; i < vec.size(); ++i)
+ if (vec[i].name == name)
+ return i;
return None;
}
// Returns indices of ELF headers containing specific section. Each index is a
// zero based number of ELF header listed within PHDRS {} script block.
-std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *Cmd) {
- std::vector<size_t> Ret;
-
- for (StringRef S : Cmd->Phdrs) {
- if (Optional<size_t> Idx = getPhdrIndex(PhdrsCommands, S))
- Ret.push_back(*Idx);
- else if (S != "NONE")
- error(Cmd->Location + ": section header '" + S +
+std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
+ std::vector<size_t> ret;
+
+ for (StringRef s : cmd->phdrs) {
+ if (Optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
+ ret.push_back(*idx);
+ else if (s != "NONE")
+ error(cmd->location + ": section header '" + s +
"' is not listed in PHDRS");
}
- return Ret;
+ return ret;
}
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 51161981efc8..9e9c08ef10ba 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -1,9 +1,8 @@
//===- LinkerScript.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -32,7 +31,6 @@ namespace elf {
class Defined;
class InputSection;
class InputSectionBase;
-class InputSectionBase;
class OutputSection;
class SectionBase;
class Symbol;
@@ -40,35 +38,35 @@ class ThunkSection;
// This represents an r-value in the linker script.
struct ExprValue {
- ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val,
- const Twine &Loc)
- : Sec(Sec), ForceAbsolute(ForceAbsolute), Val(Val), Loc(Loc.str()) {}
+ ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val,
+ const Twine &loc)
+ : sec(sec), forceAbsolute(forceAbsolute), val(val), loc(loc.str()) {}
- ExprValue(uint64_t Val) : ExprValue(nullptr, false, Val, "") {}
+ ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {}
- bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; }
+ bool isAbsolute() const { return forceAbsolute || sec == nullptr; }
uint64_t getValue() const;
uint64_t getSecAddr() const;
uint64_t getSectionOffset() const;
// If a value is relative to a section, it has a non-null Sec.
- SectionBase *Sec;
+ SectionBase *sec;
// True if this expression is enclosed in ABSOLUTE().
// This flag affects the return value of getValue().
- bool ForceAbsolute;
+ bool forceAbsolute;
- uint64_t Val;
- uint64_t Alignment = 1;
+ uint64_t val;
+ uint64_t alignment = 1;
// Original source location. Used for error messages.
- std::string Loc;
+ std::string loc;
};
// This represents an expression in the linker script.
// ScriptParser::readExpr reads an expression and returns an Expr.
// Later, we evaluate the expression by calling the function.
-typedef std::function<ExprValue()> Expr;
+using Expr = std::function<ExprValue()>;
// This enum is used to implement linker script SECTIONS command.
// https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS
@@ -80,42 +78,42 @@ enum SectionsCommandKind {
};
struct BaseCommand {
- BaseCommand(int K) : Kind(K) {}
- int Kind;
+ BaseCommand(int k) : kind(k) {}
+ int kind;
};
// This represents ". = <expr>" or "<symbol> = <expr>".
struct SymbolAssignment : BaseCommand {
- SymbolAssignment(StringRef Name, Expr E, std::string Loc)
- : BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {}
+ SymbolAssignment(StringRef name, Expr e, std::string loc)
+ : BaseCommand(AssignmentKind), name(name), expression(e), location(loc) {}
- static bool classof(const BaseCommand *C) {
- return C->Kind == AssignmentKind;
+ static bool classof(const BaseCommand *c) {
+ return c->kind == AssignmentKind;
}
// The LHS of an expression. Name is either a symbol name or ".".
- StringRef Name;
- Defined *Sym = nullptr;
+ StringRef name;
+ Defined *sym = nullptr;
// The RHS of an expression.
- Expr Expression;
+ Expr expression;
// Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN.
- bool Provide = false;
- bool Hidden = false;
+ bool provide = false;
+ bool hidden = false;
// Holds file name and line number for error reporting.
- std::string Location;
+ std::string location;
// A string representation of this command. We use this for -Map.
- std::string CommandString;
+ std::string commandString;
// Address of this assignment command.
- unsigned Addr;
+ unsigned addr;
// Size of this assignment command. This is usually 0, but if
// you move '.' this may be greater than 0.
- unsigned Size;
+ unsigned size;
};
// Linker scripts allow additional constraints to be put on ouput sections.
@@ -128,83 +126,83 @@ 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,
- uint32_t NegFlags)
- : Name(Name), Origin(Origin), Length(Length), Flags(Flags),
- NegFlags(NegFlags) {}
-
- std::string Name;
- uint64_t Origin;
- uint64_t Length;
- uint32_t Flags;
- uint32_t NegFlags;
- uint64_t CurPos = 0;
+ MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags,
+ uint32_t negFlags)
+ : name(name), origin(origin), length(length), flags(flags),
+ negFlags(negFlags) {}
+
+ std::string name;
+ uint64_t origin;
+ uint64_t length;
+ uint32_t flags;
+ uint32_t negFlags;
+ uint64_t curPos = 0;
};
// This struct represents one section match pattern in SECTIONS() command.
// It can optionally have negative match pattern for EXCLUDED_FILE command.
// Also it may be surrounded with SORT() command, so contains sorting rules.
struct SectionPattern {
- SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2)
- : ExcludedFilePat(Pat1), SectionPat(Pat2),
- SortOuter(SortSectionPolicy::Default),
- SortInner(SortSectionPolicy::Default) {}
-
- StringMatcher ExcludedFilePat;
- StringMatcher SectionPat;
- SortSectionPolicy SortOuter;
- SortSectionPolicy SortInner;
+ SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2)
+ : excludedFilePat(pat1), sectionPat(pat2),
+ sortOuter(SortSectionPolicy::Default),
+ sortInner(SortSectionPolicy::Default) {}
+
+ StringMatcher excludedFilePat;
+ StringMatcher sectionPat;
+ SortSectionPolicy sortOuter;
+ SortSectionPolicy sortInner;
};
struct InputSectionDescription : BaseCommand {
- InputSectionDescription(StringRef FilePattern)
- : BaseCommand(InputSectionKind), FilePat(FilePattern) {}
+ InputSectionDescription(StringRef filePattern)
+ : BaseCommand(InputSectionKind), filePat(filePattern) {}
- static bool classof(const BaseCommand *C) {
- return C->Kind == InputSectionKind;
+ static bool classof(const BaseCommand *c) {
+ return c->kind == InputSectionKind;
}
- StringMatcher FilePat;
+ StringMatcher filePat;
// Input sections that matches at least one of SectionPatterns
// will be associated with this InputSectionDescription.
- std::vector<SectionPattern> SectionPatterns;
+ std::vector<SectionPattern> sectionPatterns;
- std::vector<InputSection *> Sections;
+ std::vector<InputSection *> sections;
// Temporary record of synthetic ThunkSection instances and the pass that
// 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;
+ std::vector<std::pair<ThunkSection *, uint32_t>> thunkSections;
};
// Represents BYTE(), SHORT(), LONG(), or QUAD().
struct ByteCommand : BaseCommand {
- ByteCommand(Expr E, unsigned Size, std::string CommandString)
- : BaseCommand(ByteKind), CommandString(CommandString), Expression(E),
- Size(Size) {}
+ ByteCommand(Expr e, unsigned size, std::string commandString)
+ : BaseCommand(ByteKind), commandString(commandString), expression(e),
+ size(size) {}
- static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; }
+ static bool classof(const BaseCommand *c) { return c->kind == ByteKind; }
// Keeps string representing the command. Used for -Map" is perhaps better.
- std::string CommandString;
+ std::string commandString;
- Expr Expression;
+ Expr expression;
// This is just an offset of this assignment command in the output section.
- unsigned Offset;
+ unsigned offset;
// Size of this data command.
- unsigned Size;
+ unsigned size;
};
struct PhdrsCommand {
- StringRef Name;
- unsigned Type = llvm::ELF::PT_NULL;
- bool HasFilehdr = false;
- bool HasPhdrs = false;
- llvm::Optional<unsigned> Flags;
- Expr LMAExpr = nullptr;
+ StringRef name;
+ unsigned type = llvm::ELF::PT_NULL;
+ bool hasFilehdr = false;
+ bool hasPhdrs = false;
+ llvm::Optional<unsigned> flags;
+ Expr lmaExpr = nullptr;
};
class LinkerScript final {
@@ -213,35 +211,35 @@ class LinkerScript final {
// not be used outside of the scope of a call to the above functions.
struct AddressState {
AddressState();
- uint64_t ThreadBssOffset = 0;
- OutputSection *OutSec = nullptr;
- MemoryRegion *MemRegion = nullptr;
- MemoryRegion *LMARegion = nullptr;
- uint64_t LMAOffset = 0;
+ uint64_t threadBssOffset = 0;
+ OutputSection *outSec = nullptr;
+ MemoryRegion *memRegion = nullptr;
+ MemoryRegion *lmaRegion = nullptr;
+ uint64_t lmaOffset = 0;
};
- llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
+ llvm::DenseMap<StringRef, OutputSection *> nameToOutputSection;
- void addSymbol(SymbolAssignment *Cmd);
- void assignSymbol(SymbolAssignment *Cmd, bool InSec);
- void setDot(Expr E, const Twine &Loc, bool InSec);
- void expandOutputSection(uint64_t Size);
- void expandMemoryRegions(uint64_t Size);
+ void addSymbol(SymbolAssignment *cmd);
+ void assignSymbol(SymbolAssignment *cmd, bool inSec);
+ void setDot(Expr e, const Twine &loc, bool inSec);
+ void expandOutputSection(uint64_t size);
+ void expandMemoryRegions(uint64_t size);
std::vector<InputSection *>
computeInputSections(const InputSectionDescription *);
- std::vector<InputSection *> createInputSectionList(OutputSection &Cmd);
+ std::vector<InputSection *> createInputSectionList(OutputSection &cmd);
- std::vector<size_t> getPhdrIndices(OutputSection *Sec);
+ std::vector<size_t> getPhdrIndices(OutputSection *sec);
- MemoryRegion *findMemoryRegion(OutputSection *Sec);
+ MemoryRegion *findMemoryRegion(OutputSection *sec);
- void switchTo(OutputSection *Sec);
- uint64_t advance(uint64_t Size, unsigned Align);
- void output(InputSection *Sec);
+ void switchTo(OutputSection *sec);
+ uint64_t advance(uint64_t size, unsigned align);
+ void output(InputSection *sec);
- void assignOffsets(OutputSection *Sec);
+ void assignOffsets(OutputSection *sec);
// Ctx captures the local AddressState and makes it accessible
// deliberately. This is needed as there are some cases where we cannot just
@@ -249,21 +247,21 @@ class LinkerScript final {
// script parser.
// This should remain a plain pointer as its lifetime is smaller than
// LinkerScript.
- AddressState *Ctx = nullptr;
+ AddressState *ctx = nullptr;
- OutputSection *Aether;
+ OutputSection *aether;
- uint64_t Dot;
+ uint64_t dot;
public:
- OutputSection *createOutputSection(StringRef Name, StringRef Location);
- OutputSection *getOrCreateOutputSection(StringRef Name);
+ OutputSection *createOutputSection(StringRef name, StringRef location);
+ OutputSection *getOrCreateOutputSection(StringRef name);
- bool hasPhdrsCommands() { return !PhdrsCommands.empty(); }
- uint64_t getDot() { return Dot; }
- void discard(ArrayRef<InputSection *> V);
+ bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
+ uint64_t getDot() { return dot; }
+ void discard(ArrayRef<InputSection *> v);
- ExprValue getSymbolValue(StringRef Name, const Twine &Loc);
+ ExprValue getSymbolValue(StringRef name, const Twine &loc);
void addOrphanSections();
void adjustSectionsBeforeSorting();
@@ -272,9 +270,9 @@ public:
std::vector<PhdrEntry *> createPhdrs();
bool needsInterpSection();
- bool shouldKeep(InputSectionBase *S);
+ bool shouldKeep(InputSectionBase *s);
void assignAddresses();
- void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
+ void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
void processSectionCommands();
void declareSymbols();
@@ -282,31 +280,31 @@ public:
void processInsertCommands();
// SECTIONS command list.
- std::vector<BaseCommand *> SectionCommands;
+ std::vector<BaseCommand *> sectionCommands;
// PHDRS command list.
- std::vector<PhdrsCommand> PhdrsCommands;
+ std::vector<PhdrsCommand> phdrsCommands;
- bool HasSectionsCommand = false;
- bool ErrorOnMissingSection = false;
+ bool hasSectionsCommand = false;
+ bool errorOnMissingSection = false;
// List of section patterns specified with KEEP commands. They will
// be kept even if they are unused and --gc-sections is specified.
- std::vector<InputSectionDescription *> KeptSections;
+ std::vector<InputSectionDescription *> keptSections;
// A map from memory region name to a memory region descriptor.
- llvm::MapVector<llvm::StringRef, MemoryRegion *> MemoryRegions;
+ llvm::MapVector<llvm::StringRef, MemoryRegion *> memoryRegions;
// A list of symbols referenced by the script.
- std::vector<llvm::StringRef> ReferencedSymbols;
+ 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;
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertAfterCommands;
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertBeforeCommands;
};
-extern LinkerScript *Script;
+extern LinkerScript *script;
} // end namespace elf
} // end namespace lld
diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp
index b0dc6203008d..a4a6238fc655 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -1,9 +1,8 @@
//===- MapFile.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -38,69 +37,67 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-typedef DenseMap<const SectionBase *, SmallVector<Defined *, 4>> SymbolMapTy;
+using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
-static const std::string Indent8 = " "; // 8 spaces
-static const std::string Indent16 = " "; // 16 spaces
+static const std::string indent8 = " "; // 8 spaces
+static const std::string indent16 = " "; // 16 spaces
// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &OS, uint64_t VMA, uint64_t LMA,
- uint64_t Size, uint64_t Align) {
- if (Config->Is64)
- OS << format("%16llx %16llx %8llx %5lld ", VMA, LMA, Size, Align);
+static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
+ uint64_t size, uint64_t align) {
+ if (config->is64)
+ os << format("%16llx %16llx %8llx %5lld ", vma, lma, size, align);
else
- OS << format("%8llx %8llx %8llx %5lld ", VMA, LMA, Size, Align);
+ os << format("%8llx %8llx %8llx %5lld ", vma, lma, size, align);
}
// Returns a list of all symbols that we want to print out.
static std::vector<Defined *> getSymbols() {
- std::vector<Defined *> V;
- for (InputFile *File : ObjectFiles)
- for (Symbol *B : File->getSymbols())
- if (auto *DR = dyn_cast<Defined>(B))
- if (!DR->isSection() && DR->Section && DR->Section->Live &&
- (DR->File == File || DR->NeedsPltAddr || DR->Section->Bss))
- V.push_back(DR);
- return V;
+ std::vector<Defined *> v;
+ for (InputFile *file : objectFiles)
+ for (Symbol *b : file->getSymbols())
+ if (auto *dr = dyn_cast<Defined>(b))
+ if (!dr->isSection() && dr->section && dr->section->isLive() &&
+ (dr->file == file || dr->needsPltAddr || dr->section->bss))
+ v.push_back(dr);
+ return v;
}
// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<Defined *> Syms) {
- SymbolMapTy Ret;
- for (Defined *DR : Syms)
- Ret[DR->Section].push_back(DR);
+static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {
+ SymbolMapTy ret;
+ for (Defined *dr : syms)
+ ret[dr->section].push_back(dr);
// Sort symbols by address. We want to print out symbols in the
// order in the output file rather than the order they appeared
// in the input files.
- for (auto &It : Ret) {
- SmallVectorImpl<Defined *> &V = It.second;
- std::stable_sort(V.begin(), V.end(), [](Defined *A, Defined *B) {
- return A->getVA() < B->getVA();
+ for (auto &it : ret)
+ llvm::stable_sort(it.second, [](Defined *a, Defined *b) {
+ return a->getVA() < b->getVA();
});
- }
- return Ret;
+ return ret;
}
// Construct a map from symbols to their stringified representations.
// Demangling symbols (which is what toString() does) is slow, so
// we do that in batch using parallel-for.
static DenseMap<Symbol *, std::string>
-getSymbolStrings(ArrayRef<Defined *> Syms) {
- std::vector<std::string> Str(Syms.size());
- parallelForEachN(0, Syms.size(), [&](size_t I) {
- raw_string_ostream OS(Str[I]);
- OutputSection *OSec = Syms[I]->getOutputSection();
- uint64_t VMA = Syms[I]->getVA();
- uint64_t LMA = OSec ? OSec->getLMA() + VMA - OSec->getVA(0) : 0;
- writeHeader(OS, VMA, LMA, Syms[I]->getSize(), 1);
- OS << Indent16 << toString(*Syms[I]);
+getSymbolStrings(ArrayRef<Defined *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN(0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ OutputSection *osec = syms[i]->getOutputSection();
+ uint64_t vma = syms[i]->getVA();
+ uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0;
+ writeHeader(os, vma, lma, syms[i]->getSize(), 1);
+ os << indent16 << toString(*syms[i]);
});
- DenseMap<Symbol *, std::string> Ret;
- for (size_t I = 0, E = Syms.size(); I < E; ++I)
- Ret[Syms[I]] = std::move(Str[I]);
- return Ret;
+ DenseMap<Symbol *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
}
// Print .eh_frame contents. Since the section consists of EhSectionPieces,
@@ -109,114 +106,115 @@ getSymbolStrings(ArrayRef<Defined *> Syms) {
// .eh_frame tend to contain a lot of section pieces that are contiguous
// both in input file and output file. Such pieces are squashed before
// being displayed to make output compact.
-static void printEhFrame(raw_ostream &OS, OutputSection *OSec) {
- std::vector<EhSectionPiece> Pieces;
+static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
+ std::vector<EhSectionPiece> pieces;
- auto Add = [&](const EhSectionPiece &P) {
+ auto add = [&](const EhSectionPiece &p) {
// If P is adjacent to Last, squash the two.
- if (!Pieces.empty()) {
- EhSectionPiece &Last = Pieces.back();
- if (Last.Sec == P.Sec && Last.InputOff + Last.Size == P.InputOff &&
- Last.OutputOff + Last.Size == P.OutputOff) {
- Last.Size += P.Size;
+ if (!pieces.empty()) {
+ EhSectionPiece &last = pieces.back();
+ if (last.sec == p.sec && last.inputOff + last.size == p.inputOff &&
+ last.outputOff + last.size == p.outputOff) {
+ last.size += p.size;
return;
}
}
- Pieces.push_back(P);
+ pieces.push_back(p);
};
// Gather section pieces.
- for (const CieRecord *Rec : In.EhFrame->getCieRecords()) {
- Add(*Rec->Cie);
- for (const EhSectionPiece *Fde : Rec->Fdes)
- Add(*Fde);
+ for (const CieRecord *rec : sec->getCieRecords()) {
+ add(*rec->cie);
+ for (const EhSectionPiece *fde : rec->fdes)
+ add(*fde);
}
// Print out section pieces.
- for (EhSectionPiece &P : Pieces) {
- writeHeader(OS, OSec->Addr + P.OutputOff, OSec->getLMA() + P.OutputOff,
- P.Size, 1);
- OS << Indent8 << toString(P.Sec->File) << ":(" << P.Sec->Name << "+0x"
- << Twine::utohexstr(P.InputOff) + ")\n";
+ const OutputSection *osec = sec->getOutputSection();
+ for (EhSectionPiece &p : pieces) {
+ writeHeader(os, osec->addr + p.outputOff, osec->getLMA() + p.outputOff,
+ p.size, 1);
+ os << indent8 << toString(p.sec->file) << ":(" << p.sec->name << "+0x"
+ << Twine::utohexstr(p.inputOff) + ")\n";
}
}
void elf::writeMapFile() {
- if (Config->MapFile.empty())
+ if (config->mapFile.empty())
return;
// Open a map file for writing.
- std::error_code EC;
- raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
- if (EC) {
- error("cannot open " + Config->MapFile + ": " + EC.message());
+ std::error_code ec;
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
+ if (ec) {
+ error("cannot open " + config->mapFile + ": " + ec.message());
return;
}
// Collect symbol info that we want to print out.
- std::vector<Defined *> Syms = getSymbols();
- SymbolMapTy SectionSyms = getSectionSyms(Syms);
- DenseMap<Symbol *, std::string> SymStr = getSymbolStrings(Syms);
+ std::vector<Defined *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<Symbol *, std::string> symStr = getSymbolStrings(syms);
// Print out the header line.
- int W = Config->Is64 ? 16 : 8;
- OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W)
+ int w = config->is64 ? 16 : 8;
+ os << right_justify("VMA", w) << ' ' << right_justify("LMA", w)
<< " Size Align Out In Symbol\n";
- OutputSection* OSec = nullptr;
- for (BaseCommand *Base : Script->SectionCommands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- if (Cmd->Provide && !Cmd->Sym)
+ OutputSection* osec = nullptr;
+ for (BaseCommand *base : script->sectionCommands) {
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ if (cmd->provide && !cmd->sym)
continue;
- uint64_t LMA = OSec ? OSec->getLMA() + Cmd->Addr - OSec->getVA(0) : 0;
- writeHeader(OS, Cmd->Addr, LMA, Cmd->Size, 1);
- OS << Cmd->CommandString << '\n';
+ uint64_t lma = osec ? osec->getLMA() + cmd->addr - osec->getVA(0) : 0;
+ writeHeader(os, cmd->addr, lma, cmd->size, 1);
+ os << cmd->commandString << '\n';
continue;
}
- OSec = cast<OutputSection>(Base);
- writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment);
- OS << OSec->Name << '\n';
+ osec = cast<OutputSection>(base);
+ writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->alignment);
+ os << osec->name << '\n';
// Dump symbols for each input section.
- for (BaseCommand *Base : OSec->SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
- for (InputSection *IS : ISD->Sections) {
- if (IS == In.EhFrame) {
- printEhFrame(OS, OSec);
+ for (BaseCommand *base : osec->sectionCommands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (InputSection *isec : isd->sections) {
+ if (auto *ehSec = dyn_cast<EhFrameSection>(isec)) {
+ printEhFrame(os, ehSec);
continue;
}
- writeHeader(OS, IS->getVA(0), OSec->getLMA() + IS->getOffset(0),
- IS->getSize(), IS->Alignment);
- OS << Indent8 << toString(IS) << '\n';
- for (Symbol *Sym : SectionSyms[IS])
- OS << SymStr[Sym] << '\n';
+ writeHeader(os, isec->getVA(0), osec->getLMA() + isec->getOffset(0),
+ isec->getSize(), isec->alignment);
+ os << indent8 << toString(isec) << '\n';
+ for (Symbol *sym : sectionSyms[isec])
+ os << symStr[sym] << '\n';
}
continue;
}
- if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
- writeHeader(OS, OSec->Addr + Cmd->Offset, OSec->getLMA() + Cmd->Offset,
- Cmd->Size, 1);
- OS << Indent8 << Cmd->CommandString << '\n';
+ if (auto *cmd = dyn_cast<ByteCommand>(base)) {
+ writeHeader(os, osec->addr + cmd->offset, osec->getLMA() + cmd->offset,
+ cmd->size, 1);
+ os << indent8 << cmd->commandString << '\n';
continue;
}
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
- if (Cmd->Provide && !Cmd->Sym)
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
+ if (cmd->provide && !cmd->sym)
continue;
- writeHeader(OS, Cmd->Addr, OSec->getLMA() + Cmd->Addr - OSec->getVA(0),
- Cmd->Size, 1);
- OS << Indent8 << Cmd->CommandString << '\n';
+ writeHeader(os, cmd->addr, osec->getLMA() + cmd->addr - osec->getVA(0),
+ cmd->size, 1);
+ os << indent8 << cmd->commandString << '\n';
continue;
}
}
}
}
-static void print(StringRef A, StringRef B) {
- outs() << left_justify(A, 49) << " " << B << "\n";
+static void print(StringRef a, StringRef b) {
+ outs() << left_justify(a, 49) << " " << b << "\n";
}
// Output a cross reference table to stdout. This is for --cref.
@@ -231,18 +229,18 @@ static void print(StringRef A, StringRef B) {
// In this case, strlen is defined by libc.so.6 and used by other two
// files.
void elf::writeCrossReferenceTable() {
- if (!Config->Cref)
+ if (!config->cref)
return;
// Collect symbols and files.
- MapVector<Symbol *, SetVector<InputFile *>> Map;
- for (InputFile *File : ObjectFiles) {
- for (Symbol *Sym : File->getSymbols()) {
- if (isa<SharedSymbol>(Sym))
- Map[Sym].insert(File);
- if (auto *D = dyn_cast<Defined>(Sym))
- if (!D->isLocal() && (!D->Section || D->Section->Live))
- Map[D].insert(File);
+ MapVector<Symbol *, SetVector<InputFile *>> map;
+ for (InputFile *file : objectFiles) {
+ for (Symbol *sym : file->getSymbols()) {
+ if (isa<SharedSymbol>(sym))
+ map[sym].insert(file);
+ if (auto *d = dyn_cast<Defined>(sym))
+ if (!d->isLocal() && (!d->section || d->section->isLive()))
+ map[d].insert(file);
}
}
@@ -251,13 +249,13 @@ void elf::writeCrossReferenceTable() {
print("Symbol", "File");
// Print out a table.
- for (auto KV : Map) {
- Symbol *Sym = KV.first;
- SetVector<InputFile *> &Files = KV.second;
+ for (auto kv : map) {
+ Symbol *sym = kv.first;
+ SetVector<InputFile *> &files = kv.second;
- print(toString(*Sym), toString(Sym->File));
- for (InputFile *File : Files)
- if (File != Sym->File)
- print("", toString(File));
+ print(toString(*sym), toString(sym->file));
+ for (InputFile *file : files)
+ if (file != sym->file)
+ print("", toString(file));
}
}
diff --git a/ELF/MapFile.h b/ELF/MapFile.h
index 0282425888b7..7e7938919edf 100644
--- a/ELF/MapFile.h
+++ b/ELF/MapFile.h
@@ -1,9 +1,8 @@
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index 8d0ec091c327..36b847f725b8 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -1,9 +1,8 @@
//===- MarkLive.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -26,6 +25,7 @@
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
@@ -42,66 +42,79 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+namespace {
+template <class ELFT> class MarkLive {
+public:
+ MarkLive(unsigned partition) : partition(partition) {}
+
+ void run();
+ void moveToMain();
+
+private:
+ void enqueue(InputSectionBase *sec, uint64_t offset);
+ void markSymbol(Symbol *sym);
+ void mark();
+
+ template <class RelTy>
+ void resolveReloc(InputSectionBase &sec, RelTy &rel, bool isLSDA);
+
+ template <class RelTy>
+ void scanEhFrameSection(EhInputSection &eh, ArrayRef<RelTy> rels);
+
+ // The index of the partition that we are currently processing.
+ unsigned partition;
+
+ // A list of sections to visit.
+ SmallVector<InputSection *, 256> queue;
+
+ // There are normally few input sections whose names are valid C
+ // identifiers, so we just store a std::vector instead of a multimap.
+ DenseMap<StringRef, std::vector<InputSectionBase *>> cNamedSections;
+};
+} // namespace
+
template <class ELFT>
-static typename ELFT::uint getAddend(InputSectionBase &Sec,
- const typename ELFT::Rel &Rel) {
- return Target->getImplicitAddend(Sec.data().begin() + Rel.r_offset,
- Rel.getType(Config->IsMips64EL));
+static uint64_t getAddend(InputSectionBase &sec,
+ const typename ELFT::Rel &rel) {
+ return target->getImplicitAddend(sec.data().begin() + rel.r_offset,
+ rel.getType(config->isMips64EL));
}
template <class ELFT>
-static typename ELFT::uint getAddend(InputSectionBase &Sec,
- const typename ELFT::Rela &Rel) {
- return Rel.r_addend;
+static uint64_t getAddend(InputSectionBase &sec,
+ const typename ELFT::Rela &rel) {
+ return rel.r_addend;
}
-// There are normally few input sections whose names are valid C
-// identifiers, so we just store a std::vector instead of a multimap.
-static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
-
-template <class ELFT, class RelT>
-static void
-resolveReloc(InputSectionBase &Sec, RelT &Rel,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
+template <class ELFT>
+template <class RelTy>
+void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
+ bool isLSDA) {
+ Symbol &sym = sec.getFile<ELFT>()->getRelocTargetSym(rel);
// If a symbol is referenced in a live section, it is used.
- B.Used = true;
- if (auto *SS = dyn_cast<SharedSymbol>(&B))
- if (!SS->isWeak())
- SS->getFile<ELFT>().IsNeeded = true;
-
- if (auto *D = dyn_cast<Defined>(&B)) {
- auto *RelSec = dyn_cast_or_null<InputSectionBase>(D->Section);
- if (!RelSec)
+ sym.used = true;
+
+ if (auto *d = dyn_cast<Defined>(&sym)) {
+ auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
+ if (!relSec)
return;
- uint64_t Offset = D->Value;
- if (D->isSection())
- Offset += getAddend<ELFT>(Sec, Rel);
- Fn(RelSec, Offset);
- return;
- }
- if (!B.isDefined())
- for (InputSectionBase *Sec : CNamedSections.lookup(B.getName()))
- Fn(Sec, 0);
-}
+ uint64_t offset = d->value;
+ if (d->isSection())
+ offset += getAddend<ELFT>(sec, rel);
-// Calls Fn for each section that Sec refers to via relocations.
-template <class ELFT>
-static void
-forEachSuccessor(InputSection &Sec,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- if (Sec.AreRelocsRela) {
- for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
- resolveReloc<ELFT>(Sec, Rel, Fn);
- } else {
- for (const typename ELFT::Rel &Rel : Sec.template rels<ELFT>())
- resolveReloc<ELFT>(Sec, Rel, Fn);
+ if (!isLSDA || !(relSec->flags & SHF_EXECINSTR))
+ enqueue(relSec, offset);
+ return;
}
- for (InputSectionBase *IS : Sec.DependentSections)
- Fn(IS, 0);
+ if (auto *ss = dyn_cast<SharedSymbol>(&sym))
+ if (!ss->isWeak())
+ ss->getFile().isNeeded = true;
+
+ for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
+ enqueue(sec, 0);
}
// The .eh_frame section is an unfortunate special case.
@@ -118,175 +131,203 @@ forEachSuccessor(InputSection &Sec,
// A possible improvement would be to fully process .eh_frame in the middle of
// the gc pass. With that we would be able to also gc some sections holding
// LSDAs and personality functions if we found that they were unused.
-template <class ELFT, class RelTy>
-static void
-scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- const endianness E = ELFT::TargetEndianness;
-
- for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
- EhSectionPiece &Piece = EH.Pieces[I];
- unsigned FirstRelI = Piece.FirstRelocation;
- if (FirstRelI == (unsigned)-1)
+template <class ELFT>
+template <class RelTy>
+void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
+ ArrayRef<RelTy> rels) {
+ for (size_t i = 0, end = eh.pieces.size(); i < end; ++i) {
+ EhSectionPiece &piece = eh.pieces[i];
+ size_t firstRelI = piece.firstRelocation;
+ if (firstRelI == (unsigned)-1)
continue;
- if (read32<E>(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<ELFT>(EH, Rels[FirstRelI], Fn);
+ resolveReloc(eh, rels[firstRelI], false);
continue;
}
+
// This is a FDE. The relocations point to the described function or to
// a LSDA. We only need to keep the LSDA alive, so ignore anything that
// points to executable sections.
- typename ELFT::uint PieceEnd = Piece.InputOff + Piece.Size;
- for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) {
- const RelTy &Rel = Rels[I2];
- if (Rel.r_offset >= PieceEnd)
- break;
- resolveReloc<ELFT>(EH, Rels[I2],
- [&](InputSectionBase *Sec, uint64_t Offset) {
- if (Sec && Sec != &InputSection::Discarded &&
- !(Sec->Flags & SHF_EXECINSTR))
- Fn(Sec, 0);
- });
- }
+ uint64_t pieceEnd = piece.inputOff + piece.size;
+ for (size_t j = firstRelI, end2 = rels.size(); j < end2; ++j)
+ if (rels[j].r_offset < pieceEnd)
+ resolveReloc(eh, rels[j], true);
}
}
-template <class ELFT>
-static void
-scanEhFrameSection(EhInputSection &EH,
- llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
- if (!EH.NumRelocations)
- return;
-
- if (EH.AreRelocsRela)
- scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn);
- else
- scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Fn);
-}
-
// Some sections are used directly by the loader, so they should never be
// garbage-collected. This function returns true if a given section is such
// section.
-template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
- switch (Sec->Type) {
+static bool isReserved(InputSectionBase *sec) {
+ switch (sec->type) {
case SHT_FINI_ARRAY:
case SHT_INIT_ARRAY:
case SHT_NOTE:
case SHT_PREINIT_ARRAY:
return true;
default:
- StringRef S = Sec->Name;
- return S.startswith(".ctors") || S.startswith(".dtors") ||
- S.startswith(".init") || S.startswith(".fini") ||
- S.startswith(".jcr");
+ StringRef s = sec->name;
+ return s.startswith(".ctors") || s.startswith(".dtors") ||
+ s.startswith(".init") || s.startswith(".fini") ||
+ s.startswith(".jcr");
}
}
-// This is the main function of the garbage collector.
-// Starting from GC-root sections, this function visits all reachable
-// sections to set their "Live" bits.
-template <class ELFT> static void doGcSections() {
- SmallVector<InputSection *, 256> Q;
- CNamedSections.clear();
-
- auto Enqueue = [&](InputSectionBase *Sec, uint64_t Offset) {
- // Skip over discarded sections. This in theory shouldn't happen, because
- // the ELF spec doesn't allow a relocation to point to a deduplicated
- // COMDAT section directly. Unfortunately this happens in practice (e.g.
- // .eh_frame) so we need to add a check.
- if (Sec == &InputSection::Discarded)
- return;
-
+template <class ELFT>
+void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset) {
+ // Skip over discarded sections. This in theory shouldn't happen, because
+ // the ELF spec doesn't allow a relocation to point to a deduplicated
+ // COMDAT section directly. Unfortunately this happens in practice (e.g.
+ // .eh_frame) so we need to add a check.
+ if (sec == &InputSection::discarded)
+ return;
- // Usually, a whole section is marked as live or dead, but in mergeable
- // (splittable) sections, each piece of data has independent liveness bit.
- // So we explicitly tell it which offset is in use.
- if (auto *MS = dyn_cast<MergeInputSection>(Sec))
- MS->getSectionPiece(Offset)->Live = true;
+ // Usually, a whole section is marked as live or dead, but in mergeable
+ // (splittable) sections, each piece of data has independent liveness bit.
+ // So we explicitly tell it which offset is in use.
+ if (auto *ms = dyn_cast<MergeInputSection>(sec))
+ ms->getSectionPiece(offset)->live = true;
- if (Sec->Live)
- return;
- Sec->Live = true;
+ // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and
+ // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition
+ // doesn't change, we don't need to do anything.
+ if (sec->partition == 1 || sec->partition == partition)
+ return;
+ sec->partition = sec->partition ? 1 : partition;
- // Add input section to the queue.
- if (InputSection *S = dyn_cast<InputSection>(Sec))
- Q.push_back(S);
- };
+ // Add input section to the queue.
+ if (InputSection *s = dyn_cast<InputSection>(sec))
+ queue.push_back(s);
+}
- auto MarkSymbol = [&](Symbol *Sym) {
- if (auto *D = dyn_cast_or_null<Defined>(Sym))
- if (auto *IS = dyn_cast_or_null<InputSectionBase>(D->Section))
- Enqueue(IS, D->Value);
- };
+template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
+ if (auto *d = dyn_cast_or_null<Defined>(sym))
+ if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
+ enqueue(isec, d->value);
+}
+// This is the main function of the garbage collector.
+// Starting from GC-root sections, this function visits all reachable
+// sections to set their "Live" bits.
+template <class ELFT> void MarkLive<ELFT>::run() {
// Add GC root symbols.
- MarkSymbol(Symtab->find(Config->Entry));
- MarkSymbol(Symtab->find(Config->Init));
- MarkSymbol(Symtab->find(Config->Fini));
- for (StringRef S : Config->Undefined)
- MarkSymbol(Symtab->find(S));
- for (StringRef S : Script->ReferencedSymbols)
- MarkSymbol(Symtab->find(S));
// Preserve externally-visible symbols if the symbols defined by this
// file can interrupt other ELF file's symbols at runtime.
- for (Symbol *S : Symtab->getSymbols())
- if (S->includeInDynsym())
- MarkSymbol(S);
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (sym->includeInDynsym() && sym->partition == partition)
+ markSymbol(sym);
+ });
+
+ // If this isn't the main partition, that's all that we need to preserve.
+ if (partition != 1) {
+ mark();
+ return;
+ }
+
+ markSymbol(symtab->find(config->entry));
+ markSymbol(symtab->find(config->init));
+ markSymbol(symtab->find(config->fini));
+ for (StringRef s : config->undefined)
+ markSymbol(symtab->find(s));
+ for (StringRef s : script->referencedSymbols)
+ markSymbol(symtab->find(s));
// Preserve special sections and those which are specified in linker
// script KEEP command.
- for (InputSectionBase *Sec : InputSections) {
+ for (InputSectionBase *sec : inputSections) {
// Mark .eh_frame sections as live because there are usually no relocations
// that point to .eh_frames. Otherwise, the garbage collector would drop
// all of them. We also want to preserve personality routines and LSDA
// referenced by .eh_frame sections, so we scan them for that here.
- if (auto *EH = dyn_cast<EhInputSection>(Sec)) {
- EH->Live = true;
- scanEhFrameSection<ELFT>(*EH, Enqueue);
+ if (auto *eh = dyn_cast<EhInputSection>(sec)) {
+ eh->markLive();
+ if (!eh->numRelocations)
+ continue;
+
+ if (eh->areRelocsRela)
+ scanEhFrameSection(*eh, eh->template relas<ELFT>());
+ else
+ scanEhFrameSection(*eh, eh->template rels<ELFT>());
}
- if (Sec->Flags & SHF_LINK_ORDER)
+ if (sec->flags & SHF_LINK_ORDER)
continue;
- if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec)) {
- Enqueue(Sec, 0);
- } else if (isValidCIdentifier(Sec->Name)) {
- CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
- CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
+ if (isReserved(sec) || script->shouldKeep(sec)) {
+ enqueue(sec, 0);
+ } else if (isValidCIdentifier(sec->name)) {
+ cNamedSections[saver.save("__start_" + sec->name)].push_back(sec);
+ cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec);
}
}
+ mark();
+}
+
+template <class ELFT> void MarkLive<ELFT>::mark() {
// Mark all reachable sections.
- while (!Q.empty())
- forEachSuccessor<ELFT>(*Q.pop_back_val(), Enqueue);
+ while (!queue.empty()) {
+ InputSectionBase &sec = *queue.pop_back_val();
+
+ if (sec.areRelocsRela) {
+ for (const typename ELFT::Rela &rel : sec.template relas<ELFT>())
+ resolveReloc(sec, rel, false);
+ } else {
+ for (const typename ELFT::Rel &rel : sec.template rels<ELFT>())
+ resolveReloc(sec, rel, false);
+ }
+
+ for (InputSectionBase *isec : sec.dependentSections)
+ enqueue(isec, 0);
+ }
+}
+
+// Move the sections for some symbols to the main partition, specifically ifuncs
+// (because they can result in an IRELATIVE being added to the main partition's
+// GOT, which means that the ifunc must be available when the main partition is
+// loaded) and TLS symbols (because we only know how to correctly process TLS
+// relocations for the main partition).
+template <class ELFT> void MarkLive<ELFT>::moveToMain() {
+ for (InputFile *file : objectFiles)
+ for (Symbol *s : file->getSymbols())
+ if (auto *d = dyn_cast<Defined>(s))
+ if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section &&
+ d->section->isLive())
+ markSymbol(s);
+
+ mark();
}
// 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 elf::markLive() {
- if (!Config->GcSections) {
- // If -gc-sections is missing, no sections are removed.
- for (InputSectionBase *Sec : InputSections)
- Sec->Live = true;
+ // If -gc-sections is not given, no sections are removed.
+ if (!config->gcSections) {
+ for (InputSectionBase *sec : inputSections)
+ sec->markLive();
// If a DSO defines a symbol referenced in a regular object, it is needed.
- for (Symbol *Sym : Symtab->getSymbols())
- if (auto *S = dyn_cast<SharedSymbol>(Sym))
- if (S->IsUsedInRegularObj && !S->isWeak())
- S->getFile<ELFT>().IsNeeded = true;
+ symtab->forEachSymbol([](Symbol *sym) {
+ if (auto *s = dyn_cast<SharedSymbol>(sym))
+ if (s->isUsedInRegularObj && !s->isWeak())
+ s->getFile().isNeeded = true;
+ });
return;
}
+ // Otheriwse, do mark-sweep GC.
+ //
// The -gc-sections option works only for SHF_ALLOC sections
// (sections that are memory-mapped at runtime). So we can
// unconditionally make non-SHF_ALLOC sections alive except
// SHF_LINK_ORDER and SHT_REL/SHT_RELA sections.
//
- // Usually, SHF_ALLOC sections are not removed even if they are
+ // Usually, non-SHF_ALLOC sections are not removed even if they are
// unreachable through relocations because reachability is not
// a good signal whether they are garbage or not (e.g. there is
// usually no section referring to a .comment section, but we
@@ -300,22 +341,30 @@ template <class ELFT> void elf::markLive() {
// or -emit-reloc were given. And they are subject of garbage
// collection because, if we remove a text section, we also
// remove its relocation section.
- for (InputSectionBase *Sec : InputSections) {
- bool IsAlloc = (Sec->Flags & SHF_ALLOC);
- bool IsLinkOrder = (Sec->Flags & SHF_LINK_ORDER);
- bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA);
- if (!IsAlloc && !IsLinkOrder && !IsRel)
- Sec->Live = true;
+ for (InputSectionBase *sec : inputSections) {
+ bool isAlloc = (sec->flags & SHF_ALLOC);
+ bool isLinkOrder = (sec->flags & SHF_LINK_ORDER);
+ bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA);
+
+ if (!isAlloc && !isLinkOrder && !isRel)
+ sec->markLive();
}
// Follow the graph to mark all live sections.
- doGcSections<ELFT>();
+ for (unsigned curPart = 1; curPart <= partitions.size(); ++curPart)
+ MarkLive<ELFT>(curPart).run();
+
+ // If we have multiple partitions, some sections need to live in the main
+ // partition even if they were allocated to a loadable partition. Move them
+ // there now.
+ if (partitions.size() != 1)
+ MarkLive<ELFT>(1).moveToMain();
// Report garbage-collected sections.
- if (Config->PrintGcSections)
- for (InputSectionBase *Sec : InputSections)
- if (!Sec->Live)
- message("removing unused section " + toString(Sec));
+ if (config->printGcSections)
+ for (InputSectionBase *sec : inputSections)
+ if (!sec->isLive())
+ message("removing unused section " + toString(sec));
}
template void elf::markLive<ELF32LE>();
diff --git a/ELF/MarkLive.h b/ELF/MarkLive.h
index c9b99add34de..63b5b2669146 100644
--- a/ELF/MarkLive.h
+++ b/ELF/MarkLive.h
@@ -1,9 +1,8 @@
//===- MarkLive.h -----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/ELF/Options.td b/ELF/Options.td
index e43a21b923d3..3ebb46f2e1b2 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -30,7 +30,7 @@ def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">;
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">,
- MetaVarName<"[fast,md5,sha,uuid,0x<hexstring>]">;
+ MetaVarName<"[fast,md5,sha1,uuid,0x<hexstring>]">;
defm check_sections: B<"check-sections",
"Check section addresses for overlaps (default)",
@@ -63,10 +63,18 @@ defm allow_multiple_definition: B<"allow-multiple-definition",
"Allow multiple definitions",
"Do not allow multiple definitions (default)">;
+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",
"Apply link-time values for dynamic relocations",
"Do not apply link-time values for dynamic relocations (default)">;
+defm dependent_libraries: B<"dependent-libraries",
+ "Process dependent library specifiers from input files (default)",
+ "Ignore dependent library specifiers from input files">;
+
defm as_needed: B<"as-needed",
"Only set DT_NEEDED for shared libraries if used",
"Always set DT_NEEDED for shared libraries (default)">;
@@ -163,6 +171,13 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
+// This option is intentionally hidden from the user as the implementation
+// is not complete.
+def require_cet: F<"require-cet">;
+
+def force_bti: F<"force-bti">,
+ HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property">;
+
defm format: Eq<"format", "Change the input format of the inputs following this option">,
MetaVarName<"[default,elf,binary]">;
@@ -214,6 +229,9 @@ defm merge_exidx_entries: B<"merge-exidx-entries",
"Enable merging .ARM.exidx entries (default)",
"Disable merging .ARM.exidx entries">;
+def nmagic: F<"nmagic">, MetaVarName<"<magic>">,
+ HelpText<"Do not page align sections, link against static libraries.">;
+
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
@@ -226,8 +244,11 @@ def no_dynamic_linker: F<"no-dynamic-linker">,
def noinhibit_exec: F<"noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
+def no_nmagic: F<"no-nmagic">, MetaVarName<"<magic>">,
+ HelpText<"Page align sections (default)">;
+
def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
- HelpText<"Do not set the text data sections to be writable">;
+ 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">;
@@ -242,7 +263,7 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
HelpText<"Specify the binary format for the output object file">;
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
- HelpText<"Set the text and data sections to be readable and writable">;
+ HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">;
defm orphan_handling:
Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">;
@@ -251,10 +272,16 @@ defm pack_dyn_relocs:
Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
MetaVarName<"[none,android,relr,android+relr]">;
+def pac_plt: F<"pac-plt">,
+ HelpText<"AArch64 only, use pointer authentication in PLT">;
+
defm use_android_relr_tags: B<"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)">;
+def pic_veneer: F<"pic-veneer">,
+ HelpText<"Always generate position independent thunks (veneers)">;
+
defm pie: B<"pie",
"Create a position independent executable",
"Do not create a position independent executable (default)">;
@@ -267,6 +294,9 @@ defm print_icf_sections: B<"print-icf-sections",
"List identical folded sections",
"Do not list identical folded sections (default)">;
+defm print_symbol_order: Eq<"print-symbol-order",
+ "Print a symbol order specified by --call-graph-ordering-file into the speficied file">;
+
def pop_state: F<"pop-state">,
HelpText<"Undo the effect of -push-state">;
@@ -336,6 +366,9 @@ defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
defm undefined: Eq<"undefined", "Force undefined symbol during linking">,
MetaVarName<"<symbol>">;
+defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">,
+ MetaVarName<"<pattern>">;
+
defm unresolved_symbols:
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
@@ -383,6 +416,9 @@ defm wrap: Eq<"wrap", "Use wrapper functions for symbol">,
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
+def visual_studio_diagnostics_format : F<"vs-diagnostics">,
+HelpText<"Format diagnostics for Visual Studio compatiblity">;
+
// Aliases
def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
@@ -404,6 +440,7 @@ def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">;
def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">;
def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">;
def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
+def: Flag<["-"], "n">, Alias<nmagic>, HelpText<"Alias for --nmagic">;
def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">;
def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
@@ -437,14 +474,22 @@ def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
def lto_partitions: J<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
+def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
+ HelpText<"Perform context senstive PGO instrumentation">;
+def lto_cs_profile_file: J<"lto-cs-profile-file=">,
+ HelpText<"Context sensitive profile file path">;
def lto_sample_profile: J<"lto-sample-profile=">,
HelpText<"Sample profile file path">;
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">,
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=">,
@@ -465,6 +510,10 @@ 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">;
def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
+def: F<"plugin-opt=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">;
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">;
@@ -489,19 +538,20 @@ def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
def plugin_opt_slash: J<"plugin-opt=/">;
// Options listed below are silently ignored for now for compatibility.
-def: F<"allow-shlib-undefined">;
def: F<"detect-odr-violations">;
def: Flag<["-"], "g">;
def: F<"long-plt">;
def: F<"no-add-needed">;
-def: F<"no-allow-shlib-undefined">;
def: F<"no-copy-dt-needed-entries">;
def: F<"no-ctors-in-init-array">;
def: F<"no-keep-memory">;
def: F<"no-mmap-output-file">;
+def: F<"no-pipeline-knowledge">;
def: F<"no-warn-mismatch">;
+def: Flag<["-"], "p">;
def: Separate<["--", "-"], "rpath-link">;
def: J<"rpath-link=">;
+def: F<"secure-plt">;
def: F<"sort-common">;
def: F<"stats">;
def: F<"warn-execstack">;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index c1442c078736..a89bd509bc96 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -1,9 +1,8 @@
//===- OutputSections.cpp -------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -31,47 +30,46 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-uint8_t Out::First;
-PhdrEntry *Out::TlsPhdr;
-OutputSection *Out::ElfHeader;
-OutputSection *Out::ProgramHeaders;
-OutputSection *Out::PreinitArray;
-OutputSection *Out::InitArray;
-OutputSection *Out::FiniArray;
+uint8_t *Out::bufferStart;
+uint8_t Out::first;
+PhdrEntry *Out::tlsPhdr;
+OutputSection *Out::elfHeader;
+OutputSection *Out::programHeaders;
+OutputSection *Out::preinitArray;
+OutputSection *Out::initArray;
+OutputSection *Out::finiArray;
-std::vector<OutputSection *> elf::OutputSections;
+std::vector<OutputSection *> elf::outputSections;
uint32_t OutputSection::getPhdrFlags() const {
- uint32_t Ret = 0;
- if (Config->EMachine != EM_ARM || !(Flags & SHF_ARM_PURECODE))
- Ret |= PF_R;
- if (Flags & SHF_WRITE)
- Ret |= PF_W;
- if (Flags & SHF_EXECINSTR)
- Ret |= PF_X;
- return Ret;
+ uint32_t ret = 0;
+ if (config->emachine != EM_ARM || !(flags & SHF_ARM_PURECODE))
+ ret |= PF_R;
+ if (flags & SHF_WRITE)
+ ret |= PF_W;
+ if (flags & SHF_EXECINSTR)
+ ret |= PF_X;
+ return ret;
}
template <class ELFT>
-void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
- Shdr->sh_entsize = Entsize;
- Shdr->sh_addralign = Alignment;
- Shdr->sh_type = Type;
- Shdr->sh_offset = Offset;
- Shdr->sh_flags = Flags;
- Shdr->sh_info = Info;
- Shdr->sh_link = Link;
- Shdr->sh_addr = Addr;
- Shdr->sh_size = Size;
- Shdr->sh_name = ShName;
+void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) {
+ shdr->sh_entsize = entsize;
+ shdr->sh_addralign = alignment;
+ shdr->sh_type = type;
+ shdr->sh_offset = offset;
+ shdr->sh_flags = flags;
+ shdr->sh_info = info;
+ shdr->sh_link = link;
+ shdr->sh_addr = addr;
+ shdr->sh_size = size;
+ shdr->sh_name = shName;
}
-OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
+OutputSection::OutputSection(StringRef name, uint32_t type, uint64_t flags)
: BaseCommand(OutputSectionKind),
- SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
- /*Info*/ 0, /*Link*/ 0) {
- Live = false;
-}
+ SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type,
+ /*Info*/ 0, /*Link*/ 0) {}
// We allow sections of types listed below to merged into a
// single progbits section. This is typically done by linker
@@ -79,257 +77,248 @@ OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
// to be allocated for nobits sections. Other ones don't require
// any special treatment on top of progbits, so there doesn't
// seem to be a harm in merging them.
-static bool canMergeToProgbits(unsigned Type) {
- return Type == SHT_NOBITS || Type == SHT_PROGBITS || Type == SHT_INIT_ARRAY ||
- Type == SHT_PREINIT_ARRAY || Type == SHT_FINI_ARRAY ||
- Type == SHT_NOTE;
+static bool canMergeToProgbits(unsigned type) {
+ return type == SHT_NOBITS || type == SHT_PROGBITS || type == SHT_INIT_ARRAY ||
+ type == SHT_PREINIT_ARRAY || type == SHT_FINI_ARRAY ||
+ type == SHT_NOTE;
}
-void OutputSection::addSection(InputSection *IS) {
- if (!Live) {
+void OutputSection::addSection(InputSection *isec) {
+ if (!hasInputSections) {
// If IS is the first section to be added to this section,
- // initialize Type, Entsize and flags from IS.
- Live = true;
- Type = IS->Type;
- Entsize = IS->Entsize;
- Flags = IS->Flags;
+ // initialize Partition, Type, Entsize and flags from IS.
+ hasInputSections = true;
+ partition = isec->partition;
+ type = isec->type;
+ entsize = isec->entsize;
+ flags = isec->flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
- unsigned Mask = SHF_TLS | SHF_LINK_ORDER;
- if ((Flags & Mask) != (IS->Flags & Mask))
- error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
- ": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
- ": 0x" + utohexstr(Flags));
-
- if (Type != IS->Type) {
- if (!canMergeToProgbits(Type) || !canMergeToProgbits(IS->Type))
- error("section type mismatch for " + IS->Name + "\n>>> " +
- toString(IS) + ": " +
- getELFSectionTypeName(Config->EMachine, IS->Type) +
- "\n>>> output section " + Name + ": " +
- getELFSectionTypeName(Config->EMachine, Type));
- Type = SHT_PROGBITS;
+ unsigned mask = SHF_TLS | SHF_LINK_ORDER;
+ if ((flags & mask) != (isec->flags & mask))
+ error("incompatible section flags for " + name + "\n>>> " + toString(isec) +
+ ": 0x" + utohexstr(isec->flags) + "\n>>> output section " + name +
+ ": 0x" + utohexstr(flags));
+
+ if (type != isec->type) {
+ if (!canMergeToProgbits(type) || !canMergeToProgbits(isec->type))
+ error("section type mismatch for " + isec->name + "\n>>> " +
+ toString(isec) + ": " +
+ getELFSectionTypeName(config->emachine, isec->type) +
+ "\n>>> output section " + name + ": " +
+ getELFSectionTypeName(config->emachine, type));
+ type = SHT_PROGBITS;
}
}
- IS->Parent = this;
- uint64_t AndMask =
- Config->EMachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
- uint64_t OrMask = ~AndMask;
- uint64_t AndFlags = (Flags & IS->Flags) & AndMask;
- uint64_t OrFlags = (Flags | IS->Flags) & OrMask;
- Flags = AndFlags | OrFlags;
+ isec->parent = this;
+ uint64_t andMask =
+ config->emachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
+ uint64_t orMask = ~andMask;
+ uint64_t andFlags = (flags & isec->flags) & andMask;
+ uint64_t orFlags = (flags | isec->flags) & orMask;
+ flags = andFlags | orFlags;
- Alignment = std::max(Alignment, IS->Alignment);
+ alignment = std::max(alignment, isec->alignment);
// If this section contains a table of fixed-size entries, sh_entsize
// holds the element size. If it contains elements of different size we
// set sh_entsize to 0.
- if (Entsize != IS->Entsize)
- Entsize = 0;
-
- if (!IS->Assigned) {
- IS->Assigned = true;
- if (SectionCommands.empty() ||
- !isa<InputSectionDescription>(SectionCommands.back()))
- SectionCommands.push_back(make<InputSectionDescription>(""));
- auto *ISD = cast<InputSectionDescription>(SectionCommands.back());
- ISD->Sections.push_back(IS);
+ if (entsize != isec->entsize)
+ entsize = 0;
+
+ if (!isec->assigned) {
+ isec->assigned = true;
+ if (sectionCommands.empty() ||
+ !isa<InputSectionDescription>(sectionCommands.back()))
+ sectionCommands.push_back(make<InputSectionDescription>(""));
+ auto *isd = cast<InputSectionDescription>(sectionCommands.back());
+ isd->sections.push_back(isec);
}
}
-static void sortByOrder(MutableArrayRef<InputSection *> In,
- llvm::function_ref<int(InputSectionBase *S)> Order) {
- typedef std::pair<int, InputSection *> Pair;
- auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
+static void sortByOrder(MutableArrayRef<InputSection *> in,
+ llvm::function_ref<int(InputSectionBase *s)> order) {
+ std::vector<std::pair<int, InputSection *>> v;
+ for (InputSection *s : in)
+ v.push_back({order(s), s});
+ llvm::stable_sort(v, less_first());
- std::vector<Pair> V;
- for (InputSection *S : In)
- V.push_back({Order(S), S});
- std::stable_sort(V.begin(), V.end(), Comp);
-
- for (size_t I = 0; I < V.size(); ++I)
- In[I] = V[I].second;
+ for (size_t i = 0; i < v.size(); ++i)
+ in[i] = v[i].second;
}
uint64_t elf::getHeaderSize() {
- if (Config->OFormatBinary)
+ if (config->oFormatBinary)
return 0;
- return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
+ return Out::elfHeader->size + Out::programHeaders->size;
}
-bool OutputSection::classof(const BaseCommand *C) {
- return C->Kind == OutputSectionKind;
+bool OutputSection::classof(const BaseCommand *c) {
+ return c->kind == OutputSectionKind;
}
-void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) {
- assert(Live);
- for (BaseCommand *B : SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(B))
- sortByOrder(ISD->Sections, Order);
+void OutputSection::sort(llvm::function_ref<int(InputSectionBase *s)> order) {
+ assert(isLive());
+ for (BaseCommand *b : sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ sortByOrder(isd->sections, order);
}
// Fill [Buf, Buf + Size) with Filler.
// This is used for linker script "=fillexp" command.
-static void fill(uint8_t *Buf, size_t Size,
- const std::array<uint8_t, 4> &Filler) {
- size_t I = 0;
- for (; I + 4 < Size; I += 4)
- memcpy(Buf + I, Filler.data(), 4);
- memcpy(Buf + I, Filler.data(), Size - I);
+static void fill(uint8_t *buf, size_t size,
+ const std::array<uint8_t, 4> &filler) {
+ size_t i = 0;
+ for (; i + 4 < size; i += 4)
+ memcpy(buf + i, filler.data(), 4);
+ memcpy(buf + i, filler.data(), size - i);
}
// Compress section contents if this section contains debug info.
template <class ELFT> void OutputSection::maybeCompress() {
- typedef typename ELFT::Chdr Elf_Chdr;
+ using Elf_Chdr = typename ELFT::Chdr;
// Compress only DWARF debug sections.
- if (!Config->CompressDebugSections || (Flags & SHF_ALLOC) ||
- !Name.startswith(".debug_"))
+ if (!config->compressDebugSections || (flags & SHF_ALLOC) ||
+ !name.startswith(".debug_"))
return;
// Create a section header.
- ZDebugHeader.resize(sizeof(Elf_Chdr));
- auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
- Hdr->ch_type = ELFCOMPRESS_ZLIB;
- Hdr->ch_size = Size;
- Hdr->ch_addralign = Alignment;
+ zDebugHeader.resize(sizeof(Elf_Chdr));
+ auto *hdr = reinterpret_cast<Elf_Chdr *>(zDebugHeader.data());
+ hdr->ch_type = ELFCOMPRESS_ZLIB;
+ hdr->ch_size = size;
+ hdr->ch_addralign = alignment;
// Write section contents to a temporary buffer and compress it.
- std::vector<uint8_t> Buf(Size);
- writeTo<ELFT>(Buf.data());
- if (Error E = zlib::compress(toStringRef(Buf), CompressedData))
- fatal("compress failed: " + llvm::toString(std::move(E)));
+ std::vector<uint8_t> buf(size);
+ writeTo<ELFT>(buf.data());
+ if (Error e = zlib::compress(toStringRef(buf), compressedData))
+ fatal("compress failed: " + llvm::toString(std::move(e)));
// Update section headers.
- Size = sizeof(Elf_Chdr) + CompressedData.size();
- Flags |= SHF_COMPRESSED;
+ size = sizeof(Elf_Chdr) + compressedData.size();
+ flags |= SHF_COMPRESSED;
}
-static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
- if (Size == 1)
- *Buf = Data;
- else if (Size == 2)
- write16(Buf, Data);
- else if (Size == 4)
- write32(Buf, Data);
- else if (Size == 8)
- write64(Buf, Data);
+static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) {
+ if (size == 1)
+ *buf = data;
+ else if (size == 2)
+ write16(buf, data);
+ else if (size == 4)
+ write32(buf, data);
+ else if (size == 8)
+ write64(buf, data);
else
llvm_unreachable("unsupported Size argument");
}
-template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
- if (Type == SHT_NOBITS)
+template <class ELFT> void OutputSection::writeTo(uint8_t *buf) {
+ if (type == SHT_NOBITS)
return;
- Loc = Buf;
-
// If -compress-debug-section is specified and if this is a debug seciton,
// we've already compressed section contents. If that's the case,
// just write it down.
- if (!CompressedData.empty()) {
- memcpy(Buf, ZDebugHeader.data(), ZDebugHeader.size());
- memcpy(Buf + ZDebugHeader.size(), CompressedData.data(),
- CompressedData.size());
+ if (!compressedData.empty()) {
+ memcpy(buf, zDebugHeader.data(), zDebugHeader.size());
+ memcpy(buf + zDebugHeader.size(), compressedData.data(),
+ compressedData.size());
return;
}
// Write leading padding.
- std::vector<InputSection *> Sections = getInputSections(this);
- std::array<uint8_t, 4> Filler = getFiller();
- bool NonZeroFiller = read32(Filler.data()) != 0;
- if (NonZeroFiller)
- fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
+ std::vector<InputSection *> sections = getInputSections(this);
+ std::array<uint8_t, 4> filler = getFiller();
+ bool nonZeroFiller = read32(filler.data()) != 0;
+ if (nonZeroFiller)
+ fill(buf, sections.empty() ? size : sections[0]->outSecOff, filler);
- parallelForEachN(0, Sections.size(), [&](size_t I) {
- InputSection *IS = Sections[I];
- IS->writeTo<ELFT>(Buf);
+ parallelForEachN(0, sections.size(), [&](size_t i) {
+ InputSection *isec = sections[i];
+ isec->writeTo<ELFT>(buf);
// Fill gaps between sections.
- if (NonZeroFiller) {
- uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
- uint8_t *End;
- if (I + 1 == Sections.size())
- End = Buf + Size;
+ if (nonZeroFiller) {
+ uint8_t *start = buf + isec->outSecOff + isec->getSize();
+ uint8_t *end;
+ if (i + 1 == sections.size())
+ end = buf + size;
else
- End = Buf + Sections[I + 1]->OutSecOff;
- fill(Start, End - Start, Filler);
+ end = buf + sections[i + 1]->outSecOff;
+ fill(start, end - start, filler);
}
});
// Linker scripts may have BYTE()-family commands with which you
// can write arbitrary bytes to the output. Process them if any.
- for (BaseCommand *Base : SectionCommands)
- if (auto *Data = dyn_cast<ByteCommand>(Base))
- writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size);
+ for (BaseCommand *base : sectionCommands)
+ if (auto *data = dyn_cast<ByteCommand>(base))
+ writeInt(buf + data->offset, data->expression().getValue(), data->size);
}
-template <class ELFT>
-static void finalizeShtGroup(OutputSection *OS,
- InputSection *Section) {
- assert(Config->Relocatable);
+static void finalizeShtGroup(OutputSection *os,
+ InputSection *section) {
+ assert(config->relocatable);
// sh_link field for SHT_GROUP sections should contain the section index of
// the symbol table.
- OS->Link = In.SymTab->getParent()->SectionIndex;
+ os->link = in.symTab->getParent()->sectionIndex;
// sh_info then contain index of an entry in symbol table section which
// provides signature of the section group.
- ObjFile<ELFT> *Obj = Section->getFile<ELFT>();
- ArrayRef<Symbol *> Symbols = Obj->getSymbols();
- OS->Info = In.SymTab->getSymbolIndex(Symbols[Section->Info]);
+ ArrayRef<Symbol *> symbols = section->file->getSymbols();
+ os->info = in.symTab->getSymbolIndex(symbols[section->info]);
}
-template <class ELFT> void OutputSection::finalize() {
- if (Type == SHT_NOBITS)
- for (BaseCommand *Base : SectionCommands)
- if (isa<ByteCommand>(Base))
- Type = SHT_PROGBITS;
-
- std::vector<InputSection *> V = getInputSections(this);
- InputSection *First = V.empty() ? nullptr : V[0];
+void OutputSection::finalize() {
+ std::vector<InputSection *> v = getInputSections(this);
+ InputSection *first = v.empty() ? nullptr : v[0];
- if (Flags & SHF_LINK_ORDER) {
+ if (flags & SHF_LINK_ORDER) {
// We must preserve the link order dependency of sections with the
// SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We
// need to translate the InputSection sh_link to the OutputSection sh_link,
// all InputSections in the OutputSection have the same dependency.
- if (auto *D = First->getLinkOrderDep())
- Link = D->getParent()->SectionIndex;
+ if (auto *ex = dyn_cast<ARMExidxSyntheticSection>(first))
+ link = ex->getLinkOrderDep()->getParent()->sectionIndex;
+ else if (auto *d = first->getLinkOrderDep())
+ link = d->getParent()->sectionIndex;
}
- if (Type == SHT_GROUP) {
- finalizeShtGroup<ELFT>(this, First);
+ if (type == SHT_GROUP) {
+ finalizeShtGroup(this, first);
return;
}
- if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
+ if (!config->copyRelocs || (type != SHT_RELA && type != SHT_REL))
return;
- if (isa<SyntheticSection>(First))
+ if (isa<SyntheticSection>(first))
return;
- Link = In.SymTab->getParent()->SectionIndex;
+ link = in.symTab->getParent()->sectionIndex;
// sh_info for SHT_REL[A] sections should contain the section header index of
// the section to which the relocation applies.
- InputSectionBase *S = First->getRelocatedSection();
- Info = S->getOutputSection()->SectionIndex;
- Flags |= SHF_INFO_LINK;
+ InputSectionBase *s = first->getRelocatedSection();
+ info = s->getOutputSection()->sectionIndex;
+ flags |= SHF_INFO_LINK;
}
// Returns true if S matches /Filename.?\.o$/.
-static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
- if (!S.endswith(".o"))
+static bool isCrtBeginEnd(StringRef s, StringRef filename) {
+ if (!s.endswith(".o"))
return false;
- S = S.drop_back(2);
- if (S.endswith(Filename))
+ s = s.drop_back(2);
+ if (s.endswith(filename))
return true;
- return !S.empty() && S.drop_back().endswith(Filename);
+ return !s.empty() && s.drop_back().endswith(filename);
}
-static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); }
-static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
+static bool isCrtbegin(StringRef s) { return isCrtBeginEnd(s, "crtbegin"); }
+static bool isCrtend(StringRef s) { return isCrtBeginEnd(s, "crtend"); }
// .ctors and .dtors are sorted by this priority from highest to lowest.
//
@@ -349,52 +338,52 @@ static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
// .ctors are duplicate features (and .init_array is newer.) However, there
// are too many real-world use cases of .ctors, so we had no choice to
// support that with this rather ad-hoc semantics.
-static bool compCtors(const InputSection *A, const InputSection *B) {
- bool BeginA = isCrtbegin(A->File->getName());
- bool BeginB = isCrtbegin(B->File->getName());
- if (BeginA != BeginB)
- return BeginA;
- bool EndA = isCrtend(A->File->getName());
- bool EndB = isCrtend(B->File->getName());
- if (EndA != EndB)
- return EndB;
- StringRef X = A->Name;
- StringRef Y = B->Name;
- assert(X.startswith(".ctors") || X.startswith(".dtors"));
- assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
- X = X.substr(6);
- Y = Y.substr(6);
- return X < Y;
+static bool compCtors(const InputSection *a, const InputSection *b) {
+ bool beginA = isCrtbegin(a->file->getName());
+ bool beginB = isCrtbegin(b->file->getName());
+ if (beginA != beginB)
+ return beginA;
+ bool endA = isCrtend(a->file->getName());
+ bool endB = isCrtend(b->file->getName());
+ if (endA != endB)
+ return endB;
+ StringRef x = a->name;
+ StringRef y = b->name;
+ assert(x.startswith(".ctors") || x.startswith(".dtors"));
+ assert(y.startswith(".ctors") || y.startswith(".dtors"));
+ x = x.substr(6);
+ y = y.substr(6);
+ return x < y;
}
// Sorts input sections by the special rules for .ctors and .dtors.
// Unfortunately, the rules are different from the one for .{init,fini}_array.
// Read the comment above.
void OutputSection::sortCtorsDtors() {
- assert(SectionCommands.size() == 1);
- auto *ISD = cast<InputSectionDescription>(SectionCommands[0]);
- std::stable_sort(ISD->Sections.begin(), ISD->Sections.end(), compCtors);
+ assert(sectionCommands.size() == 1);
+ auto *isd = cast<InputSectionDescription>(sectionCommands[0]);
+ llvm::stable_sort(isd->sections, compCtors);
}
// 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 elf::getPriority(StringRef S) {
- size_t Pos = S.rfind('.');
- if (Pos == StringRef::npos)
+int elf::getPriority(StringRef s) {
+ size_t pos = s.rfind('.');
+ if (pos == StringRef::npos)
return 65536;
- int V;
- if (!to_integer(S.substr(Pos + 1), V, 10))
+ int v;
+ if (!to_integer(s.substr(pos + 1), v, 10))
return 65536;
- return V;
+ return v;
}
-std::vector<InputSection *> elf::getInputSections(OutputSection *OS) {
- std::vector<InputSection *> Ret;
- for (BaseCommand *Base : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- Ret.insert(Ret.end(), ISD->Sections.begin(), ISD->Sections.end());
- return Ret;
+std::vector<InputSection *> elf::getInputSections(OutputSection *os) {
+ std::vector<InputSection *> ret;
+ for (BaseCommand *base : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ ret.insert(ret.end(), isd->sections.begin(), isd->sections.end());
+ return ret;
}
// Sorts input sections by section name suffixes, so that .foo.N comes
@@ -405,14 +394,14 @@ std::vector<InputSection *> elf::getInputSections(OutputSection *OS) {
// For more detail, read the section of the GCC's manual about init_priority.
void OutputSection::sortInitFini() {
// Sort sections by priority.
- sort([](InputSectionBase *S) { return getPriority(S->Name); });
+ sort([](InputSectionBase *s) { return getPriority(s->name); });
}
std::array<uint8_t, 4> OutputSection::getFiller() {
- if (Filler)
- return *Filler;
- if (Flags & SHF_EXECINSTR)
- return Target->TrapInstr;
+ if (filler)
+ return *filler;
+ if (flags & SHF_EXECINSTR)
+ return target->trapInstr;
return {0, 0, 0, 0};
}
@@ -430,8 +419,3 @@ template void OutputSection::maybeCompress<ELF32LE>();
template void OutputSection::maybeCompress<ELF32BE>();
template void OutputSection::maybeCompress<ELF64LE>();
template void OutputSection::maybeCompress<ELF64BE>();
-
-template void OutputSection::finalize<ELF32LE>();
-template void OutputSection::finalize<ELF32BE>();
-template void OutputSection::finalize<ELF64LE>();
-template void OutputSection::finalize<ELF64BE>();
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index 113bf6836926..fff8327ea376 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -1,9 +1,8 @@
//===- OutputSections.h -----------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -23,17 +22,8 @@ namespace lld {
namespace elf {
struct PhdrEntry;
-class Symbol;
-struct EhSectionPiece;
-class EhInputSection;
class InputSection;
class InputSectionBase;
-class MergeInputSection;
-class OutputSection;
-template <class ELFT> class ObjFile;
-template <class ELFT> class SharedFile;
-class SharedSymbol;
-class Defined;
// This represents a section in an output file.
// It is composed of multiple InputSections.
@@ -41,19 +31,19 @@ class Defined;
// non-overlapping file offsets and VAs.
class OutputSection final : public BaseCommand, public SectionBase {
public:
- OutputSection(StringRef Name, uint32_t Type, uint64_t Flags);
+ OutputSection(StringRef name, uint32_t type, uint64_t flags);
- static bool classof(const SectionBase *S) {
- return S->kind() == SectionBase::Output;
+ static bool classof(const SectionBase *s) {
+ return s->kind() == SectionBase::Output;
}
- static bool classof(const BaseCommand *C);
+ static bool classof(const BaseCommand *c);
- uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; }
- template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
+ uint64_t getLMA() const { return ptLoad ? addr + ptLoad->lmaOffset : addr; }
+ template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *sHdr);
- uint32_t SectionIndex = UINT32_MAX;
- unsigned SortRank;
+ uint32_t sectionIndex = UINT32_MAX;
+ unsigned sortRank;
uint32_t getPhdrFlags() const;
@@ -64,78 +54,82 @@ public:
// section offset we use the following formula: Off = Off_first + VA -
// VA_first, where Off_first and VA_first is file offset and VA of first
// section in PT_LOAD.
- PhdrEntry *PtLoad = nullptr;
+ PhdrEntry *ptLoad = nullptr;
// Pointer to a relocation section for this section. Usually nullptr because
// we consume relocations, but if --emit-relocs is specified (which is rare),
// it may have a non-null value.
- OutputSection *RelocationSection = nullptr;
+ OutputSection *relocationSection = nullptr;
// Initially this field is the number of InputSections that have been added to
// the OutputSection so far. Later on, after a call to assignAddresses, it
// corresponds to the Elf_Shdr member.
- uint64_t Size = 0;
+ uint64_t size = 0;
// The following fields correspond to Elf_Shdr members.
- uint64_t Offset = 0;
- uint64_t Addr = 0;
- uint32_t ShName = 0;
-
- void addSection(InputSection *IS);
+ uint64_t offset = 0;
+ uint64_t addr = 0;
+ uint32_t shName = 0;
- // Location in the output buffer.
- uint8_t *Loc = nullptr;
+ void addSection(InputSection *isec);
// The following members are normally only used in linker scripts.
- MemoryRegion *MemRegion = nullptr;
- MemoryRegion *LMARegion = nullptr;
- Expr AddrExpr;
- Expr AlignExpr;
- Expr LMAExpr;
- Expr SubalignExpr;
- std::vector<BaseCommand *> SectionCommands;
- std::vector<StringRef> Phdrs;
- llvm::Optional<std::array<uint8_t, 4>> Filler;
- ConstraintKind Constraint = ConstraintKind::NoConstraint;
- std::string Location;
- std::string MemoryRegionName;
- std::string LMARegionName;
- bool NonAlloc = false;
- bool Noload = false;
- bool ExpressionsUseSymbols = false;
- bool InOverlay = false;
-
- template <class ELFT> void finalize();
- template <class ELFT> void writeTo(uint8_t *Buf);
+ MemoryRegion *memRegion = nullptr;
+ MemoryRegion *lmaRegion = nullptr;
+ Expr addrExpr;
+ Expr alignExpr;
+ Expr lmaExpr;
+ Expr subalignExpr;
+ std::vector<BaseCommand *> sectionCommands;
+ std::vector<StringRef> phdrs;
+ llvm::Optional<std::array<uint8_t, 4>> filler;
+ ConstraintKind constraint = ConstraintKind::NoConstraint;
+ std::string location;
+ std::string memoryRegionName;
+ std::string lmaRegionName;
+ bool nonAlloc = false;
+ bool noload = false;
+ bool expressionsUseSymbols = false;
+ bool usedInExpression = false;
+ bool inOverlay = false;
+
+ // Tracks whether the section has ever had an input section added to it, even
+ // if the section was later removed (e.g. because it is a synthetic section
+ // that wasn't needed). This is needed for orphan placement.
+ bool hasInputSections = false;
+
+ void finalize();
+ template <class ELFT> void writeTo(uint8_t *buf);
template <class ELFT> void maybeCompress();
- void sort(llvm::function_ref<int(InputSectionBase *S)> Order);
+ void sort(llvm::function_ref<int(InputSectionBase *s)> order);
void sortInitFini();
void sortCtorsDtors();
private:
// Used for implementation of --compress-debug-sections option.
- std::vector<uint8_t> ZDebugHeader;
- llvm::SmallVector<char, 1> CompressedData;
+ std::vector<uint8_t> zDebugHeader;
+ llvm::SmallVector<char, 1> compressedData;
std::array<uint8_t, 4> getFiller();
};
-int getPriority(StringRef S);
+int getPriority(StringRef s);
-std::vector<InputSection *> getInputSections(OutputSection* OS);
+std::vector<InputSection *> getInputSections(OutputSection* os);
// All output sections that are handled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
struct Out {
- static uint8_t First;
- static PhdrEntry *TlsPhdr;
- static OutputSection *ElfHeader;
- static OutputSection *ProgramHeaders;
- static OutputSection *PreinitArray;
- static OutputSection *InitArray;
- static OutputSection *FiniArray;
+ static uint8_t *bufferStart;
+ static uint8_t first;
+ static PhdrEntry *tlsPhdr;
+ static OutputSection *elfHeader;
+ static OutputSection *programHeaders;
+ static OutputSection *preinitArray;
+ static OutputSection *initArray;
+ static OutputSection *finiArray;
};
} // namespace elf
@@ -146,7 +140,7 @@ namespace elf {
uint64_t getHeaderSize();
-extern std::vector<OutputSection *> OutputSections;
+extern std::vector<OutputSection *> outputSections;
} // namespace elf
} // namespace lld
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index 812468896f0d..ee48f4808136 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -1,9 +1,8 @@
//===- Relocations.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -66,11 +65,11 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) {
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
- if (Cmd->Sym == &Sym)
- return Cmd->Location;
+static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *cmd = dyn_cast<SymbolAssignment>(base))
+ if (cmd->sym == &sym)
+ return cmd->location;
return None;
}
@@ -79,19 +78,51 @@ static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) {
// >>> defined in /home/alice/src/foo.o
// >>> referenced by bar.c:12 (/home/alice/src/bar.c:12)
// >>> /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 Src = S.getSrcMsg(Sym, Off);
- if (!Src.empty())
- Msg += Src + "\n>>> ";
- return Msg + S.getObjMsg(Off);
+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 src = s.getSrcMsg(sym, off);
+ if (!src.empty())
+ msg += src + "\n>>> ";
+ return msg + s.getObjMsg(off);
+}
+
+namespace {
+// Build a bitmask with one bit set for each RelExpr.
+//
+// Constexpr function arguments can't be used in static asserts, so we
+// use template arguments to build the mask.
+// But function template partial specializations don't exist (needed
+// for base case of the recursion), so we need a dummy struct.
+template <RelExpr... Exprs> struct RelExprMaskBuilder {
+ static inline uint64_t build() { return 0; }
+};
+
+// Specialization for recursive case.
+template <RelExpr Head, RelExpr... Tail>
+struct RelExprMaskBuilder<Head, Tail...> {
+ static inline uint64_t build() {
+ static_assert(0 <= Head && Head < 64,
+ "RelExpr is too large for 64-bit mask!");
+ return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
+ }
+};
+} // namespace
+
+// Return true if `Expr` is one of `Exprs`.
+// There are fewer than 64 RelExpr's, so we can represent any set of
+// RelExpr's as a constant bit mask and test for membership with a
+// couple cheap bitwise operations.
+template <RelExpr... Exprs> bool oneof(RelExpr expr) {
+ assert(0 <= expr && (int)expr < 64 &&
+ "RelExpr is too large for 64-bit mask!");
+ return (uint64_t(1) << expr) & RelExprMaskBuilder<Exprs...>::build();
}
// This function is similar to the `handleTlsRelocation`. MIPS does not
@@ -100,204 +131,173 @@ static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
// pollute other `handleTlsRelocation` by MIPS `ifs` statements.
// Mips has a custom MipsGotSection that handles the writing of GOT entries
// without dynamic relocations.
-static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym,
- InputSectionBase &C, uint64_t Offset,
- int64_t Addend, RelExpr Expr) {
- if (Expr == R_MIPS_TLSLD) {
- In.MipsGot->addTlsIndex(*C.File);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+static unsigned handleMipsTlsRelocation(RelType type, Symbol &sym,
+ InputSectionBase &c, uint64_t offset,
+ int64_t addend, RelExpr expr) {
+ if (expr == R_MIPS_TLSLD) {
+ in.mipsGot->addTlsIndex(*c.file);
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
- if (Expr == R_MIPS_TLSGD) {
- In.MipsGot->addDynTlsEntry(*C.File, Sym);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ if (expr == R_MIPS_TLSGD) {
+ in.mipsGot->addDynTlsEntry(*c.file, sym);
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
return 0;
}
-// This function is similar to the `handleMipsTlsRelocation`. ARM also does not
-// support any relaxations for TLS relocations. ARM is logically similar to Mips
-// in how it handles TLS, but Mips uses its own custom GOT which handles some
-// of the cases that ARM uses GOT relocations for.
-//
-// We look for TLS global dynamic and local dynamic relocations, these may
-// require the generation of a pair of GOT entries that have associated
-// dynamic relocations. When the results of the dynamic relocations can be
-// resolved at static link time we do so. This is necessary for static linking
-// as there will be no dynamic loader to resolve them at load-time.
+// Notes about General Dynamic and Local Dynamic TLS models below. They may
+// require the generation of a pair of GOT entries that have associated dynamic
+// relocations. The pair of GOT entries created are of the form GOT[e0] Module
+// Index (Used to find pointer to TLS block at run-time) GOT[e1] Offset of
+// symbol in TLS block.
//
-// The pair of GOT entries created are of the form
-// GOT[e0] Module Index (Used to find pointer to TLS block at run-time)
-// GOT[e1] Offset of symbol in TLS block
-template <class ELFT>
-static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
- InputSectionBase &C, uint64_t Offset,
- int64_t Addend, RelExpr Expr) {
- // The Dynamic TLS Module Index Relocation for a symbol defined in an
- // executable is always 1. If the target Symbol is not preemptible then
- // we know the offset into the TLS block at static link time.
- bool NeedDynId = Sym.IsPreemptible || Config->Shared;
- bool NeedDynOff = Sym.IsPreemptible;
-
- auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) {
- if (Dyn)
- In.RelaDyn->addReloc(Type, In.Got, Off, Dest);
- else
- In.Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
- };
-
- // Local Dynamic is for access to module local TLS variables, while still
- // being suitable for being dynamically loaded via dlopen.
- // GOT[e0] is the module index, with a special value of 0 for the current
- // module. GOT[e1] is unused. There only needs to be one module index entry.
- if (Expr == R_TLSLD_PC && In.Got->addTlsIndex()) {
- AddTlsReloc(In.Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
- NeedDynId ? nullptr : &Sym, NeedDynId);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- return 1;
- }
-
- // Global Dynamic is the most general purpose access model. When we know
- // the module index and offset of symbol in TLS block we can fill these in
- // using static GOT relocations.
- if (Expr == R_TLSGD_PC) {
- if (In.Got->addDynTlsEntry(Sym)) {
- uint64_t Off = In.Got->getGlobalDynOffset(Sym);
- AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId);
- AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym,
- NeedDynOff);
- }
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- return 1;
- }
- return 0;
-}
-
// Returns the number of relocations processed.
template <class ELFT>
static unsigned
-handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
- typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
- if (!Sym.isTls())
+handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
+ typename ELFT::uint offset, int64_t addend, RelExpr expr) {
+ if (!sym.isTls())
return 0;
- if (Config->EMachine == EM_ARM)
- return handleARMTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr);
- if (Config->EMachine == EM_MIPS)
- return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr);
-
- if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
- Config->Shared) {
- if (In.Got->addDynTlsEntry(Sym)) {
- uint64_t Off = In.Got->getGlobalDynOffset(Sym);
- In.RelaDyn->addReloc(
- {Target->TlsDescRel, In.Got, Off, !Sym.IsPreemptible, &Sym, 0});
+ if (config->emachine == EM_MIPS)
+ return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);
+
+ if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC>(
+ expr) &&
+ config->shared) {
+ if (in.got->addDynTlsEntry(sym)) {
+ uint64_t off = in.got->getGlobalDynOffset(sym);
+ mainPart->relaDyn->addReloc(
+ {target->tlsDescRel, in.got, off, !sym.isPreemptible, &sym, 0});
}
- if (Expr != R_TLSDESC_CALL)
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ if (expr != R_TLSDESC_CALL)
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
- if (isRelExprOneOf<R_TLSLD_GOT, R_TLSLD_GOT_FROM_END, R_TLSLD_PC,
- R_TLSLD_HINT>(Expr)) {
+ bool canRelax = config->emachine != EM_ARM && 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.
+ //
+ // ARM and RISC-V do not support any relaxations for TLS relocations, however,
+ // we can omit the DTPMOD dynamic relocations and resolve them at link time
+ // because them are always 1. This may be necessary for static linking as
+ // DTPMOD may not be expected at load time.
+ bool isLocalInExecutable = !sym.isPreemptible && !config->shared;
+
+ // Local Dynamic is for access to module local TLS variables, while still
+ // being suitable for being dynamically loaded via dlopen. GOT[e0] is the
+ // module index, with a special value of 0 for the current module. GOT[e1] is
+ // unused. There only needs to be one module index entry.
+ 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 (!Config->Shared) {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type,
- Offset, Addend, &Sym});
- return Target->TlsGdRelaxSkip;
+ if (canRelax && !config->shared) {
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
+ offset, addend, &sym});
+ return target->getTlsGdRelaxSkip(type);
}
- if (Expr == R_TLSLD_HINT)
+ if (expr == R_TLSLD_HINT)
return 1;
- if (In.Got->addTlsIndex())
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got,
- In.Got->getTlsIndexOff(), nullptr);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ if (in.got->addTlsIndex()) {
+ if (isLocalInExecutable)
+ in.got->relocations.push_back(
+ {R_ADDEND, target->symbolicRel, in.got->getTlsIndexOff(), 1, &sym});
+ else
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got,
+ in.got->getTlsIndexOff(), nullptr);
+ }
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (Expr == R_ABS && !Config->Shared) {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type,
- Offset, Addend, &Sym});
+ if (expr == R_DTPREL && !config->shared) {
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
+ offset, addend, &sym});
return 1;
}
// Local-Dynamic sequence where offset of tls variable relative to dynamic
- // thread pointer is stored in the got.
- if (Expr == R_TLSLD_GOT_OFF) {
- // Local-Dynamic relocs can be relaxed to local-exec
- if (!Config->Shared) {
- C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym});
- return 1;
+ // thread pointer is stored in the got. This cannot be relaxed to Local-Exec.
+ if (expr == R_TLSLD_GOT_OFF) {
+ if (!sym.isInGot()) {
+ in.got->addEntry(sym);
+ uint64_t off = sym.getGotOffset();
+ in.got->relocations.push_back(
+ {R_ABS, target->tlsOffsetRel, off, 0, &sym});
}
- if (!Sym.isInGot()) {
- In.Got->addEntry(Sym);
- uint64_t Off = Sym.getGotOffset();
- In.Got->Relocations.push_back(
- {R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
- }
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
- if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL,
- R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
- if (Config->Shared) {
- if (In.Got->addDynTlsEntry(Sym)) {
- uint64_t Off = In.Got->getGlobalDynOffset(Sym);
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got, Off, &Sym);
+ 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 (in.got->addDynTlsEntry(sym)) {
+ uint64_t off = in.got->getGlobalDynOffset(sym);
+
+ if (isLocalInExecutable)
+ // Write one to the GOT slot.
+ in.got->relocations.push_back(
+ {R_ADDEND, target->symbolicRel, off, 1, &sym});
+ else
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, in.got, off, &sym);
// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
- uint64_t OffsetOff = Off + Config->Wordsize;
- if (Sym.IsPreemptible)
- In.RelaDyn->addReloc(Target->TlsOffsetRel, In.Got, OffsetOff, &Sym);
+ uint64_t offsetOff = off + config->wordsize;
+ if (sym.isPreemptible)
+ mainPart->relaDyn->addReloc(target->tlsOffsetRel, in.got, offsetOff,
+ &sym);
else
- In.Got->Relocations.push_back(
- {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym});
+ in.got->relocations.push_back(
+ {R_ABS, target->tlsOffsetRel, offsetOff, 0, &sym});
}
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ c.relocations.push_back({expr, type, offset, addend, &sym});
return 1;
}
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not.
- if (Sym.IsPreemptible) {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
- Offset, Addend, &Sym});
- if (!Sym.isInGot()) {
- In.Got->addEntry(Sym);
- In.RelaDyn->addReloc(Target->TlsGotRel, In.Got, Sym.getGotOffset(),
- &Sym);
+ if (sym.isPreemptible) {
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_GD_TO_IE), type,
+ offset, addend, &sym});
+ if (!sym.isInGot()) {
+ in.got->addEntry(sym);
+ mainPart->relaDyn->addReloc(target->tlsGotRel, in.got, sym.getGotOffset(),
+ &sym);
}
} else {
- C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type,
- Offset, Addend, &Sym});
+ c.relocations.push_back(
+ {target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_GD_TO_LE), type,
+ offset, addend, &sym});
}
- return Target->TlsGdRelaxSkip;
+ return target->getTlsGdRelaxSkip(type);
}
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
- if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_AARCH64_GOT_PAGE_PC,
- R_GOT_OFF, R_TLSIE_HINT>(Expr) &&
- !Config->Shared && !Sym.IsPreemptible) {
- C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym});
+ if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
+ R_TLSIE_HINT>(expr) &&
+ canRelax && isLocalInExecutable) {
+ c.relocations.push_back({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
return 1;
}
- if (Expr == R_TLSIE_HINT)
+ if (expr == R_TLSIE_HINT)
return 1;
return 0;
}
-static RelType getMipsPairType(RelType Type, bool IsLocal) {
- switch (Type) {
+static RelType getMipsPairType(RelType type, bool isLocal) {
+ switch (type) {
case R_MIPS_HI16:
return R_MIPS_LO16;
case R_MIPS_GOT16:
@@ -309,9 +309,9 @@ static RelType getMipsPairType(RelType Type, bool IsLocal) {
// the high 16 bits of the symbol's value. A paired R_MIPS_LO16
// relocations handle low 16 bits of the address. That allows
// to allocate only one GOT entry for every 64 KBytes of local data.
- return IsLocal ? R_MIPS_LO16 : R_MIPS_NONE;
+ return isLocal ? R_MIPS_LO16 : R_MIPS_NONE;
case R_MICROMIPS_GOT16:
- return IsLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
+ return isLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
case R_MIPS_PCHI16:
return R_MIPS_PCLO16;
case R_MICROMIPS_HI16:
@@ -323,40 +323,38 @@ static RelType getMipsPairType(RelType Type, bool IsLocal) {
// True if non-preemptable symbol always has the same value regardless of where
// the DSO is loaded.
-static bool isAbsolute(const Symbol &Sym) {
- if (Sym.isUndefWeak())
+static bool isAbsolute(const Symbol &sym) {
+ if (sym.isUndefWeak())
return true;
- if (const auto *DR = dyn_cast<Defined>(&Sym))
- return DR->Section == nullptr; // Absolute symbol.
+ if (const auto *dr = dyn_cast<Defined>(&sym))
+ return dr->section == nullptr; // Absolute symbol.
return false;
}
-static bool isAbsoluteValue(const Symbol &Sym) {
- return isAbsolute(Sym) || Sym.isTls();
+static bool isAbsoluteValue(const Symbol &sym) {
+ return isAbsolute(sym) || sym.isTls();
}
// Returns true if Expr refers a PLT entry.
-static bool needsPlt(RelExpr Expr) {
- return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC,
- R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr);
+static bool needsPlt(RelExpr expr) {
+ return oneof<R_PLT_PC, R_PPC32_PLTREL, R_PPC64_CALL_PLT, R_PLT>(expr);
}
// Returns true if Expr refers a GOT entry. Note that this function
// returns false for TLS variables even though they need GOT, because
// TLS variables uses GOT differently than the regular variables.
-static bool needsGot(RelExpr Expr) {
- return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
- R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
- R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END,
- R_GOT_PLT>(Expr);
+static bool needsGot(RelExpr expr) {
+ return oneof<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
+ R_GOT_PC, R_GOTPLT>(expr);
}
// True if this expression is of the form Sym - X, where X is a position in the
// file (PC, or GOT for example).
-static bool isRelExpr(RelExpr Expr) {
- return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL,
- R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC,
- R_RELAX_GOT_PC>(Expr);
+static bool isRelExpr(RelExpr expr) {
+ return oneof<R_PC, R_GOTREL, R_GOTPLTREL, R_MIPS_GOTREL, R_PPC64_CALL,
+ R_PPC64_RELAX_TOC, R_AARCH64_PAGE_PC, R_RELAX_GOT_PC,
+ R_RISCV_PC_INDIRECT>(expr);
}
// Returns true if a given relocation can be computed at link-time.
@@ -368,43 +366,43 @@ static bool isRelExpr(RelExpr Expr) {
//
// If this function returns false, that means we need to emit a
// dynamic relocation so that the relocation will be fixed at load-time.
-static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
- InputSectionBase &S, uint64_t RelOff) {
+static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
+ InputSectionBase &s, uint64_t relOff) {
// These expressions always compute a constant
- if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
- R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
- R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
- R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC,
- R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
- R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT,
- R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT,
- R_TLSLD_HINT, R_TLSIE_HINT>(E))
+ if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
+ R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
+ R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
+ R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, R_PPC32_PLTREL,
+ R_PPC64_CALL_PLT, R_PPC64_RELAX_TOC, R_RISCV_ADD, R_TLSDESC_CALL,
+ R_TLSDESC_PC, R_AARCH64_TLSDESC_PAGE, R_HINT, R_TLSLD_HINT,
+ R_TLSIE_HINT>(e))
return true;
// These never do, except if the entire file is position dependent or if
// only the low bits are used.
- if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC)
- return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
+ if (e == R_GOT || e == R_PLT || e == R_TLSDESC)
+ return target->usesOnlyLowPageBits(type) || !config->isPic;
- if (Sym.IsPreemptible)
+ if (sym.isPreemptible)
return false;
- if (!Config->Pic)
+ if (!config->isPic)
return true;
// The size of a non preemptible symbol is a constant.
- if (E == R_SIZE)
+ if (e == R_SIZE)
return true;
// For the target and the relocation, we want to know if they are
// absolute or relative.
- bool AbsVal = isAbsoluteValue(Sym);
- bool RelE = isRelExpr(E);
- if (AbsVal && !RelE)
+ bool absVal = isAbsoluteValue(sym);
+ bool relE = isRelExpr(e);
+ if (absVal && !relE)
return true;
- if (!AbsVal && RelE)
+ if (!absVal && relE)
return true;
- if (!AbsVal && !RelE)
- return Target->usesOnlyLowPageBits(Type);
+ if (!absVal && !relE)
+ return target->usesOnlyLowPageBits(type);
// Relative relocation to an absolute value. This is normally unrepresentable,
// but if the relocation refers to a weak undefined symbol, we allow it to
@@ -414,59 +412,60 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
// Another special case is MIPS _gp_disp symbol which represents offset
// between start of a function and '_gp' value and defined as absolute just
// to simplify the code.
- assert(AbsVal && RelE);
- if (Sym.isUndefWeak())
+ assert(absVal && relE);
+ if (sym.isUndefWeak())
return true;
- error("relocation " + toString(Type) + " cannot refer to absolute symbol: " +
- toString(Sym) + getLocation(S, Sym, RelOff));
+ // We set the final symbols values for linker script defined symbols later.
+ // They always can be computed as a link time constant.
+ if (sym.scriptDefined)
+ return true;
+
+ error("relocation " + toString(type) + " cannot refer to absolute symbol: " +
+ toString(sym) + getLocation(s, sym, relOff));
return true;
}
-static RelExpr toPlt(RelExpr Expr) {
- switch (Expr) {
- case R_PPC_CALL:
- return R_PPC_CALL_PLT;
+static RelExpr toPlt(RelExpr expr) {
+ switch (expr) {
+ case R_PPC64_CALL:
+ return R_PPC64_CALL_PLT;
case R_PC:
return R_PLT_PC;
- case R_AARCH64_PAGE_PC:
- return R_AARCH64_PLT_PAGE_PC;
- case R_AARCH64_GOT_PAGE_PC:
- return R_AARCH64_GOT_PAGE_PC_PLT;
case R_ABS:
return R_PLT;
- case R_GOT:
- return R_GOT_PLT;
default:
- return Expr;
+ return expr;
}
}
-static RelExpr fromPlt(RelExpr Expr) {
+static RelExpr fromPlt(RelExpr expr) {
// We decided not to use a plt. Optimize a reference to the plt to a
// reference to the symbol itself.
- switch (Expr) {
+ switch (expr) {
case R_PLT_PC:
+ case R_PPC32_PLTREL:
return R_PC;
- case R_PPC_CALL_PLT:
- return R_PPC_CALL;
+ case R_PPC64_CALL_PLT:
+ return R_PPC64_CALL;
case R_PLT:
return R_ABS;
default:
- return Expr;
+ return expr;
}
}
// Returns true if a given shared symbol is in a read-only segment in a DSO.
-template <class ELFT> static bool isReadOnly(SharedSymbol &SS) {
- typedef typename ELFT::Phdr Elf_Phdr;
+template <class ELFT> static bool isReadOnly(SharedSymbol &ss) {
+ using Elf_Phdr = typename ELFT::Phdr;
// Determine if the symbol is read-only by scanning the DSO's program headers.
- const SharedFile<ELFT> &File = SS.getFile<ELFT>();
- for (const Elf_Phdr &Phdr : check(File.getObj().program_headers()))
- if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) &&
- !(Phdr.p_flags & ELF::PF_W) && SS.Value >= Phdr.p_vaddr &&
- SS.Value < Phdr.p_vaddr + Phdr.p_memsz)
+ const SharedFile &file = ss.getFile();
+ for (const Elf_Phdr &phdr :
+ check(file.template getObj<ELFT>().program_headers()))
+ if ((phdr.p_type == ELF::PT_LOAD || phdr.p_type == ELF::PT_GNU_RELRO) &&
+ !(phdr.p_flags & ELF::PF_W) && ss.value >= phdr.p_vaddr &&
+ ss.value < phdr.p_vaddr + phdr.p_memsz)
return true;
return false;
}
@@ -477,22 +476,22 @@ template <class ELFT> static bool isReadOnly(SharedSymbol &SS) {
// them are copied by a copy relocation, all of them need to be copied.
// Otherwise, they would refer to different places at runtime.
template <class ELFT>
-static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
- typedef typename ELFT::Sym Elf_Sym;
+static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &ss) {
+ using Elf_Sym = typename ELFT::Sym;
- SharedFile<ELFT> &File = SS.getFile<ELFT>();
+ SharedFile &file = ss.getFile();
- SmallSet<SharedSymbol *, 4> Ret;
- for (const Elf_Sym &S : File.getGlobalELFSyms()) {
- if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
- S.getType() == STT_TLS || S.st_value != SS.Value)
+ SmallSet<SharedSymbol *, 4> ret;
+ for (const Elf_Sym &s : file.template getGlobalELFSyms<ELFT>()) {
+ if (s.st_shndx == SHN_UNDEF || s.st_shndx == SHN_ABS ||
+ s.getType() == STT_TLS || s.st_value != ss.value)
continue;
- StringRef Name = check(S.getName(File.getStringTable()));
- Symbol *Sym = Symtab->find(Name);
- if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym))
- Ret.insert(Alias);
+ StringRef name = check(s.getName(file.getStringTable()));
+ Symbol *sym = symtab->find(name);
+ if (auto *alias = dyn_cast_or_null<SharedSymbol>(sym))
+ ret.insert(alias);
}
- return Ret;
+ return ret;
}
// When a symbol is copy relocated or we create a canonical plt entry, it is
@@ -500,19 +499,21 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
// in .bss and in the case of a canonical plt entry it is in .plt. This function
// replaces the existing symbol with a Defined pointing to the appropriate
// location.
-static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
- uint64_t Size) {
- Symbol Old = Sym;
- replaceSymbol<Defined>(&Sym, Sym.File, Sym.getName(), Sym.Binding,
- Sym.StOther, Sym.Type, Value, Size, Sec);
- Sym.PltIndex = Old.PltIndex;
- Sym.GotIndex = Old.GotIndex;
- Sym.VerdefIndex = Old.VerdefIndex;
- Sym.PPC64BranchltIndex = Old.PPC64BranchltIndex;
- Sym.IsPreemptible = true;
- Sym.ExportDynamic = true;
- Sym.IsUsedInRegularObj = true;
- Sym.Used = true;
+static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
+ uint64_t size) {
+ Symbol old = sym;
+
+ sym.replace(Defined{sym.file, sym.getName(), sym.binding, sym.stOther,
+ sym.type, value, size, sec});
+
+ sym.pltIndex = old.pltIndex;
+ sym.gotIndex = old.gotIndex;
+ sym.verdefIndex = old.verdefIndex;
+ sym.ppc64BranchltIndex = old.ppc64BranchltIndex;
+ sym.isPreemptible = true;
+ sym.exportDynamic = true;
+ sym.isUsedInRegularObj = true;
+ sym.used = true;
}
// Reserve space in .bss or .bss.rel.ro for copy relocation.
@@ -557,29 +558,29 @@ static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
// to the variable in .bss. This kind of issue is sometimes very hard to
// debug. What's a solution? Instead of exporting a varaible V from a DSO,
// define an accessor getV().
-template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
+template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
// Copy relocation against zero-sized symbol doesn't make sense.
- uint64_t SymSize = SS.getSize();
- if (SymSize == 0 || SS.Alignment == 0)
- fatal("cannot create a copy relocation for symbol " + toString(SS));
+ uint64_t symSize = ss.getSize();
+ if (symSize == 0 || ss.alignment == 0)
+ fatal("cannot create a copy relocation for symbol " + toString(ss));
// See if this symbol is in a read-only segment. If so, preserve the symbol's
// memory protection by reserving space in the .bss.rel.ro section.
- bool IsReadOnly = isReadOnly<ELFT>(SS);
- BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss",
- SymSize, SS.Alignment);
- if (IsReadOnly)
- In.BssRelRo->getParent()->addSection(Sec);
+ bool isRO = isReadOnly<ELFT>(ss);
+ BssSection *sec =
+ make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
+ if (isRO)
+ in.bssRelRo->getParent()->addSection(sec);
else
- In.Bss->getParent()->addSection(Sec);
+ in.bss->getParent()->addSection(sec);
// Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly
// interpose any aliases.
- for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS))
- replaceWithDefined(*Sym, Sec, 0, Sym->Size);
+ for (SharedSymbol *sym : getSymbolsAt<ELFT>(ss))
+ replaceWithDefined(*sym, sec, 0, sym->size);
- In.RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
+ mainPart->relaDyn->addReloc(target->copyRel, sec, 0, &ss);
}
// MIPS has an odd notion of "paired" relocations to calculate addends.
@@ -587,34 +588,34 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
// R_MIPS_LO16 relocation after that, and an addend is calculated using
// the two relocations.
template <class ELFT, class RelTy>
-static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
- InputSectionBase &Sec, RelExpr Expr,
- bool IsLocal) {
- if (Expr == R_MIPS_GOTREL && IsLocal)
- return Sec.getFile<ELFT>()->MipsGp0;
+static int64_t computeMipsAddend(const RelTy &rel, const RelTy *end,
+ InputSectionBase &sec, RelExpr expr,
+ bool isLocal) {
+ if (expr == R_MIPS_GOTREL && isLocal)
+ return sec.getFile<ELFT>()->mipsGp0;
// The ABI says that the paired relocation is used only for REL.
// See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (RelTy::IsRela)
return 0;
- RelType Type = Rel.getType(Config->IsMips64EL);
- uint32_t PairTy = getMipsPairType(Type, IsLocal);
- if (PairTy == R_MIPS_NONE)
+ RelType type = rel.getType(config->isMips64EL);
+ uint32_t pairTy = getMipsPairType(type, isLocal);
+ if (pairTy == R_MIPS_NONE)
return 0;
- const uint8_t *Buf = Sec.data().data();
- uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
+ const uint8_t *buf = sec.data().data();
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
// To make things worse, paired relocations might not be contiguous in
// the relocation table, so we need to do linear search. *sigh*
- for (const RelTy *RI = &Rel; RI != End; ++RI)
- if (RI->getType(Config->IsMips64EL) == PairTy &&
- RI->getSymbol(Config->IsMips64EL) == SymIndex)
- return Target->getImplicitAddend(Buf + RI->r_offset, PairTy);
+ for (const RelTy *ri = &rel; ri != end; ++ri)
+ if (ri->getType(config->isMips64EL) == pairTy &&
+ ri->getSymbol(config->isMips64EL) == symIndex)
+ return target->getImplicitAddend(buf + ri->r_offset, pairTy);
- warn("can't find matching " + toString(PairTy) + " relocation for " +
- toString(Type));
+ warn("can't find matching " + toString(pairTy) + " relocation for " +
+ toString(type));
return 0;
}
@@ -622,59 +623,174 @@ static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
// is in a relocation itself. If it is REL, we need to read it from an
// input section.
template <class ELFT, class RelTy>
-static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
- InputSectionBase &Sec, RelExpr Expr,
- bool IsLocal) {
- int64_t Addend;
- RelType Type = Rel.getType(Config->IsMips64EL);
+static int64_t computeAddend(const RelTy &rel, const RelTy *end,
+ InputSectionBase &sec, RelExpr expr,
+ bool isLocal) {
+ int64_t addend;
+ RelType type = rel.getType(config->isMips64EL);
if (RelTy::IsRela) {
- Addend = getAddend<ELFT>(Rel);
+ addend = getAddend<ELFT>(rel);
} else {
- const uint8_t *Buf = Sec.data().data();
- Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type);
+ const uint8_t *buf = sec.data().data();
+ addend = target->getImplicitAddend(buf + rel.r_offset, type);
}
- if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC)
- Addend += getPPC64TocBase();
- if (Config->EMachine == EM_MIPS)
- Addend += computeMipsAddend<ELFT>(Rel, End, Sec, Expr, IsLocal);
+ if (config->emachine == EM_PPC64 && config->isPic && type == R_PPC64_TOC)
+ addend += getPPC64TocBase();
+ if (config->emachine == EM_MIPS)
+ addend += computeMipsAddend<ELFT>(rel, end, sec, expr, isLocal);
- return Addend;
+ return addend;
}
-// Report an undefined symbol if necessary.
-// Returns true if this function printed out an error message.
-static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
- uint64_t Offset) {
- if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak())
- return false;
+// Custom error message if Sym is defined in a discarded section.
+template <class ELFT>
+static std::string maybeReportDiscarded(Undefined &sym) {
+ auto *file = dyn_cast_or_null<ObjFile<ELFT>>(sym.file);
+ if (!file || !sym.discardedSecIdx ||
+ file->getSections()[sym.discardedSecIdx] != &InputSection::discarded)
+ return "";
+ ArrayRef<Elf_Shdr_Impl<ELFT>> objSections =
+ CHECK(file->getObj().sections(), file);
+
+ std::string msg;
+ if (sym.type == ELF::STT_SECTION) {
+ msg = "relocation refers to a discarded section: ";
+ msg += CHECK(
+ file->getObj().getSectionName(&objSections[sym.discardedSecIdx]), file);
+ } else {
+ msg = "relocation refers to a symbol in a discarded section: " +
+ toString(sym);
+ }
+ msg += "\n>>> defined in " + toString(file);
+
+ Elf_Shdr_Impl<ELFT> elfSec = objSections[sym.discardedSecIdx - 1];
+ if (elfSec.sh_type != SHT_GROUP)
+ return msg;
+
+ // If the discarded section is a COMDAT.
+ StringRef signature = file->getShtGroupSignature(objSections, elfSec);
+ if (const InputFile *prevailing =
+ symtab->comdatGroups.lookup(CachedHashStringRef(signature)))
+ msg += "\n>>> section group signature: " + signature.str() +
+ "\n>>> prevailing definition is in " + toString(prevailing);
+ return msg;
+}
- bool CanBeExternal =
- Sym.computeBinding() != STB_LOCAL && Sym.Visibility == STV_DEFAULT;
- if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal)
- return false;
+// Undefined diagnostics are collected in a vector and emitted once all of
+// them are known, so that some postprocessing on the list of undefined symbols
+// can happen before lld emits diagnostics.
+struct UndefinedDiag {
+ Symbol *sym;
+ struct Loc {
+ InputSectionBase *sec;
+ uint64_t offset;
+ };
+ std::vector<Loc> locs;
+ bool isWarning;
+};
+
+static std::vector<UndefinedDiag> undefs;
+
+template <class ELFT>
+static void reportUndefinedSymbol(const UndefinedDiag &undef) {
+ Symbol &sym = *undef.sym;
+
+ auto visibility = [&]() -> std::string {
+ switch (sym.visibility) {
+ case STV_INTERNAL:
+ return "internal ";
+ case STV_HIDDEN:
+ return "hidden ";
+ case STV_PROTECTED:
+ return "protected ";
+ default:
+ return "";
+ }
+ };
- std::string Msg =
- "undefined symbol: " + toString(Sym) + "\n>>> referenced by ";
+ std::string msg = maybeReportDiscarded<ELFT>(cast<Undefined>(sym));
+ if (msg.empty())
+ msg = "undefined " + visibility() + "symbol: " + toString(sym);
+
+ const size_t maxUndefReferences = 10;
+ size_t i = 0;
+ for (UndefinedDiag::Loc l : undef.locs) {
+ if (i >= maxUndefReferences)
+ break;
+ InputSectionBase &sec = *l.sec;
+ uint64_t offset = l.offset;
+
+ msg += "\n>>> referenced by ";
+ std::string src = sec.getSrcMsg(sym, offset);
+ if (!src.empty())
+ msg += src + "\n>>> ";
+ msg += sec.getObjMsg(offset);
+ i++;
+ }
- std::string Src = Sec.getSrcMsg(Sym, Offset);
- if (!Src.empty())
- Msg += Src + "\n>>> ";
- Msg += Sec.getObjMsg(Offset);
+ if (i < undef.locs.size())
+ msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
+ .str();
- if (Sym.getName().startswith("_ZTV"))
- Msg += "\nthe vtable symbol may be undefined because the class is missing "
+ 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)";
- if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) ||
- Config->NoinhibitExec) {
- warn(Msg);
- return false;
+ if (undef.isWarning)
+ warn(msg);
+ else
+ error(msg);
+}
+
+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;
+ for (UndefinedDiag &undef : undefs) {
+ assert(undef.locs.size() == 1);
+ if (UndefinedDiag *canon = firstRef.lookup(undef.sym)) {
+ canon->locs.push_back(undef.locs[0]);
+ undef.locs.clear();
+ } else
+ firstRef[undef.sym] = &undef;
}
- error(Msg);
- return true;
+ for (const UndefinedDiag &undef : undefs) {
+ if (!undef.locs.empty())
+ reportUndefinedSymbol<ELFT>(undef);
+ }
+ undefs.clear();
+}
+
+// Report an undefined symbol if necessary.
+// Returns true if the undefined symbol will produce an error message.
+template <class ELFT>
+static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
+ uint64_t offset) {
+ if (!sym.isUndefined() || sym.isWeak())
+ return false;
+
+ bool canBeExternal = !sym.isLocal() && sym.computeBinding() != STB_LOCAL &&
+ sym.visibility == STV_DEFAULT;
+ if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
+ return false;
+
+ // clang (as of 2019-06-12) / gcc (as of 8.2.1) PPC64 may emit a .rela.toc
+ // which references a switch table in a discarded .rodata/.text section. The
+ // .toc and the .rela.toc are incorrectly not placed in the comdat. The ELF
+ // spec says references from outside the group to a STB_LOCAL symbol are not
+ // allowed. Work around the bug.
+ if (config->emachine == EM_PPC64 &&
+ cast<Undefined>(sym).discardedSecIdx != 0 && sec.name == ".toc")
+ return false;
+
+ bool isWarning =
+ (config->unresolvedSymbols == UnresolvedPolicy::Warn && canBeExternal) ||
+ config->noinhibitExec;
+ undefs.push_back({&sym, {{&sec, offset}}, isWarning});
+ return !isWarning;
}
// MIPS N32 ABI treats series of successive relocations with the same offset
@@ -682,14 +798,14 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
// packs all relocations into the single relocation record. Here we emulate
// this for the N32 ABI. Iterate over relocation with the same offset and put
// theirs types into the single bit-set.
-template <class RelTy> static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End) {
- RelType Type = 0;
- uint64_t Offset = Rel->r_offset;
-
- int N = 0;
- while (Rel != End && Rel->r_offset == Offset)
- Type |= (Rel++)->getType(Config->IsMips64EL) << (8 * N++);
- return Type;
+template <class RelTy> static RelType getMipsN32RelType(RelTy *&rel, RelTy *end) {
+ RelType type = 0;
+ uint64_t offset = rel->r_offset;
+
+ int n = 0;
+ while (rel != end && rel->r_offset == offset)
+ type |= (rel++)->getType(config->isMips64EL) << (8 * n++);
+ return type;
}
// .eh_frame sections are mergeable input sections, so their input
@@ -706,77 +822,72 @@ template <class RelTy> static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End)
namespace {
class OffsetGetter {
public:
- explicit OffsetGetter(InputSectionBase &Sec) {
- if (auto *Eh = dyn_cast<EhInputSection>(&Sec))
- Pieces = Eh->Pieces;
+ explicit OffsetGetter(InputSectionBase &sec) {
+ if (auto *eh = dyn_cast<EhInputSection>(&sec))
+ pieces = eh->pieces;
}
// Translates offsets in input sections to offsets in output sections.
// Given offset must increase monotonically. We assume that Piece is
- // sorted by InputOff.
- uint64_t get(uint64_t Off) {
- if (Pieces.empty())
- return Off;
-
- while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off)
- ++I;
- if (I == Pieces.size())
+ // sorted by inputOff.
+ uint64_t get(uint64_t off) {
+ if (pieces.empty())
+ return off;
+
+ while (i != pieces.size() && pieces[i].inputOff + pieces[i].size <= off)
+ ++i;
+ if (i == pieces.size())
fatal(".eh_frame: relocation is not in any piece");
// Pieces must be contiguous, so there must be no holes in between.
- assert(Pieces[I].InputOff <= Off && "Relocation not in any piece");
+ assert(pieces[i].inputOff <= off && "Relocation not in any piece");
// Offset -1 means that the piece is dead (i.e. garbage collected).
- if (Pieces[I].OutputOff == -1)
+ if (pieces[i].outputOff == -1)
return -1;
- return Pieces[I].OutputOff + Off - Pieces[I].InputOff;
+ return pieces[i].outputOff + off - pieces[i].inputOff;
}
private:
- ArrayRef<EhSectionPiece> Pieces;
- size_t I = 0;
+ ArrayRef<EhSectionPiece> pieces;
+ size_t i = 0;
};
} // namespace
-static void addRelativeReloc(InputSectionBase *IS, uint64_t OffsetInSec,
- Symbol *Sym, int64_t Addend, RelExpr Expr,
- RelType Type) {
- // Add a relative relocation. If RelrDyn section is enabled, and the
+static void addRelativeReloc(InputSectionBase *isec, uint64_t offsetInSec,
+ Symbol *sym, int64_t addend, RelExpr expr,
+ RelType type) {
+ Partition &part = isec->getPartition();
+
+ // Add a relative relocation. If relrDyn section is enabled, and the
// relocation offset is guaranteed to be even, add the relocation to
- // the RelrDyn section, otherwise add it to the RelaDyn section.
- // RelrDyn sections don't support odd offsets. Also, RelrDyn sections
+ // the relrDyn section, otherwise add it to the relaDyn section.
+ // relrDyn sections don't support odd offsets. Also, relrDyn sections
// don't store the addend values, so we must write it to the relocated
// address.
- if (In.RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
- IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
- In.RelrDyn->Relocs.push_back({IS, OffsetInSec});
+ if (part.relrDyn && isec->alignment >= 2 && offsetInSec % 2 == 0) {
+ isec->relocations.push_back({expr, type, offsetInSec, addend, sym});
+ part.relrDyn->relocs.push_back({isec, offsetInSec});
return;
}
- In.RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, Expr,
- Type);
+ part.relaDyn->addReloc(target->relativeRel, isec, offsetInSec, sym, addend,
+ expr, type);
}
template <class ELFT, class GotPltSection>
-static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
- RelocationBaseSection *Rel, RelType Type, Symbol &Sym) {
- Plt->addEntry<ELFT>(Sym);
- GotPlt->addEntry(Sym);
- Rel->addReloc(
- {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0});
+static void addPltEntry(PltSection *plt, GotPltSection *gotPlt,
+ RelocationBaseSection *rel, RelType type, Symbol &sym) {
+ plt->addEntry<ELFT>(sym);
+ gotPlt->addEntry(sym);
+ rel->addReloc(
+ {type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0});
}
-template <class ELFT> static void addGotEntry(Symbol &Sym) {
- In.Got->addEntry(Sym);
-
- RelExpr Expr;
- if (Sym.isTls())
- Expr = R_TLS;
- else if (Sym.isGnuIFunc())
- Expr = R_PLT;
- else
- Expr = R_ABS;
+static void addGotEntry(Symbol &sym) {
+ in.got->addEntry(sym);
- uint64_t Off = Sym.getGotOffset();
+ RelExpr expr = sym.isTls() ? R_TLS : R_ABS;
+ uint64_t off = sym.getGotOffset();
// If a GOT slot value can be calculated at link-time, which is now,
// we can just fill that out.
@@ -785,42 +896,42 @@ template <class ELFT> static void addGotEntry(Symbol &Sym) {
// add a static relocation to a Relocations vector so that
// InputSection::relocate will do the work for us. We may be able
// to just write a value now, but it is a TODO.)
- bool IsLinkTimeConstant =
- !Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym));
- if (IsLinkTimeConstant) {
- In.Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
+ bool isLinkTimeConstant =
+ !sym.isPreemptible && (!config->isPic || isAbsolute(sym));
+ if (isLinkTimeConstant) {
+ in.got->relocations.push_back({expr, target->symbolicRel, off, 0, &sym});
return;
}
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
// the GOT slot will be fixed at load-time.
- if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) {
- addRelativeReloc(In.Got, Off, &Sym, 0, R_ABS, Target->GotRel);
+ if (!sym.isTls() && !sym.isPreemptible && config->isPic && !isAbsolute(sym)) {
+ addRelativeReloc(in.got, off, &sym, 0, R_ABS, target->symbolicRel);
return;
}
- In.RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel, In.Got,
- Off, &Sym, 0, Sym.IsPreemptible ? R_ADDEND : R_ABS,
- Target->GotRel);
+ mainPart->relaDyn->addReloc(
+ sym.isTls() ? target->tlsGotRel : target->gotRel, in.got, off, &sym, 0,
+ sym.isPreemptible ? R_ADDEND : R_ABS, target->symbolicRel);
}
// Return true if we can define a symbol in the executable that
// contains the value/function of a symbol defined in a shared
// library.
-static bool canDefineSymbolInExecutable(Symbol &Sym) {
+static bool canDefineSymbolInExecutable(Symbol &sym) {
// If the symbol has default visibility the symbol defined in the
// executable will preempt it.
// Note that we want the visibility of the shared symbol itself, not
// the visibility of the symbol in the output file we are producing. That is
- // why we use Sym.StOther.
- if ((Sym.StOther & 0x3) == STV_DEFAULT)
+ // why we use Sym.stOther.
+ if ((sym.stOther & 0x3) == STV_DEFAULT)
return true;
// If we are allowed to break address equality of functions, defining
// a plt entry will allow the program to call the function in the
// .so, but the .so and the executable will no agree on the address
// of the function. Similar logic for objects.
- return ((Sym.isFunc() && Config->IgnoreFunctionAddressEquality) ||
- (Sym.isObject() && Config->IgnoreDataAddressEquality));
+ return ((sym.isFunc() && config->ignoreFunctionAddressEquality) ||
+ (sym.isObject() && config->ignoreDataAddressEquality));
}
// The reason we have to do this early scan is as follows
@@ -837,23 +948,33 @@ static bool canDefineSymbolInExecutable(Symbol &Sym) {
// complicates things for the dynamic linker and means we would have to reserve
// space for the extra PT_LOAD even if we end up not using it.
template <class ELFT, class RelTy>
-static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
- uint64_t Offset, Symbol &Sym, const RelTy &Rel,
- int64_t Addend) {
- if (isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Offset)) {
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
+ uint64_t offset, Symbol &sym, const RelTy &rel,
+ int64_t addend) {
+ // If the relocation is known to be a link-time constant, we know no dynamic
+ // relocation will be created, pass the control to relocateAlloc() or
+ // relocateNonAlloc() to resolve it.
+ //
+ // The behavior of an undefined weak reference is implementation defined. If
+ // the relocation is to a weak undef, and we are producing an executable, let
+ // relocate{,Non}Alloc() resolve it.
+ if (isStaticLinkTimeConstant(expr, type, sym, sec, offset) ||
+ (!config->shared && sym.isUndefWeak())) {
+ sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
- bool CanWrite = (Sec.Flags & SHF_WRITE) || !Config->ZText;
- if (CanWrite) {
- // R_GOT refers to a position in the got, even if the symbol is preemptible.
- bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT;
- if (!IsPreemptibleValue) {
- addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type);
+ bool canWrite = (sec.flags & SHF_WRITE) || !config->zText;
+ if (canWrite) {
+ RelType rel = target->getDynRel(type);
+ if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) {
+ addRelativeReloc(&sec, offset, &sym, addend, expr, type);
return;
- } else if (RelType Rel = Target->getDynRel(Type)) {
- In.RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type);
+ } else if (rel != 0) {
+ if (config->emachine == EM_MIPS && rel == target->symbolicRel)
+ rel = target->relativeRel;
+ sec.getPartition().relaDyn->addReloc(rel, &sec, offset, &sym, addend,
+ R_ADDEND, type);
// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
@@ -870,62 +991,62 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
// to the GOT entry and reads the GOT entry when it needs to perform
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
- if (Config->EMachine == EM_MIPS)
- In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
+ if (config->emachine == EM_MIPS)
+ in.mipsGot->addEntry(*sec.file, sym, addend, expr);
return;
}
}
- // If the relocation is to a weak undef, and we are producing
- // executable, give up on it and produce a non preemptible 0.
- if (!Config->Shared && Sym.isUndefWeak()) {
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- return;
- }
-
- if (!CanWrite && (Config->Pic && !isRelExpr(Expr))) {
+ if (!canWrite && (config->isPic && !isRelExpr(expr))) {
error(
- "can't create dynamic relocation " + toString(Type) + " against " +
- (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) +
+ "can't create dynamic relocation " + toString(type) + " against " +
+ (sym.getName().empty() ? "local symbol" : "symbol: " + toString(sym)) +
" in readonly segment; recompile object files with -fPIC "
"or pass '-Wl,-z,notext' to allow text relocations in the output" +
- getLocation(Sec, Sym, Offset));
+ getLocation(sec, sym, offset));
return;
}
- // Copy relocations are only possible if we are creating an executable.
- if (Config->Shared) {
- errorOrWarn("relocation " + toString(Type) +
- " cannot be used against symbol " + toString(Sym) +
- "; recompile with -fPIC" + getLocation(Sec, Sym, Offset));
+ // Copy relocations (for STT_OBJECT) and canonical PLT (for STT_FUNC) are only
+ // possible in an executable.
+ //
+ // Among R_ABS relocatoin types, symbolicRel has the same size as the word
+ // size. Others have fewer bits and may cause runtime overflow in -pie/-shared
+ // mode. Disallow them.
+ if (config->shared ||
+ (config->pie && expr == R_ABS && type != target->symbolicRel)) {
+ errorOrWarn(
+ "relocation " + toString(type) + " cannot be used against " +
+ (sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
+ "; recompile with -fPIC" + getLocation(sec, sym, offset));
return;
}
// If the symbol is undefined we already reported any relevant errors.
- if (Sym.isUndefined())
+ if (sym.isUndefined())
return;
- if (!canDefineSymbolInExecutable(Sym)) {
- error("cannot preempt symbol: " + toString(Sym) +
- getLocation(Sec, Sym, Offset));
+ if (!canDefineSymbolInExecutable(sym)) {
+ error("cannot preempt symbol: " + toString(sym) +
+ getLocation(sec, sym, offset));
return;
}
- if (Sym.isObject()) {
+ if (sym.isObject()) {
// Produce a copy relocation.
- if (auto *SS = dyn_cast<SharedSymbol>(&Sym)) {
- if (!Config->ZCopyreloc)
- error("unresolvable relocation " + toString(Type) +
- " against symbol '" + toString(*SS) +
+ if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
+ if (!config->zCopyreloc)
+ error("unresolvable relocation " + toString(type) +
+ " against symbol '" + toString(*ss) +
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
- getLocation(Sec, Sym, Offset));
- addCopyRelSymbol<ELFT>(*SS);
+ getLocation(sec, sym, offset));
+ addCopyRelSymbol<ELFT>(*ss);
}
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
- if (Sym.isFunc()) {
+ if (sym.isFunc()) {
// This handles a non PIC program call to function in a shared library. In
// an ideal world, we could just report an error saying the relocation can
// overflow at runtime. In the real world with glibc, crt1.o has a
@@ -953,167 +1074,333 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
// compiled without -fPIE/-fPIC and doesn't maintain ebx.
// * If a library definition gets preempted to the executable, it will have
// the wrong ebx value.
- if (Config->Pie && Config->EMachine == EM_386)
- errorOrWarn("symbol '" + toString(Sym) +
+ if (config->pie && config->emachine == EM_386)
+ errorOrWarn("symbol '" + toString(sym) +
"' cannot be preempted; recompile with -fPIE" +
- getLocation(Sec, Sym, Offset));
- if (!Sym.isInPlt())
- addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
- if (!Sym.isDefined())
- replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0);
- Sym.NeedsPltAddr = true;
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ getLocation(sec, sym, offset));
+ if (!sym.isInPlt())
+ addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
+ if (!sym.isDefined())
+ replaceWithDefined(
+ sym, in.plt,
+ target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
+ sym.needsPltAddr = true;
+ sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
- errorOrWarn("symbol '" + toString(Sym) + "' has no type" +
- getLocation(Sec, Sym, Offset));
+ errorOrWarn("symbol '" + toString(sym) + "' has no type" +
+ getLocation(sec, sym, offset));
}
+struct IRelativeReloc {
+ RelType type;
+ InputSectionBase *sec;
+ uint64_t offset;
+ Symbol *sym;
+};
+
+static std::vector<IRelativeReloc> iRelativeRelocs;
+
template <class ELFT, class RelTy>
-static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
- RelTy *End) {
- const RelTy &Rel = *I;
- Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
- RelType Type;
+static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
+ RelTy *end) {
+ const RelTy &rel = *i;
+ uint32_t symIndex = rel.getSymbol(config->isMips64EL);
+ Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIndex);
+ RelType type;
// Deal with MIPS oddity.
- if (Config->MipsN32Abi) {
- Type = getMipsN32RelType(I, End);
+ if (config->mipsN32Abi) {
+ type = getMipsN32RelType(i, end);
} else {
- Type = Rel.getType(Config->IsMips64EL);
- ++I;
+ type = rel.getType(config->isMips64EL);
+ ++i;
}
// Get an offset in an output section this relocation is applied to.
- uint64_t Offset = GetOffset.get(Rel.r_offset);
- if (Offset == uint64_t(-1))
+ uint64_t offset = getOffset.get(rel.r_offset);
+ if (offset == uint64_t(-1))
return;
- // Skip if the target symbol is an erroneous undefined symbol.
- if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
+ // Error if the target symbol is undefined. Symbol index 0 may be used by
+ // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
+ if (symIndex != 0 && maybeReportUndefined<ELFT>(sym, sec, rel.r_offset))
return;
- const uint8_t *RelocatedAddr = Sec.data().begin() + Rel.r_offset;
- RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr);
+ const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
+ RelExpr expr = target->getRelExpr(type, sym, relocatedAddr);
// Ignore "hint" relocations because they are only markers for relaxation.
- if (isRelExprOneOf<R_HINT, R_NONE>(Expr))
+ if (oneof<R_HINT, R_NONE>(expr))
return;
- // Strenghten or relax relocations.
- //
- // GNU ifunc symbols must be accessed via PLT because their addresses
- // are determined by runtime.
+ // 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 "
+ "old version of glibc (glibc 2.28 and earlier). If this applies to "
+ "you, consider recompiling the object files without -fPIC and "
+ "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
+ "turn off this warning." +
+ getLocation(sec, sym, offset));
+ }
+
+ // Read an addend.
+ int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
+
+ // Relax relocations.
//
- // On the other hand, if we know that a PLT entry will be resolved within
- // the same ELF module, we can skip PLT access and directly jump to the
- // destination function. For example, if we are linking a main exectuable,
- // all dynamic symbols that can be resolved within the executable will
- // actually be resolved that way at runtime, because the main exectuable
- // is always at the beginning of a search list. We can leverage that fact.
- if (Sym.isGnuIFunc()) {
- if (!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 "
- "old version of glibc (glibc 2.28 and earlier). If this applies to "
- "you, consider recompiling the object files without -fPIC and "
- "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
- "turn off this warning." +
- getLocation(Sec, Sym, Offset));
+ // If we know that a PLT entry will be resolved within the same ELF module, we
+ // can skip PLT access and directly jump to the destination function. For
+ // example, if we are linking a main exectuable, all dynamic symbols that can
+ // be resolved within the executable will actually be resolved that way at
+ // runtime, because the main exectuable is always at the beginning of a search
+ // list. We can leverage that fact.
+ if (!sym.isPreemptible && (!sym.isGnuIFunc() || config->zIfuncNoplt)) {
+ if (expr == R_GOT_PC && !isAbsoluteValue(sym)) {
+ expr = target->adjustRelaxExpr(type, relocatedAddr, expr);
+ } else {
+ // Addend of R_PPC_PLTREL24 is used to choose call stub type. It should be
+ // ignored if optimized to R_PC.
+ if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL)
+ addend = 0;
+ expr = fromPlt(expr);
}
- Expr = toPlt(Expr);
- } else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) {
- Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
- } else if (!Sym.IsPreemptible) {
- Expr = fromPlt(Expr);
}
- // This relocation does not require got entry, but it is relative to got and
- // needs it to be created. Here we request for that.
- if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL,
- R_GOTREL_FROM_END, R_PPC_TOC>(Expr))
- In.Got->HasGotOffRel = true;
-
- // Read an addend.
- int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
+ // If the relocation does not emit a GOT or GOTPLT entry but its computation
+ // uses their addresses, we need GOT or GOTPLT to be created.
+ //
+ // The 4 types that relative GOTPLT are all x86 and x86-64 specific.
+ if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
+ in.gotPlt->hasGotPltOffRel = true;
+ } else if (oneof<R_GOTONLY_PC, R_GOTREL, R_PPC64_TOCBASE, R_PPC64_RELAX_TOC>(
+ expr)) {
+ in.got->hasGotOffRel = true;
+ }
// Process some TLS relocations, including relaxing TLS relocations.
// Note that this function does not handle all TLS relocations.
- if (unsigned Processed =
- handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) {
- I += (Processed - 1);
+ if (unsigned processed =
+ handleTlsRelocation<ELFT>(type, sym, sec, offset, addend, expr)) {
+ i += (processed - 1);
return;
}
- // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
- if (needsPlt(Expr) && !Sym.isInPlt()) {
- if (Sym.isGnuIFunc() && !Sym.IsPreemptible)
- addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
- Sym);
- else
- addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
+ // We were asked not to generate PLT entries for ifuncs. Instead, pass the
+ // direct relocation on through.
+ if (sym.isGnuIFunc() && config->zIfuncNoplt) {
+ sym.exportDynamic = true;
+ mainPart->relaDyn->addReloc(type, &sec, offset, &sym, addend, R_ADDEND, type);
+ return;
}
- // Create a GOT slot if a relocation needs GOT.
- if (needsGot(Expr)) {
- if (Config->EMachine == EM_MIPS) {
- // MIPS ABI has special rules to process GOT entries and doesn't
- // require relocation entries for them. A special case is TLS
- // relocations. In that case dynamic loader applies dynamic
- // relocations to initialize TLS GOT entries.
- // See "Global Offset Table" in Chapter 5 in the following document
- // for detailed description:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
- } else if (!Sym.isInGot()) {
- addGotEntry<ELFT>(Sym);
+ // Non-preemptible ifuncs require special handling. First, handle the usual
+ // case where the symbol isn't one of these.
+ if (!sym.isGnuIFunc() || sym.isPreemptible) {
+ // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
+ if (needsPlt(expr) && !sym.isInPlt())
+ addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
+
+ // Create a GOT slot if a relocation needs GOT.
+ if (needsGot(expr)) {
+ if (config->emachine == EM_MIPS) {
+ // MIPS ABI has special rules to process GOT entries and doesn't
+ // require relocation entries for them. A special case is TLS
+ // relocations. In that case dynamic loader applies dynamic
+ // relocations to initialize TLS GOT entries.
+ // See "Global Offset Table" in Chapter 5 in the following document
+ // for detailed description:
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+ in.mipsGot->addEntry(*sec.file, sym, addend, expr);
+ } else if (!sym.isInGot()) {
+ addGotEntry(sym);
+ }
+ }
+ } else {
+ // Handle a reference to a non-preemptible ifunc. These are special in a
+ // few ways:
+ //
+ // - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
+ // a fixed value. But assuming that all references to the ifunc are
+ // GOT-generating or PLT-generating, the handling of an ifunc is
+ // relatively straightforward. We create a PLT entry in Iplt, which is
+ // usually at the end of .plt, which makes an indirect call using a
+ // matching GOT entry in igotPlt, which is usually at the end of .got.plt.
+ // The GOT entry is relocated using an IRELATIVE relocation in relaIplt,
+ // which is usually at the end of .rela.plt. Unlike most relocations in
+ // .rela.plt, which may be evaluated lazily without -z now, dynamic
+ // loaders evaluate IRELATIVE relocs eagerly, which means that for
+ // IRELATIVE relocs only, GOT-generating relocations can point directly to
+ // .got.plt without requiring a separate GOT entry.
+ //
+ // - Despite the fact that an ifunc does not have a fixed value, compilers
+ // that are not passed -fPIC will assume that they do, and will emit
+ // direct (non-GOT-generating, non-PLT-generating) relocations to the
+ // symbol. This means that if a direct relocation to the symbol is
+ // seen, the linker must set a value for the symbol, and this value must
+ // be consistent no matter what type of reference is made to the symbol.
+ // This can be done by creating a PLT entry for the symbol in the way
+ // described above and making it canonical, that is, making all references
+ // point to the PLT entry instead of the resolver. In lld we also store
+ // the address of the PLT entry in the dynamic symbol table, which means
+ // that the symbol will also have the same value in other modules.
+ // Because the value loaded from the GOT needs to be consistent with
+ // the value computed using a direct relocation, a non-preemptible ifunc
+ // may end up with two GOT entries, one in .got.plt that points to the
+ // address returned by the resolver and is used only by the PLT entry,
+ // and another in .got that points to the PLT entry and is used by
+ // GOT-generating relocations.
+ //
+ // - The fact that these symbols do not have a fixed value makes them an
+ // exception to the general rule that a statically linked executable does
+ // not require any form of dynamic relocation. To handle these relocations
+ // correctly, the IRELATIVE relocations are stored in an array which a
+ // statically linked executable's startup code must enumerate using the
+ // linker-defined symbols __rela?_iplt_{start,end}.
+ //
+ // - An absolute relocation to a non-preemptible ifunc (such as a global
+ // variable containing a pointer to the ifunc) needs to be relocated in
+ // the exact same way as a GOT entry, so we can avoid needing to make the
+ // PLT entry canonical by translating such relocations into IRELATIVE
+ // relocations in the relaIplt.
+ if (!sym.isInPlt()) {
+ // Create PLT and GOTPLT slots for the symbol.
+ sym.isInIplt = true;
+
+ // Create a copy of the symbol to use as the target of the IRELATIVE
+ // relocation in the igotPlt. This is in case we make the PLT canonical
+ // later, which would overwrite the original symbol.
+ //
+ // FIXME: Creating a copy of the symbol here is a bit of a hack. All
+ // that's really needed to create the IRELATIVE is the section and value,
+ // so ideally we should just need to copy those.
+ auto *directSym = make<Defined>(cast<Defined>(sym));
+ addPltEntry<ELFT>(in.iplt, in.igotPlt, in.relaIplt, target->iRelativeRel,
+ *directSym);
+ sym.pltIndex = directSym->pltIndex;
+ }
+ if (expr == R_ABS && addend == 0 && (sec.flags & SHF_WRITE)) {
+ // We might be able to represent this as an IRELATIVE. But we don't know
+ // yet whether some later relocation will make the symbol point to a
+ // canonical PLT, which would make this either a dynamic RELATIVE (PIC) or
+ // static (non-PIC) relocation. So we keep a record of the information
+ // required to process the relocation, and after scanRelocs() has been
+ // called on all relocations, the relocation is resolved by
+ // addIRelativeRelocs().
+ iRelativeRelocs.push_back({type, &sec, offset, &sym});
+ return;
+ }
+ if (needsGot(expr)) {
+ // Redirect GOT accesses to point to the Igot.
+ //
+ // This field is also used to keep track of whether we ever needed a GOT
+ // entry. If we did and we make the PLT canonical later, we'll need to
+ // create a GOT entry pointing to the PLT entry for Sym.
+ sym.gotInIgot = true;
+ } else if (!needsPlt(expr)) {
+ // Make the ifunc's PLT entry canonical by changing the value of its
+ // symbol to redirect all references to point to it.
+ unsigned entryOffset = sym.pltIndex * target->pltEntrySize;
+ if (config->zRetpolineplt)
+ entryOffset += target->pltHeaderSize;
+
+ auto &d = cast<Defined>(sym);
+ d.section = in.iplt;
+ d.value = entryOffset;
+ d.size = 0;
+ // It's important to set the symbol type here so that dynamic loaders
+ // don't try to call the PLT as if it were an ifunc resolver.
+ d.type = STT_FUNC;
+
+ if (sym.gotInIgot) {
+ // We previously encountered a GOT generating reference that we
+ // redirected to the Igot. Now that the PLT entry is canonical we must
+ // clear the redirection to the Igot and add a GOT entry. As we've
+ // changed the symbol type to STT_FUNC future GOT generating references
+ // will naturally use this GOT entry.
+ //
+ // We don't need to worry about creating a MIPS GOT here because ifuncs
+ // aren't a thing on MIPS.
+ sym.gotInIgot = false;
+ addGotEntry(sym);
+ }
}
}
- processRelocAux<ELFT>(Sec, Expr, Type, Offset, Sym, Rel, Addend);
+ processRelocAux<ELFT>(sec, expr, type, offset, sym, rel, addend);
}
template <class ELFT, class RelTy>
-static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
- OffsetGetter GetOffset(Sec);
+static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
+ OffsetGetter getOffset(sec);
// Not all relocations end up in Sec.Relocations, but a lot do.
- Sec.Relocations.reserve(Rels.size());
-
- for (auto I = Rels.begin(), End = Rels.end(); I != End;)
- scanReloc<ELFT>(Sec, GetOffset, I, End);
-
- // Sort relocations by offset to binary search for R_RISCV_PCREL_HI20
- if (Config->EMachine == EM_RISCV)
- std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(),
- RelocationOffsetComparator{});
+ sec.relocations.reserve(rels.size());
+
+ for (auto i = rels.begin(), end = rels.end(); i != end;)
+ scanReloc<ELFT>(sec, getOffset, i, end);
+
+ // Sort relocations by offset for more efficient searching for
+ // R_RISCV_PCREL_HI20 and R_PPC64_ADDR64.
+ if (config->emachine == EM_RISCV ||
+ (config->emachine == EM_PPC64 && sec.name == ".toc"))
+ llvm::stable_sort(sec.relocations,
+ [](const Relocation &lhs, const Relocation &rhs) {
+ return lhs.offset < rhs.offset;
+ });
}
-template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
- if (S.AreRelocsRela)
- scanRelocs<ELFT>(S, S.relas<ELFT>());
+template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
+ if (s.areRelocsRela)
+ scanRelocs<ELFT>(s, s.relas<ELFT>());
else
- scanRelocs<ELFT>(S, S.rels<ELFT>());
+ scanRelocs<ELFT>(s, s.rels<ELFT>());
+}
+
+// Figure out which representation to use for any absolute relocs to
+// non-preemptible ifuncs that we visited during scanRelocs().
+void elf::addIRelativeRelocs() {
+ for (IRelativeReloc &r : iRelativeRelocs) {
+ if (r.sym->type == STT_GNU_IFUNC)
+ in.relaIplt->addReloc(
+ {target->iRelativeRel, r.sec, r.offset, true, r.sym, 0});
+ else if (config->isPic)
+ addRelativeReloc(r.sec, r.offset, r.sym, 0, R_ABS, r.type);
+ else
+ r.sec->relocations.push_back({R_ABS, r.type, r.offset, 0, r.sym});
+ }
+ iRelativeRelocs.clear();
}
-static bool mergeCmp(const InputSection *A, const InputSection *B) {
+static bool mergeCmp(const InputSection *a, const InputSection *b) {
// std::merge requires a strict weak ordering.
- if (A->OutSecOff < B->OutSecOff)
+ if (a->outSecOff < b->outSecOff)
return true;
- if (A->OutSecOff == B->OutSecOff) {
- auto *TA = dyn_cast<ThunkSection>(A);
- auto *TB = dyn_cast<ThunkSection>(B);
+ if (a->outSecOff == b->outSecOff) {
+ auto *ta = dyn_cast<ThunkSection>(a);
+ auto *tb = dyn_cast<ThunkSection>(b);
// Check if Thunk is immediately before any specific Target
// InputSection for example Mips LA25 Thunks.
- if (TA && TA->getTargetInputSection() == B)
+ if (ta && ta->getTargetInputSection() == b)
return true;
// Place Thunk Sections without specific targets before
// non-Thunk Sections.
- if (TA && !TB && !TA->getTargetInputSection())
+ if (ta && !tb && !ta->getTargetInputSection())
return true;
}
@@ -1123,14 +1410,14 @@ static bool mergeCmp(const InputSection *A, const InputSection *B) {
// Call Fn on every executable InputSection accessed via the linker script
// InputSectionDescription::Sections.
static void forEachInputSectionDescription(
- ArrayRef<OutputSection *> OutputSections,
- llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
- for (OutputSection *OS : OutputSections) {
- if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
+ ArrayRef<OutputSection *> outputSections,
+ llvm::function_ref<void(OutputSection *, InputSectionDescription *)> fn) {
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
continue;
- for (BaseCommand *BC : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
- Fn(OS, ISD);
+ for (BaseCommand *bc : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(bc))
+ fn(os, isd);
}
}
@@ -1225,54 +1512,54 @@ static void forEachInputSectionDescription(
// in the Sections vector, and recalculate the InputSection output section
// offsets.
// This may invalidate any output section offsets stored outside of InputSection
-void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
+void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> outputSections) {
forEachInputSectionDescription(
- OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
- if (ISD->ThunkSections.empty())
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ if (isd->thunkSections.empty())
return;
// Remove any zero sized precreated Thunks.
- llvm::erase_if(ISD->ThunkSections,
- [](const std::pair<ThunkSection *, uint32_t> &TS) {
- return TS.first->getSize() == 0;
+ llvm::erase_if(isd->thunkSections,
+ [](const std::pair<ThunkSection *, uint32_t> &ts) {
+ return ts.first->getSize() == 0;
});
// ISD->ThunkSections contains all created ThunkSections, including
// those inserted in previous passes. Extract the Thunks created this
- // pass and order them in ascending OutSecOff.
- std::vector<ThunkSection *> NewThunks;
- for (const std::pair<ThunkSection *, uint32_t> TS : ISD->ThunkSections)
- if (TS.second == Pass)
- NewThunks.push_back(TS.first);
- std::stable_sort(NewThunks.begin(), NewThunks.end(),
- [](const ThunkSection *A, const ThunkSection *B) {
- return A->OutSecOff < B->OutSecOff;
- });
-
- // Merge sorted vectors of Thunks and InputSections by OutSecOff
- std::vector<InputSection *> Tmp;
- Tmp.reserve(ISD->Sections.size() + NewThunks.size());
-
- std::merge(ISD->Sections.begin(), ISD->Sections.end(),
- NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp),
+ // pass and order them in ascending outSecOff.
+ std::vector<ThunkSection *> newThunks;
+ for (const std::pair<ThunkSection *, uint32_t> ts : isd->thunkSections)
+ if (ts.second == pass)
+ newThunks.push_back(ts.first);
+ llvm::stable_sort(newThunks,
+ [](const ThunkSection *a, const ThunkSection *b) {
+ return a->outSecOff < b->outSecOff;
+ });
+
+ // Merge sorted vectors of Thunks and InputSections by outSecOff
+ std::vector<InputSection *> tmp;
+ tmp.reserve(isd->sections.size() + newThunks.size());
+
+ std::merge(isd->sections.begin(), isd->sections.end(),
+ newThunks.begin(), newThunks.end(), std::back_inserter(tmp),
mergeCmp);
- ISD->Sections = std::move(Tmp);
+ isd->sections = std::move(tmp);
});
}
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
// is in range of Src. An ISD maps to a range of InputSections described by a
// linker script section pattern such as { .text .text.* }.
-ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS, InputSection *IS,
- InputSectionDescription *ISD,
- uint32_t Type, uint64_t Src) {
- for (std::pair<ThunkSection *, uint32_t> TP : ISD->ThunkSections) {
- ThunkSection *TS = TP.first;
- uint64_t TSBase = OS->Addr + TS->OutSecOff;
- uint64_t TSLimit = TSBase + TS->getSize();
- if (Target->inBranchRange(Type, Src, (Src > TSLimit) ? TSBase : TSLimit))
- return TS;
+ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os, InputSection *isec,
+ InputSectionDescription *isd,
+ uint32_t type, uint64_t src) {
+ for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
+ ThunkSection *ts = tp.first;
+ uint64_t tsBase = os->addr + ts->outSecOff;
+ uint64_t tsLimit = tsBase + ts->getSize();
+ if (target->inBranchRange(type, src, (src > tsLimit) ? tsBase : tsLimit))
+ return ts;
}
// No suitable ThunkSection exists. This can happen when there is a branch
@@ -1280,40 +1567,40 @@ ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS, InputSection *IS,
// many Thunks. Create a new ThunkSection as close to the InputSection as
// possible. Error if InputSection is so large we cannot place ThunkSection
// anywhere in Range.
- uint64_t ThunkSecOff = IS->OutSecOff;
- if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff)) {
- ThunkSecOff = IS->OutSecOff + IS->getSize();
- if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff))
+ uint64_t thunkSecOff = isec->outSecOff;
+ if (!target->inBranchRange(type, src, os->addr + thunkSecOff)) {
+ thunkSecOff = isec->outSecOff + isec->getSize();
+ if (!target->inBranchRange(type, src, os->addr + thunkSecOff))
fatal("InputSection too large for range extension thunk " +
- IS->getObjMsg(Src - (OS->Addr + IS->OutSecOff)));
+ isec->getObjMsg(src - (os->addr + isec->outSecOff)));
}
- return addThunkSection(OS, ISD, ThunkSecOff);
+ return addThunkSection(os, isd, thunkSecOff);
}
// Add a Thunk that needs to be placed in a ThunkSection that immediately
// precedes its Target.
-ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
- ThunkSection *TS = ThunkedSections.lookup(IS);
- if (TS)
- return TS;
+ThunkSection *ThunkCreator::getISThunkSec(InputSection *isec) {
+ ThunkSection *ts = thunkedSections.lookup(isec);
+ if (ts)
+ return ts;
// Find InputSectionRange within Target Output Section (TOS) that the
// InputSection (IS) that we need to precede is in.
- OutputSection *TOS = IS->getParent();
- for (BaseCommand *BC : TOS->SectionCommands) {
- auto *ISD = dyn_cast<InputSectionDescription>(BC);
- if (!ISD || ISD->Sections.empty())
+ OutputSection *tos = isec->getParent();
+ for (BaseCommand *bc : tos->sectionCommands) {
+ auto *isd = dyn_cast<InputSectionDescription>(bc);
+ if (!isd || isd->sections.empty())
continue;
- InputSection *First = ISD->Sections.front();
- InputSection *Last = ISD->Sections.back();
+ InputSection *first = isd->sections.front();
+ InputSection *last = isd->sections.back();
- if (IS->OutSecOff < First->OutSecOff || Last->OutSecOff < IS->OutSecOff)
+ if (isec->outSecOff < first->outSecOff || last->outSecOff < isec->outSecOff)
continue;
- TS = addThunkSection(TOS, ISD, IS->OutSecOff);
- ThunkedSections[IS] = TS;
- return TS;
+ ts = addThunkSection(tos, isd, isec->outSecOff);
+ thunkedSections[isec] = ts;
+ return ts;
}
return nullptr;
@@ -1336,82 +1623,93 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
// distance from a thunk to its target will be sufficiently small to
// allow for the creation of a short thunk.
void ThunkCreator::createInitialThunkSections(
- ArrayRef<OutputSection *> OutputSections) {
- uint32_t ThunkSectionSpacing = Target->getThunkSectionSpacing();
+ ArrayRef<OutputSection *> outputSections) {
+ uint32_t thunkSectionSpacing = target->getThunkSectionSpacing();
forEachInputSectionDescription(
- OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
- if (ISD->Sections.empty())
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ if (isd->sections.empty())
return;
- uint32_t ISDBegin = ISD->Sections.front()->OutSecOff;
- uint32_t ISDEnd =
- ISD->Sections.back()->OutSecOff + ISD->Sections.back()->getSize();
- uint32_t LastThunkLowerBound = -1;
- if (ISDEnd - ISDBegin > ThunkSectionSpacing * 2)
- LastThunkLowerBound = ISDEnd - ThunkSectionSpacing;
-
- uint32_t ISLimit;
- uint32_t PrevISLimit = ISDBegin;
- uint32_t ThunkUpperBound = ISDBegin + ThunkSectionSpacing;
-
- for (const InputSection *IS : ISD->Sections) {
- ISLimit = IS->OutSecOff + IS->getSize();
- if (ISLimit > ThunkUpperBound) {
- addThunkSection(OS, ISD, PrevISLimit);
- ThunkUpperBound = PrevISLimit + ThunkSectionSpacing;
+ uint32_t isdBegin = isd->sections.front()->outSecOff;
+ uint32_t isdEnd =
+ isd->sections.back()->outSecOff + isd->sections.back()->getSize();
+ uint32_t lastThunkLowerBound = -1;
+ if (isdEnd - isdBegin > thunkSectionSpacing * 2)
+ lastThunkLowerBound = isdEnd - thunkSectionSpacing;
+
+ uint32_t isecLimit;
+ uint32_t prevIsecLimit = isdBegin;
+ uint32_t thunkUpperBound = isdBegin + thunkSectionSpacing;
+
+ for (const InputSection *isec : isd->sections) {
+ isecLimit = isec->outSecOff + isec->getSize();
+ if (isecLimit > thunkUpperBound) {
+ addThunkSection(os, isd, prevIsecLimit);
+ thunkUpperBound = prevIsecLimit + thunkSectionSpacing;
}
- if (ISLimit > LastThunkLowerBound)
+ if (isecLimit > lastThunkLowerBound)
break;
- PrevISLimit = ISLimit;
+ prevIsecLimit = isecLimit;
}
- addThunkSection(OS, ISD, ISLimit);
+ addThunkSection(os, isd, isecLimit);
});
}
-ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
- InputSectionDescription *ISD,
- uint64_t Off) {
- auto *TS = make<ThunkSection>(OS, Off);
- ISD->ThunkSections.push_back({TS, Pass});
- return TS;
+ThunkSection *ThunkCreator::addThunkSection(OutputSection *os,
+ InputSectionDescription *isd,
+ uint64_t off) {
+ auto *ts = make<ThunkSection>(os, off);
+ ts->partition = os->partition;
+ isd->thunkSections.push_back({ts, pass});
+ return ts;
}
-std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
- uint64_t Src) {
- std::vector<Thunk *> *ThunkVec = nullptr;
+static bool isThunkSectionCompatible(InputSection *source,
+ SectionBase *target) {
+ // We can't reuse thunks in different loadable partitions because they might
+ // not be loaded. But partition 1 (the main partition) will always be loaded.
+ if (source->partition != target->partition)
+ return target->partition == 1;
+ return true;
+}
+
+std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
+ Relocation &rel, uint64_t src) {
+ std::vector<Thunk *> *thunkVec = nullptr;
// We use (section, offset) pair to find the thunk position if possible so
// that we create only one thunk for aliased symbols or ICFed sections.
- if (auto *D = dyn_cast<Defined>(&Sym))
- if (!D->isInPlt() && D->Section)
- ThunkVec = &ThunkedSymbolsBySection[{D->Section->Repl, D->Value}];
- if (!ThunkVec)
- ThunkVec = &ThunkedSymbols[&Sym];
+ if (auto *d = dyn_cast<Defined>(rel.sym))
+ if (!d->isInPlt() && d->section)
+ thunkVec = &thunkedSymbolsBySection[{d->section->repl, d->value}];
+ if (!thunkVec)
+ thunkVec = &thunkedSymbols[rel.sym];
// Check existing Thunks for Sym to see if they can be reused
- for (Thunk *T : *ThunkVec)
- if (T->isCompatibleWith(Type) &&
- Target->inBranchRange(Type, Src, T->getThunkTargetSym()->getVA()))
- return std::make_pair(T, false);
+ for (Thunk *t : *thunkVec)
+ if (isThunkSectionCompatible(isec, t->getThunkTargetSym()->section) &&
+ t->isCompatibleWith(*isec, rel) &&
+ target->inBranchRange(rel.type, src, t->getThunkTargetSym()->getVA()))
+ return std::make_pair(t, false);
// No existing compatible Thunk in range, create a new one
- Thunk *T = addThunk(Type, Sym);
- ThunkVec->push_back(T);
- return std::make_pair(T, true);
+ Thunk *t = addThunk(*isec, rel);
+ thunkVec->push_back(t);
+ return std::make_pair(t, true);
}
// Return true if the relocation target is an in range Thunk.
// Return false if the relocation is not to a Thunk. If the relocation target
// was originally to a Thunk, but is no longer in range we revert the
// relocation back to its original non-Thunk target.
-bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
- if (Thunk *T = Thunks.lookup(Rel.Sym)) {
- if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA()))
+bool ThunkCreator::normalizeExistingThunk(Relocation &rel, uint64_t src) {
+ if (Thunk *t = thunks.lookup(rel.sym)) {
+ if (target->inBranchRange(rel.type, src, rel.sym->getVA()))
return true;
- Rel.Sym = &T->Destination;
- if (Rel.Sym->isInPlt())
- Rel.Expr = toPlt(Rel.Expr);
+ rel.sym = &t->destination;
+ if (rel.sym->isInPlt())
+ rel.expr = toPlt(rel.expr);
}
return false;
}
@@ -1441,15 +1739,15 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
// made no changes. If the target requires range extension thunks, currently
// ARM, then any future change in offset between caller and callee risks a
// relocation out of range error.
-bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
- bool AddressesChanged = false;
+bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
+ bool addressesChanged = false;
- if (Pass == 0 && Target->getThunkSectionSpacing())
- createInitialThunkSections(OutputSections);
+ if (pass == 0 && target->getThunkSectionSpacing())
+ createInitialThunkSections(outputSections);
// With Thunk Size much smaller than branch range we expect to
// converge quickly; if we get to 10 something has gone wrong.
- if (Pass == 10)
+ if (pass == 10)
fatal("thunk creation not converged");
// Create all the Thunks and insert them into synthetic ThunkSections. The
@@ -1458,55 +1756,64 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
// ThunkSections as ThunkSections are not always inserted into the same
// InputSectionDescription as the caller.
forEachInputSectionDescription(
- OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
- for (InputSection *IS : ISD->Sections)
- for (Relocation &Rel : IS->Relocations) {
- uint64_t Src = IS->getVA(Rel.Offset);
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ for (InputSection *isec : isd->sections)
+ for (Relocation &rel : isec->relocations) {
+ uint64_t src = isec->getVA(rel.offset);
// If we are a relocation to an existing Thunk, check if it is
// still in range. If not then Rel will be altered to point to its
// original target so another Thunk can be generated.
- if (Pass > 0 && normalizeExistingThunk(Rel, Src))
+ if (pass > 0 && normalizeExistingThunk(rel, src))
continue;
- if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src,
- *Rel.Sym))
+ if (!target->needsThunk(rel.expr, rel.type, isec->file, src,
+ *rel.sym))
continue;
- Thunk *T;
- bool IsNew;
- std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src);
+ Thunk *t;
+ bool isNew;
+ std::tie(t, isNew) = getThunk(isec, rel, src);
- if (IsNew) {
+ if (isNew) {
// Find or create a ThunkSection for the new Thunk
- ThunkSection *TS;
- if (auto *TIS = T->getTargetInputSection())
- TS = getISThunkSec(TIS);
+ ThunkSection *ts;
+ if (auto *tis = t->getTargetInputSection())
+ ts = getISThunkSec(tis);
else
- TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src);
- TS->addThunk(T);
- Thunks[T->getThunkTargetSym()] = T;
+ ts = getISDThunkSec(os, isec, isd, rel.type, src);
+ ts->addThunk(t);
+ thunks[t->getThunkTargetSym()] = t;
}
// Redirect relocation to Thunk, we never go via the PLT to a Thunk
- Rel.Sym = T->getThunkTargetSym();
- Rel.Expr = fromPlt(Rel.Expr);
+ rel.sym = t->getThunkTargetSym();
+ rel.expr = fromPlt(rel.expr);
+
+ // The addend of R_PPC_PLTREL24 should be ignored after changing to
+ // R_PC.
+ if (config->emachine == EM_PPC && rel.type == R_PPC_PLTREL24)
+ rel.addend = 0;
}
- for (auto &P : ISD->ThunkSections)
- AddressesChanged |= P.first->assignOffsets();
+ for (auto &p : isd->thunkSections)
+ addressesChanged |= p.first->assignOffsets();
});
- for (auto &P : ThunkedSections)
- AddressesChanged |= P.second->assignOffsets();
+ for (auto &p : thunkedSections)
+ addressesChanged |= p.second->assignOffsets();
// Merge all created synthetic ThunkSections back into OutputSection
- mergeThunks(OutputSections);
- ++Pass;
- return AddressesChanged;
+ mergeThunks(outputSections);
+ ++pass;
+ return addressesChanged;
}
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/ELF/Relocations.h b/ELF/Relocations.h
index d00e68bd36e6..d74d7b9b458e 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -1,9 +1,8 @@
//===- Relocations.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -24,136 +23,100 @@ class OutputSection;
class SectionBase;
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
-typedef uint32_t RelType;
+using RelType = uint32_t;
// List of target-independent relocation types. Relocations read
// from files are converted to these types so that the main code
// doesn't have to know about architecture-specific details.
enum RelExpr {
- R_INVALID,
R_ABS,
R_ADDEND,
- R_AARCH64_GOT_PAGE_PC,
- // The expression is used for IFUNC support. Describes PC-relative
- // address of the memory page of GOT entry. This entry is used for
- // a redirection to IPLT.
- R_AARCH64_GOT_PAGE_PC_PLT,
- R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
- R_AARCH64_PAGE_PC,
- R_AARCH64_PLT_PAGE_PC,
- R_AARCH64_TLSDESC_PAGE,
- R_ARM_SBREL,
+ R_DTPREL,
R_GOT,
- // The expression is used for IFUNC support. Evaluates to GOT entry,
- // containing redirection to the IPLT.
- R_GOT_PLT,
- R_GOTONLY_PC,
- R_GOTONLY_PC_FROM_END,
- R_GOTREL,
- R_GOTREL_FROM_END,
- R_GOT_FROM_END,
R_GOT_OFF,
R_GOT_PC,
- R_HEXAGON_GOT,
+ R_GOTONLY_PC,
+ R_GOTPLTONLY_PC,
+ R_GOTPLT,
+ R_GOTPLTREL,
+ R_GOTREL,
R_HINT,
- R_MIPS_GOTREL,
- R_MIPS_GOT_GP,
- R_MIPS_GOT_GP_PC,
- R_MIPS_GOT_LOCAL_PAGE,
- R_MIPS_GOT_OFF,
- R_MIPS_GOT_OFF32,
- R_MIPS_TLSGD,
- R_MIPS_TLSLD,
R_NEG_TLS,
R_NONE,
R_PC,
R_PLT,
R_PLT_PC,
- R_PPC_CALL,
- R_PPC_CALL_PLT,
- R_PPC_TOC,
R_RELAX_GOT_PC,
R_RELAX_GOT_PC_NOPIC,
R_RELAX_TLS_GD_TO_IE,
R_RELAX_TLS_GD_TO_IE_ABS,
- R_RELAX_TLS_GD_TO_IE_END,
R_RELAX_TLS_GD_TO_IE_GOT_OFF,
+ R_RELAX_TLS_GD_TO_IE_GOTPLT,
R_RELAX_TLS_GD_TO_LE,
R_RELAX_TLS_GD_TO_LE_NEG,
R_RELAX_TLS_IE_TO_LE,
R_RELAX_TLS_LD_TO_LE,
R_RELAX_TLS_LD_TO_LE_ABS,
- R_RISCV_PC_INDIRECT,
R_SIZE,
R_TLS,
R_TLSDESC,
R_TLSDESC_CALL,
+ R_TLSDESC_PC,
R_TLSGD_GOT,
- R_TLSGD_GOT_FROM_END,
+ R_TLSGD_GOTPLT,
R_TLSGD_PC,
R_TLSIE_HINT,
R_TLSLD_GOT,
- R_TLSLD_GOT_FROM_END,
+ R_TLSLD_GOTPLT,
R_TLSLD_GOT_OFF,
R_TLSLD_HINT,
R_TLSLD_PC,
-};
-// Build a bitmask with one bit set for each RelExpr.
-//
-// Constexpr function arguments can't be used in static asserts, so we
-// use template arguments to build the mask.
-// But function template partial specializations don't exist (needed
-// for base case of the recursion), so we need a dummy struct.
-template <RelExpr... Exprs> struct RelExprMaskBuilder {
- static inline uint64_t build() { return 0; }
-};
-
-// Specialization for recursive case.
-template <RelExpr Head, RelExpr... Tail>
-struct RelExprMaskBuilder<Head, Tail...> {
- static inline uint64_t build() {
- static_assert(0 <= Head && Head < 64,
- "RelExpr is too large for 64-bit mask!");
- return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
- }
+ // The following is abstract relocation types used for only one target.
+ //
+ // Even though RelExpr is intended to be a target-neutral representation
+ // of a relocation type, there are some relocations whose semantics are
+ // unique to a target. Such relocation are marked with R_<TARGET_NAME>.
+ R_AARCH64_GOT_PAGE_PC,
+ R_AARCH64_PAGE_PC,
+ R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
+ R_AARCH64_TLSDESC_PAGE,
+ R_ARM_SBREL,
+ R_HEXAGON_GOT,
+ R_MIPS_GOTREL,
+ R_MIPS_GOT_GP,
+ R_MIPS_GOT_GP_PC,
+ R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32,
+ R_MIPS_TLSGD,
+ R_MIPS_TLSLD,
+ R_PPC32_PLTREL,
+ R_PPC64_CALL,
+ R_PPC64_CALL_PLT,
+ R_PPC64_RELAX_TOC,
+ R_PPC64_TOCBASE,
+ R_RISCV_ADD,
+ R_RISCV_PC_INDIRECT,
};
-// Return true if `Expr` is one of `Exprs`.
-// There are fewer than 64 RelExpr's, so we can represent any set of
-// RelExpr's as a constant bit mask and test for membership with a
-// couple cheap bitwise operations.
-template <RelExpr... Exprs> bool isRelExprOneOf(RelExpr Expr) {
- assert(0 <= Expr && (int)Expr < 64 &&
- "RelExpr is too large for 64-bit mask!");
- return (uint64_t(1) << Expr) & RelExprMaskBuilder<Exprs...>::build();
-}
-
// Architecture-neutral representation of relocation.
struct Relocation {
- RelExpr Expr;
- RelType Type;
- uint64_t Offset;
- int64_t Addend;
- Symbol *Sym;
+ RelExpr expr;
+ RelType type;
+ uint64_t offset;
+ int64_t addend;
+ Symbol *sym;
};
-struct RelocationOffsetComparator {
- bool operator()(const Relocation &Lhs, const Relocation &Rhs) {
- return Lhs.Offset < Rhs.Offset;
- }
+// This function writes undefined symbol diagnostics to an internal buffer.
+// Call reportUndefinedSymbols() after calling scanRelocations() to emit
+// the diagnostics.
+template <class ELFT> void scanRelocations(InputSectionBase &);
- // For std::lower_bound, std::upper_bound, std::equal_range.
- bool operator()(const Relocation &Rel, uint64_t Val) {
- return Rel.Offset < Val;
- }
+template <class ELFT> void reportUndefinedSymbols();
- bool operator()(uint64_t Val, const Relocation &Rel) {
- return Val < Rel.Offset;
- }
-};
-
-template <class ELFT> void scanRelocations(InputSectionBase &);
+void addIRelativeRelocs();
class ThunkSection;
class Thunk;
@@ -162,56 +125,57 @@ struct InputSectionDescription;
class ThunkCreator {
public:
// Return true if Thunks have been added to OutputSections
- bool createThunks(ArrayRef<OutputSection *> OutputSections);
+ bool createThunks(ArrayRef<OutputSection *> outputSections);
// The number of completed passes of createThunks this permits us
// to do one time initialization on Pass 0 and put a limit on the
// number of times it can be called to prevent infinite loops.
- uint32_t Pass = 0;
+ uint32_t pass = 0;
private:
- void mergeThunks(ArrayRef<OutputSection *> OutputSections);
+ void mergeThunks(ArrayRef<OutputSection *> outputSections);
- ThunkSection *getISDThunkSec(OutputSection *OS, InputSection *IS,
- InputSectionDescription *ISD, uint32_t Type,
- uint64_t Src);
+ ThunkSection *getISDThunkSec(OutputSection *os, InputSection *isec,
+ InputSectionDescription *isd, uint32_t type,
+ uint64_t src);
- ThunkSection *getISThunkSec(InputSection *IS);
+ ThunkSection *getISThunkSec(InputSection *isec);
- void createInitialThunkSections(ArrayRef<OutputSection *> OutputSections);
+ void createInitialThunkSections(ArrayRef<OutputSection *> outputSections);
- std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
+ std::pair<Thunk *, bool> getThunk(InputSection *isec, Relocation &rel,
+ uint64_t src);
- ThunkSection *addThunkSection(OutputSection *OS, InputSectionDescription *,
- uint64_t Off);
+ ThunkSection *addThunkSection(OutputSection *os, InputSectionDescription *,
+ uint64_t off);
- bool normalizeExistingThunk(Relocation &Rel, uint64_t Src);
+ bool normalizeExistingThunk(Relocation &rel, uint64_t src);
// Record all the available Thunks for a Symbol
llvm::DenseMap<std::pair<SectionBase *, uint64_t>, std::vector<Thunk *>>
- ThunkedSymbolsBySection;
- llvm::DenseMap<Symbol *, std::vector<Thunk *>> ThunkedSymbols;
+ thunkedSymbolsBySection;
+ llvm::DenseMap<Symbol *, std::vector<Thunk *>> thunkedSymbols;
// Find a Thunk from the Thunks symbol definition, we can use this to find
// the Thunk from a relocation to the Thunks symbol definition.
- llvm::DenseMap<Symbol *, Thunk *> Thunks;
+ llvm::DenseMap<Symbol *, Thunk *> thunks;
// Track InputSections that have an inline ThunkSection placed in front
// an inline ThunkSection may have control fall through to the section below
// so we need to make sure that there is only one of them.
// The Mips LA25 Thunk is an example of an inline ThunkSection.
- llvm::DenseMap<InputSection *, ThunkSection *> ThunkedSections;
+ llvm::DenseMap<InputSection *, ThunkSection *> thunkedSections;
};
// Return a int64_t to make sure we get the sign extension out of the way as
// early as possible.
template <class ELFT>
-static inline int64_t getAddend(const typename ELFT::Rel &Rel) {
+static inline int64_t getAddend(const typename ELFT::Rel &rel) {
return 0;
}
template <class ELFT>
-static inline int64_t getAddend(const typename ELFT::Rela &Rel) {
- return Rel.r_addend;
+static inline int64_t getAddend(const typename ELFT::Rela &rel) {
+ return rel.r_addend;
}
} // namespace elf
} // namespace lld
diff --git a/ELF/ScriptLexer.cpp b/ELF/ScriptLexer.cpp
index 9a372c6d1c6f..953a3df8a31c 100644
--- a/ELF/ScriptLexer.cpp
+++ b/ELF/ScriptLexer.cpp
@@ -1,9 +1,8 @@
//===- ScriptLexer.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -42,166 +41,169 @@ using namespace lld::elf;
// Returns a whole line containing the current token.
StringRef ScriptLexer::getLine() {
- StringRef S = getCurrentMB().getBuffer();
- StringRef Tok = Tokens[Pos - 1];
+ StringRef s = getCurrentMB().getBuffer();
+ StringRef tok = tokens[pos - 1];
- size_t Pos = S.rfind('\n', Tok.data() - S.data());
- if (Pos != StringRef::npos)
- S = S.substr(Pos + 1);
- return S.substr(0, S.find_first_of("\r\n"));
+ size_t pos = s.rfind('\n', tok.data() - s.data());
+ if (pos != StringRef::npos)
+ s = s.substr(pos + 1);
+ return s.substr(0, s.find_first_of("\r\n"));
}
// Returns 1-based line number of the current token.
size_t ScriptLexer::getLineNumber() {
- StringRef S = getCurrentMB().getBuffer();
- StringRef Tok = Tokens[Pos - 1];
- return S.substr(0, Tok.data() - S.data()).count('\n') + 1;
+ StringRef s = getCurrentMB().getBuffer();
+ StringRef tok = tokens[pos - 1];
+ return s.substr(0, tok.data() - s.data()).count('\n') + 1;
}
// Returns 0-based column number of the current token.
size_t ScriptLexer::getColumnNumber() {
- StringRef Tok = Tokens[Pos - 1];
- return Tok.data() - getLine().data();
+ StringRef tok = tokens[pos - 1];
+ return tok.data() - getLine().data();
}
std::string ScriptLexer::getCurrentLocation() {
- std::string Filename = getCurrentMB().getBufferIdentifier();
- return (Filename + ":" + Twine(getLineNumber())).str();
+ std::string filename = getCurrentMB().getBufferIdentifier();
+ return (filename + ":" + Twine(getLineNumber())).str();
}
-ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); }
+ScriptLexer::ScriptLexer(MemoryBufferRef mb) { tokenize(mb); }
// We don't want to record cascading errors. Keep only the first one.
-void ScriptLexer::setError(const Twine &Msg) {
+void ScriptLexer::setError(const Twine &msg) {
if (errorCount())
return;
- std::string S = (getCurrentLocation() + ": " + Msg).str();
- if (Pos)
- S += "\n>>> " + getLine().str() + "\n>>> " +
+ std::string s = (getCurrentLocation() + ": " + msg).str();
+ if (pos)
+ s += "\n>>> " + getLine().str() + "\n>>> " +
std::string(getColumnNumber(), ' ') + "^";
- error(S);
+ error(s);
}
// Split S into linker script tokens.
-void ScriptLexer::tokenize(MemoryBufferRef MB) {
- std::vector<StringRef> Vec;
- MBs.push_back(MB);
- StringRef S = MB.getBuffer();
- StringRef Begin = S;
+void ScriptLexer::tokenize(MemoryBufferRef mb) {
+ std::vector<StringRef> vec;
+ mbs.push_back(mb);
+ StringRef s = mb.getBuffer();
+ StringRef begin = s;
for (;;) {
- S = skipSpace(S);
- if (S.empty())
+ s = skipSpace(s);
+ if (s.empty())
break;
// Quoted token. Note that double-quote characters are parts of a token
// because, in a glob match context, only unquoted tokens are interpreted
// as glob patterns. Double-quoted tokens are literal patterns in that
// context.
- if (S.startswith("\"")) {
- size_t E = S.find("\"", 1);
- if (E == StringRef::npos) {
- StringRef Filename = MB.getBufferIdentifier();
- size_t Lineno = Begin.substr(0, S.data() - Begin.data()).count('\n');
- error(Filename + ":" + Twine(Lineno + 1) + ": unclosed quote");
+ if (s.startswith("\"")) {
+ size_t e = s.find("\"", 1);
+ if (e == StringRef::npos) {
+ StringRef filename = mb.getBufferIdentifier();
+ size_t lineno = begin.substr(0, s.data() - begin.data()).count('\n');
+ error(filename + ":" + Twine(lineno + 1) + ": unclosed quote");
return;
}
- Vec.push_back(S.take_front(E + 1));
- S = S.substr(E + 1);
+ vec.push_back(s.take_front(e + 1));
+ s = s.substr(e + 1);
continue;
}
// ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
// "|", "||", "&" and "&&" are different operators.
- if (S.startswith("<<") || S.startswith("<=") || S.startswith(">>") ||
- S.startswith(">=") || S.startswith("||") || S.startswith("&&")) {
- Vec.push_back(S.substr(0, 2));
- S = S.substr(2);
+ if (s.startswith("<<") || s.startswith("<=") || s.startswith(">>") ||
+ s.startswith(">=") || s.startswith("||") || s.startswith("&&")) {
+ vec.push_back(s.substr(0, 2));
+ s = s.substr(2);
continue;
}
// Unquoted token. This is more relaxed than tokens in C-like language,
// so that you can write "file-name.cpp" as one bare token, for example.
- size_t Pos = S.find_first_not_of(
+ size_t pos = s.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789_.$/\\~=+[]*?-!^:");
// A character that cannot start a word (which is usually a
// punctuation) forms a single character token.
- if (Pos == 0)
- Pos = 1;
- Vec.push_back(S.substr(0, Pos));
- S = S.substr(Pos);
+ if (pos == 0)
+ pos = 1;
+ vec.push_back(s.substr(0, pos));
+ s = s.substr(pos);
}
- Tokens.insert(Tokens.begin() + Pos, Vec.begin(), Vec.end());
+ tokens.insert(tokens.begin() + pos, vec.begin(), vec.end());
}
// Skip leading whitespace characters or comments.
-StringRef ScriptLexer::skipSpace(StringRef S) {
+StringRef ScriptLexer::skipSpace(StringRef s) {
for (;;) {
- if (S.startswith("/*")) {
- size_t E = S.find("*/", 2);
- if (E == StringRef::npos) {
+ if (s.startswith("/*")) {
+ size_t e = s.find("*/", 2);
+ if (e == StringRef::npos) {
error("unclosed comment in a linker script");
return "";
}
- S = S.substr(E + 2);
+ s = s.substr(e + 2);
continue;
}
- if (S.startswith("#")) {
- size_t E = S.find('\n', 1);
- if (E == StringRef::npos)
- E = S.size() - 1;
- S = S.substr(E + 1);
+ if (s.startswith("#")) {
+ size_t e = s.find('\n', 1);
+ if (e == StringRef::npos)
+ e = s.size() - 1;
+ s = s.substr(e + 1);
continue;
}
- size_t Size = S.size();
- S = S.ltrim();
- if (S.size() == Size)
- return S;
+ size_t size = s.size();
+ s = s.ltrim();
+ if (s.size() == size)
+ return s;
}
}
// An erroneous token is handled as if it were the last token before EOF.
-bool ScriptLexer::atEOF() { return errorCount() || Tokens.size() == Pos; }
+bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; }
// Split a given string as an expression.
// This function returns "3", "*" and "5" for "3*5" for example.
-static std::vector<StringRef> tokenizeExpr(StringRef S) {
- StringRef Ops = "+-*/:!~"; // List of operators
+static std::vector<StringRef> tokenizeExpr(StringRef s) {
+ StringRef ops = "+-*/:!~=<>"; // List of operators
// Quoted strings are literal strings, so we don't want to split it.
- if (S.startswith("\""))
- return {S};
+ if (s.startswith("\""))
+ return {s};
// Split S with operators as separators.
- std::vector<StringRef> Ret;
- while (!S.empty()) {
- size_t E = S.find_first_of(Ops);
+ std::vector<StringRef> ret;
+ while (!s.empty()) {
+ size_t e = s.find_first_of(ops);
// No need to split if there is no operator.
- if (E == StringRef::npos) {
- Ret.push_back(S);
+ if (e == StringRef::npos) {
+ ret.push_back(s);
break;
}
// Get a token before the opreator.
- if (E != 0)
- Ret.push_back(S.substr(0, E));
-
- // Get the operator as a token. Keep != as one token.
- if (S.substr(E).startswith("!=")) {
- Ret.push_back(S.substr(E, 2));
- S = S.substr(E + 2);
+ if (e != 0)
+ ret.push_back(s.substr(0, e));
+
+ // Get the operator as a token.
+ // Keep !=, ==, >=, <=, << and >> operators as a single tokens.
+ if (s.substr(e).startswith("!=") || s.substr(e).startswith("==") ||
+ s.substr(e).startswith(">=") || s.substr(e).startswith("<=") ||
+ s.substr(e).startswith("<<") || s.substr(e).startswith(">>")) {
+ ret.push_back(s.substr(e, 2));
+ s = s.substr(e + 2);
} else {
- Ret.push_back(S.substr(E, 1));
- S = S.substr(E + 1);
+ ret.push_back(s.substr(e, 1));
+ s = s.substr(e + 1);
}
}
- return Ret;
+ return ret;
}
// In contexts where expressions are expected, the lexer should apply
@@ -214,14 +216,14 @@ static std::vector<StringRef> tokenizeExpr(StringRef S) {
//
// This function may split the current token into multiple tokens.
void ScriptLexer::maybeSplitExpr() {
- if (!InExpr || errorCount() || atEOF())
+ if (!inExpr || errorCount() || atEOF())
return;
- std::vector<StringRef> V = tokenizeExpr(Tokens[Pos]);
- if (V.size() == 1)
+ std::vector<StringRef> v = tokenizeExpr(tokens[pos]);
+ if (v.size() == 1)
return;
- Tokens.erase(Tokens.begin() + Pos);
- Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
+ tokens.erase(tokens.begin() + pos);
+ tokens.insert(tokens.begin() + pos, v.begin(), v.end());
}
StringRef ScriptLexer::next() {
@@ -233,28 +235,28 @@ StringRef ScriptLexer::next() {
setError("unexpected EOF");
return "";
}
- return Tokens[Pos++];
+ return tokens[pos++];
}
StringRef ScriptLexer::peek() {
- StringRef Tok = next();
+ StringRef tok = next();
if (errorCount())
return "";
- Pos = Pos - 1;
- return Tok;
+ pos = pos - 1;
+ return tok;
}
StringRef ScriptLexer::peek2() {
skip();
- StringRef Tok = next();
+ StringRef tok = next();
if (errorCount())
return "";
- Pos = Pos - 2;
- return Tok;
+ pos = pos - 2;
+ return tok;
}
-bool ScriptLexer::consume(StringRef Tok) {
- if (peek() == Tok) {
+bool ScriptLexer::consume(StringRef tok) {
+ if (peek() == tok) {
skip();
return true;
}
@@ -262,12 +264,12 @@ bool ScriptLexer::consume(StringRef Tok) {
}
// Consumes Tok followed by ":". Space is allowed between Tok and ":".
-bool ScriptLexer::consumeLabel(StringRef Tok) {
- if (consume((Tok + ":").str()))
+bool ScriptLexer::consumeLabel(StringRef tok) {
+ if (consume((tok + ":").str()))
return true;
- if (Tokens.size() >= Pos + 2 && Tokens[Pos] == Tok &&
- Tokens[Pos + 1] == ":") {
- Pos += 2;
+ if (tokens.size() >= pos + 2 && tokens[pos] == tok &&
+ tokens[pos + 1] == ":") {
+ pos += 2;
return true;
}
return false;
@@ -275,24 +277,24 @@ bool ScriptLexer::consumeLabel(StringRef Tok) {
void ScriptLexer::skip() { (void)next(); }
-void ScriptLexer::expect(StringRef Expect) {
+void ScriptLexer::expect(StringRef expect) {
if (errorCount())
return;
- StringRef Tok = next();
- if (Tok != Expect)
- setError(Expect + " expected, but got " + Tok);
+ StringRef tok = next();
+ if (tok != expect)
+ setError(expect + " expected, but got " + tok);
}
// Returns true if S encloses T.
-static bool encloses(StringRef S, StringRef T) {
- return S.bytes_begin() <= T.bytes_begin() && T.bytes_end() <= S.bytes_end();
+static bool encloses(StringRef s, StringRef t) {
+ return s.bytes_begin() <= t.bytes_begin() && t.bytes_end() <= s.bytes_end();
}
MemoryBufferRef ScriptLexer::getCurrentMB() {
// Find input buffer containing the current token.
- assert(!MBs.empty() && Pos > 0);
- for (MemoryBufferRef MB : MBs)
- if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
- return MB;
+ assert(!mbs.empty() && pos > 0);
+ for (MemoryBufferRef mb : mbs)
+ if (encloses(mb.getBuffer(), tokens[pos - 1]))
+ return mb;
llvm_unreachable("getCurrentMB: failed to find a token");
}
diff --git a/ELF/ScriptLexer.h b/ELF/ScriptLexer.h
index fc6b5b1008a7..98e4cac95a73 100644
--- a/ELF/ScriptLexer.h
+++ b/ELF/ScriptLexer.h
@@ -1,9 +1,8 @@
//===- ScriptLexer.h --------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -21,25 +20,25 @@ namespace elf {
class ScriptLexer {
public:
- explicit ScriptLexer(MemoryBufferRef MB);
+ explicit ScriptLexer(MemoryBufferRef mb);
- void setError(const Twine &Msg);
- void tokenize(MemoryBufferRef MB);
- static StringRef skipSpace(StringRef S);
+ void setError(const Twine &msg);
+ void tokenize(MemoryBufferRef mb);
+ static StringRef skipSpace(StringRef s);
bool atEOF();
StringRef next();
StringRef peek();
StringRef peek2();
void skip();
- bool consume(StringRef Tok);
- void expect(StringRef Expect);
- bool consumeLabel(StringRef Tok);
+ bool consume(StringRef tok);
+ void expect(StringRef expect);
+ bool consumeLabel(StringRef tok);
std::string getCurrentLocation();
- std::vector<MemoryBufferRef> MBs;
- std::vector<StringRef> Tokens;
- bool InExpr = false;
- size_t Pos = 0;
+ std::vector<MemoryBufferRef> mbs;
+ std::vector<StringRef> tokens;
+ bool inExpr = false;
+ size_t pos = 0;
private:
void maybeSplitExpr();
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index eee3f0e330cc..8f0aa660145a 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -1,9 +1,8 @@
//===- ScriptParser.cpp ---------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -41,22 +40,29 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-static bool isUnderSysroot(StringRef Path);
-
namespace {
class ScriptParser final : ScriptLexer {
public:
- ScriptParser(MemoryBufferRef MB)
- : ScriptLexer(MB),
- IsUnderSysroot(isUnderSysroot(MB.getBufferIdentifier())) {}
+ ScriptParser(MemoryBufferRef mb) : ScriptLexer(mb) {
+ // Initialize IsUnderSysroot
+ if (config->sysroot == "")
+ return;
+ StringRef path = mb.getBufferIdentifier();
+ for (; !path.empty(); path = sys::path::parent_path(path)) {
+ if (!sys::fs::equivalent(config->sysroot, path))
+ continue;
+ isUnderSysroot = true;
+ return;
+ }
+ }
void readLinkerScript();
void readVersionScript();
void readDynamicList();
- void readDefsym(StringRef Name);
+ void readDefsym(StringRef name);
private:
- void addFile(StringRef Path);
+ void addFile(StringRef path);
void readAsNeeded();
void readEntry();
@@ -76,25 +82,23 @@ private:
void readVersion();
void readVersionScriptCommand();
- SymbolAssignment *readSymbolAssignment(StringRef Name);
- ByteCommand *readByteCommand(StringRef Tok);
+ SymbolAssignment *readSymbolAssignment(StringRef name);
+ ByteCommand *readByteCommand(StringRef tok);
std::array<uint8_t, 4> readFill();
- std::array<uint8_t, 4> parseFill(StringRef Tok);
- bool readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2);
- void readSectionAddressType(OutputSection *Cmd);
+ bool readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2);
+ void readSectionAddressType(OutputSection *cmd);
OutputSection *readOverlaySectionDescription();
- OutputSection *readOutputSectionDescription(StringRef OutSec);
+ OutputSection *readOutputSectionDescription(StringRef outSec);
std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
- InputSectionDescription *readInputSectionDescription(StringRef Tok);
+ InputSectionDescription *readInputSectionDescription(StringRef tok);
StringMatcher readFilePatterns();
std::vector<SectionPattern> readInputSectionsList();
- InputSectionDescription *readInputSectionRules(StringRef FilePattern);
+ InputSectionDescription *readInputSectionRules(StringRef filePattern);
unsigned readPhdrType();
SortSectionPolicy readSortKind();
- SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
- SymbolAssignment *readAssignment(StringRef Tok);
- std::tuple<ELFKind, uint16_t, bool> readBfdName();
+ SymbolAssignment *readProvideHidden(bool provide, bool hidden);
+ SymbolAssignment *readAssignment(StringRef tok);
void readSort();
Expr readAssert();
Expr readConstant();
@@ -103,97 +107,88 @@ private:
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
- Expr combine(StringRef Op, Expr L, Expr R);
+ Expr combine(StringRef op, Expr l, Expr r);
Expr readExpr();
- Expr readExpr1(Expr Lhs, int MinPrec);
+ Expr readExpr1(Expr lhs, int minPrec);
StringRef readParenLiteral();
Expr readPrimary();
- Expr readTernary(Expr Cond);
+ Expr readTernary(Expr cond);
Expr readParenExpr();
// For parsing version script.
std::vector<SymbolVersion> readVersionExtern();
void readAnonymousDeclaration();
- void readVersionDeclaration(StringRef VerStr);
+ void readVersionDeclaration(StringRef verStr);
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
readSymbols();
// True if a script being read is in a subdirectory specified by -sysroot.
- bool IsUnderSysroot;
+ bool isUnderSysroot = false;
// A set to detect an INCLUDE() cycle.
- StringSet<> Seen;
+ StringSet<> seen;
};
} // namespace
-static StringRef unquote(StringRef S) {
- if (S.startswith("\""))
- return S.substr(1, S.size() - 2);
- return S;
-}
-
-static bool isUnderSysroot(StringRef Path) {
- if (Config->Sysroot == "")
- return false;
- for (; !Path.empty(); Path = sys::path::parent_path(Path))
- if (sys::fs::equivalent(Config->Sysroot, Path))
- return true;
- return false;
+static StringRef unquote(StringRef s) {
+ if (s.startswith("\""))
+ return s.substr(1, s.size() - 2);
+ return s;
}
// Some operations only support one non absolute value. Move the
// absolute one to the right hand side for convenience.
-static void moveAbsRight(ExprValue &A, ExprValue &B) {
- if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute()))
- std::swap(A, B);
- if (!B.isAbsolute())
- error(A.Loc + ": at least one side of the expression must be absolute");
+static void moveAbsRight(ExprValue &a, ExprValue &b) {
+ if (a.sec == nullptr || (a.forceAbsolute && !b.isAbsolute()))
+ std::swap(a, b);
+ if (!b.isAbsolute())
+ error(a.loc + ": at least one side of the expression must be absolute");
}
-static ExprValue add(ExprValue A, ExprValue B) {
- moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc};
+static ExprValue add(ExprValue a, ExprValue b) {
+ moveAbsRight(a, b);
+ return {a.sec, a.forceAbsolute, a.getSectionOffset() + b.getValue(), a.loc};
}
-static ExprValue sub(ExprValue A, ExprValue B) {
+static ExprValue sub(ExprValue a, ExprValue b) {
// The distance between two symbols in sections is absolute.
- if (!A.isAbsolute() && !B.isAbsolute())
- return A.getValue() - B.getValue();
- return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
+ if (!a.isAbsolute() && !b.isAbsolute())
+ return a.getValue() - b.getValue();
+ return {a.sec, false, a.getSectionOffset() - b.getValue(), a.loc};
}
-static ExprValue bitAnd(ExprValue A, ExprValue B) {
- moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute,
- (A.getValue() & B.getValue()) - A.getSecAddr(), A.Loc};
+static ExprValue bitAnd(ExprValue a, ExprValue b) {
+ moveAbsRight(a, b);
+ return {a.sec, a.forceAbsolute,
+ (a.getValue() & b.getValue()) - a.getSecAddr(), a.loc};
}
-static ExprValue bitOr(ExprValue A, ExprValue B) {
- moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute,
- (A.getValue() | B.getValue()) - A.getSecAddr(), A.Loc};
+static ExprValue bitOr(ExprValue a, ExprValue b) {
+ moveAbsRight(a, b);
+ return {a.sec, a.forceAbsolute,
+ (a.getValue() | b.getValue()) - a.getSecAddr(), a.loc};
}
void ScriptParser::readDynamicList() {
- Config->HasDynamicList = true;
+ config->hasDynamicList = true;
expect("{");
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::tie(Locals, Globals) = readSymbols();
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::tie(locals, globals) = readSymbols();
expect(";");
if (!atEOF()) {
setError("EOF expected, but got " + next());
return;
}
- if (!Locals.empty()) {
+ if (!locals.empty()) {
setError("\"local:\" scope not supported in --dynamic-list");
return;
}
- for (SymbolVersion V : Globals)
- Config->DynamicList.push_back(V);
+ for (SymbolVersion v : globals)
+ config->dynamicList.push_back(v);
}
void ScriptParser::readVersionScript() {
@@ -209,14 +204,14 @@ void ScriptParser::readVersionScriptCommand() {
}
while (!atEOF() && !errorCount() && peek() != "}") {
- StringRef VerStr = next();
- if (VerStr == "{") {
+ StringRef verStr = next();
+ if (verStr == "{") {
setError("anonymous version definition is used in "
"combination with other version definitions");
return;
}
expect("{");
- readVersionDeclaration(VerStr);
+ readVersionDeclaration(verStr);
}
}
@@ -228,135 +223,135 @@ void ScriptParser::readVersion() {
void ScriptParser::readLinkerScript() {
while (!atEOF()) {
- StringRef Tok = next();
- if (Tok == ";")
+ StringRef tok = next();
+ if (tok == ";")
continue;
- if (Tok == "ENTRY") {
+ if (tok == "ENTRY") {
readEntry();
- } else if (Tok == "EXTERN") {
+ } else if (tok == "EXTERN") {
readExtern();
- } else if (Tok == "GROUP") {
+ } else if (tok == "GROUP") {
readGroup();
- } else if (Tok == "INCLUDE") {
+ } else if (tok == "INCLUDE") {
readInclude();
- } else if (Tok == "INPUT") {
+ } else if (tok == "INPUT") {
readInput();
- } else if (Tok == "MEMORY") {
+ } else if (tok == "MEMORY") {
readMemory();
- } else if (Tok == "OUTPUT") {
+ } else if (tok == "OUTPUT") {
readOutput();
- } else if (Tok == "OUTPUT_ARCH") {
+ } else if (tok == "OUTPUT_ARCH") {
readOutputArch();
- } else if (Tok == "OUTPUT_FORMAT") {
+ } else if (tok == "OUTPUT_FORMAT") {
readOutputFormat();
- } else if (Tok == "PHDRS") {
+ } else if (tok == "PHDRS") {
readPhdrs();
- } else if (Tok == "REGION_ALIAS") {
+ } else if (tok == "REGION_ALIAS") {
readRegionAlias();
- } else if (Tok == "SEARCH_DIR") {
+ } else if (tok == "SEARCH_DIR") {
readSearchDir();
- } else if (Tok == "SECTIONS") {
+ } else if (tok == "SECTIONS") {
readSections();
- } else if (Tok == "TARGET") {
+ } else if (tok == "TARGET") {
readTarget();
- } else if (Tok == "VERSION") {
+ } else if (tok == "VERSION") {
readVersion();
- } else if (SymbolAssignment *Cmd = readAssignment(Tok)) {
- Script->SectionCommands.push_back(Cmd);
+ } else if (SymbolAssignment *cmd = readAssignment(tok)) {
+ script->sectionCommands.push_back(cmd);
} else {
- setError("unknown directive: " + Tok);
+ setError("unknown directive: " + tok);
}
}
}
-void ScriptParser::readDefsym(StringRef Name) {
+void ScriptParser::readDefsym(StringRef name) {
if (errorCount())
return;
- Expr E = readExpr();
+ Expr e = readExpr();
if (!atEOF())
setError("EOF expected, but got " + next());
- SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation());
- Script->SectionCommands.push_back(Cmd);
+ SymbolAssignment *cmd = make<SymbolAssignment>(name, e, getCurrentLocation());
+ script->sectionCommands.push_back(cmd);
}
-void ScriptParser::addFile(StringRef S) {
- if (IsUnderSysroot && S.startswith("/")) {
- SmallString<128> PathData;
- StringRef Path = (Config->Sysroot + S).toStringRef(PathData);
- if (sys::fs::exists(Path)) {
- Driver->addFile(Saver.save(Path), /*WithLOption=*/false);
+void ScriptParser::addFile(StringRef s) {
+ if (isUnderSysroot && s.startswith("/")) {
+ SmallString<128> pathData;
+ StringRef path = (config->sysroot + s).toStringRef(pathData);
+ if (sys::fs::exists(path)) {
+ driver->addFile(saver.save(path), /*withLOption=*/false);
return;
}
}
- if (S.startswith("/")) {
- Driver->addFile(S, /*WithLOption=*/false);
- } else if (S.startswith("=")) {
- if (Config->Sysroot.empty())
- Driver->addFile(S.substr(1), /*WithLOption=*/false);
+ if (s.startswith("/")) {
+ driver->addFile(s, /*withLOption=*/false);
+ } else if (s.startswith("=")) {
+ 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")) {
- Driver->addLibrary(S.substr(2));
- } else if (sys::fs::exists(S)) {
- Driver->addFile(S, /*WithLOption=*/false);
+ driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)),
+ /*withLOption=*/false);
+ } else if (s.startswith("-l")) {
+ 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);
+ if (Optional<std::string> path = findFromSearchPaths(s))
+ driver->addFile(saver.save(*path), /*withLOption=*/true);
else
- setError("unable to find " + S);
+ setError("unable to find " + s);
}
}
void ScriptParser::readAsNeeded() {
expect("(");
- bool Orig = Config->AsNeeded;
- Config->AsNeeded = true;
+ bool orig = config->asNeeded;
+ config->asNeeded = true;
while (!errorCount() && !consume(")"))
addFile(unquote(next()));
- Config->AsNeeded = Orig;
+ config->asNeeded = orig;
}
void ScriptParser::readEntry() {
// -e <symbol> takes predecence over ENTRY(<symbol>).
expect("(");
- StringRef Tok = next();
- if (Config->Entry.empty())
- Config->Entry = Tok;
+ StringRef tok = next();
+ if (config->entry.empty())
+ config->entry = tok;
expect(")");
}
void ScriptParser::readExtern() {
expect("(");
while (!errorCount() && !consume(")"))
- Config->Undefined.push_back(next());
+ config->undefined.push_back(unquote(next()));
}
void ScriptParser::readGroup() {
- bool Orig = InputFile::IsInGroup;
- InputFile::IsInGroup = true;
+ bool orig = InputFile::isInGroup;
+ InputFile::isInGroup = true;
readInput();
- InputFile::IsInGroup = Orig;
- if (!Orig)
- ++InputFile::NextGroupId;
+ InputFile::isInGroup = orig;
+ if (!orig)
+ ++InputFile::nextGroupId;
}
void ScriptParser::readInclude() {
- StringRef Tok = unquote(next());
+ StringRef tok = unquote(next());
- if (!Seen.insert(Tok).second) {
+ if (!seen.insert(tok).second) {
setError("there is a cycle in linker script INCLUDEs");
return;
}
- if (Optional<std::string> Path = searchScript(Tok)) {
- if (Optional<MemoryBufferRef> MB = readFile(*Path))
- tokenize(*MB);
+ if (Optional<std::string> path = searchScript(tok)) {
+ if (Optional<MemoryBufferRef> mb = readFile(*path))
+ tokenize(*mb);
return;
}
- setError("cannot find linker script " + Tok);
+ setError("cannot find linker script " + tok);
}
void ScriptParser::readInput() {
@@ -372,9 +367,9 @@ void ScriptParser::readInput() {
void ScriptParser::readOutput() {
// -o <file> takes predecence over OUTPUT(<file>).
expect("(");
- StringRef Tok = next();
- if (Config->OutputFile.empty())
- Config->OutputFile = unquote(Tok);
+ StringRef tok = next();
+ if (config->outputFile.empty())
+ config->outputFile = unquote(tok);
expect(")");
}
@@ -385,39 +380,27 @@ void ScriptParser::readOutputArch() {
skip();
}
-std::tuple<ELFKind, uint16_t, bool> ScriptParser::readBfdName() {
- StringRef S = unquote(next());
- if (S == "elf32-i386")
- return std::make_tuple(ELF32LEKind, EM_386, false);
- if (S == "elf32-iamcu")
- return std::make_tuple(ELF32LEKind, EM_IAMCU, false);
- if (S == "elf32-littlearm")
- return std::make_tuple(ELF32LEKind, EM_ARM, false);
- if (S == "elf32-x86-64")
- return std::make_tuple(ELF32LEKind, EM_X86_64, false);
- if (S == "elf64-littleaarch64")
- return std::make_tuple(ELF64LEKind, EM_AARCH64, false);
- if (S == "elf64-powerpc")
- return std::make_tuple(ELF64BEKind, EM_PPC64, false);
- if (S == "elf64-powerpcle")
- return std::make_tuple(ELF64LEKind, EM_PPC64, false);
- if (S == "elf64-x86-64")
- return std::make_tuple(ELF64LEKind, EM_X86_64, false);
- if (S == "elf32-tradbigmips")
- return std::make_tuple(ELF32BEKind, EM_MIPS, false);
- if (S == "elf32-ntradbigmips")
- return std::make_tuple(ELF32BEKind, EM_MIPS, true);
- if (S == "elf32-tradlittlemips")
- return std::make_tuple(ELF32LEKind, EM_MIPS, false);
- if (S == "elf32-ntradlittlemips")
- return std::make_tuple(ELF32LEKind, EM_MIPS, true);
- if (S == "elf64-tradbigmips")
- return std::make_tuple(ELF64BEKind, EM_MIPS, false);
- if (S == "elf64-tradlittlemips")
- return std::make_tuple(ELF64LEKind, EM_MIPS, false);
-
- setError("unknown output format name: " + S);
- return std::make_tuple(ELFNoneKind, EM_NONE, false);
+static std::pair<ELFKind, uint16_t> parseBfdName(StringRef s) {
+ return StringSwitch<std::pair<ELFKind, uint16_t>>(s)
+ .Case("elf32-i386", {ELF32LEKind, EM_386})
+ .Case("elf32-iamcu", {ELF32LEKind, EM_IAMCU})
+ .Case("elf32-littlearm", {ELF32LEKind, EM_ARM})
+ .Case("elf32-x86-64", {ELF32LEKind, EM_X86_64})
+ .Case("elf64-aarch64", {ELF64LEKind, EM_AARCH64})
+ .Case("elf64-littleaarch64", {ELF64LEKind, EM_AARCH64})
+ .Case("elf32-powerpc", {ELF32BEKind, EM_PPC})
+ .Case("elf64-powerpc", {ELF64BEKind, EM_PPC64})
+ .Case("elf64-powerpcle", {ELF64LEKind, EM_PPC64})
+ .Case("elf64-x86-64", {ELF64LEKind, EM_X86_64})
+ .Cases("elf32-tradbigmips", "elf32-bigmips", {ELF32BEKind, EM_MIPS})
+ .Case("elf32-ntradbigmips", {ELF32BEKind, EM_MIPS})
+ .Case("elf32-tradlittlemips", {ELF32LEKind, EM_MIPS})
+ .Case("elf32-ntradlittlemips", {ELF32LEKind, EM_MIPS})
+ .Case("elf64-tradbigmips", {ELF64BEKind, EM_MIPS})
+ .Case("elf64-tradlittlemips", {ELF64LEKind, EM_MIPS})
+ .Case("elf32-littleriscv", {ELF32LEKind, EM_RISCV})
+ .Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV})
+ .Default({ELFNoneKind, EM_NONE});
}
// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little).
@@ -425,9 +408,16 @@ std::tuple<ELFKind, uint16_t, bool> ScriptParser::readBfdName() {
void ScriptParser::readOutputFormat() {
expect("(");
- std::tuple<ELFKind, uint16_t, bool> BfdTuple = readBfdName();
- if (Config->EKind == ELFNoneKind)
- std::tie(Config->EKind, Config->EMachine, Config->MipsN32Abi) = BfdTuple;
+ StringRef name = unquote(next());
+ StringRef s = name;
+ 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);
+ if (s == "elf32-ntradlittlemips" || s == "elf32-ntradbigmips")
+ config->mipsN32Abi = true;
if (consume(")"))
return;
@@ -442,46 +432,46 @@ void ScriptParser::readPhdrs() {
expect("{");
while (!errorCount() && !consume("}")) {
- PhdrsCommand Cmd;
- Cmd.Name = next();
- Cmd.Type = readPhdrType();
+ PhdrsCommand cmd;
+ cmd.name = next();
+ cmd.type = readPhdrType();
while (!errorCount() && !consume(";")) {
if (consume("FILEHDR"))
- Cmd.HasFilehdr = true;
+ cmd.hasFilehdr = true;
else if (consume("PHDRS"))
- Cmd.HasPhdrs = true;
+ cmd.hasPhdrs = true;
else if (consume("AT"))
- Cmd.LMAExpr = readParenExpr();
+ cmd.lmaExpr = readParenExpr();
else if (consume("FLAGS"))
- Cmd.Flags = readParenExpr()().getValue();
+ cmd.flags = readParenExpr()().getValue();
else
setError("unexpected header attribute: " + next());
}
- Script->PhdrsCommands.push_back(Cmd);
+ script->phdrsCommands.push_back(cmd);
}
}
void ScriptParser::readRegionAlias() {
expect("(");
- StringRef Alias = unquote(next());
+ StringRef alias = unquote(next());
expect(",");
- StringRef Name = next();
+ StringRef name = next();
expect(")");
- if (Script->MemoryRegions.count(Alias))
- setError("redefinition of memory region '" + Alias + "'");
- if (!Script->MemoryRegions.count(Name))
- setError("memory region '" + Name + "' is not defined");
- Script->MemoryRegions.insert({Alias, Script->MemoryRegions[Name]});
+ if (script->memoryRegions.count(alias))
+ setError("redefinition of memory region '" + alias + "'");
+ if (!script->memoryRegions.count(name))
+ setError("memory region '" + name + "' is not defined");
+ script->memoryRegions.insert({alias, script->memoryRegions[name]});
}
void ScriptParser::readSearchDir() {
expect("(");
- StringRef Tok = next();
- if (!Config->Nostdlib)
- Config->SearchPaths.push_back(unquote(Tok));
+ StringRef tok = next();
+ if (!config->nostdlib)
+ config->searchPaths.push_back(unquote(tok));
expect(")");
}
@@ -493,83 +483,83 @@ std::vector<BaseCommand *> ScriptParser::readOverlay() {
// VA and LMA expressions are optional, though for simplicity of
// implementation we assume they are not. That is what OVERLAY was designed
// for first of all: to allow sections with overlapping VAs at different LMAs.
- Expr AddrExpr = readExpr();
+ Expr addrExpr = readExpr();
expect(":");
expect("AT");
- Expr LMAExpr = readParenExpr();
+ Expr lmaExpr = readParenExpr();
expect("{");
- std::vector<BaseCommand *> V;
- OutputSection *Prev = nullptr;
+ std::vector<BaseCommand *> v;
+ OutputSection *prev = nullptr;
while (!errorCount() && !consume("}")) {
// VA is the same for all sections. The LMAs are consecutive in memory
// starting from the base load address specified.
- OutputSection *OS = readOverlaySectionDescription();
- OS->AddrExpr = AddrExpr;
- if (Prev)
- OS->LMAExpr = [=] { return Prev->getLMA() + Prev->Size; };
+ OutputSection *os = readOverlaySectionDescription();
+ os->addrExpr = addrExpr;
+ if (prev)
+ os->lmaExpr = [=] { return prev->getLMA() + prev->size; };
else
- OS->LMAExpr = LMAExpr;
- V.push_back(OS);
- Prev = OS;
+ os->lmaExpr = lmaExpr;
+ v.push_back(os);
+ prev = os;
}
// According to the specification, at the end of the overlay, the location
// counter should be equal to the overlay base address plus size of the
// largest section seen in the overlay.
// Here we want to create the Dot assignment command to achieve that.
- Expr MoveDot = [=] {
- uint64_t Max = 0;
- for (BaseCommand *Cmd : V)
- Max = std::max(Max, cast<OutputSection>(Cmd)->Size);
- return AddrExpr().getValue() + Max;
+ Expr moveDot = [=] {
+ uint64_t max = 0;
+ for (BaseCommand *cmd : v)
+ max = std::max(max, cast<OutputSection>(cmd)->size);
+ return addrExpr().getValue() + max;
};
- V.push_back(make<SymbolAssignment>(".", MoveDot, getCurrentLocation()));
- return V;
+ v.push_back(make<SymbolAssignment>(".", moveDot, getCurrentLocation()));
+ return v;
}
void ScriptParser::readSections() {
- Script->HasSectionsCommand = true;
+ 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;
+ config->singleRoRx = true;
expect("{");
- std::vector<BaseCommand *> V;
+ std::vector<BaseCommand *> v;
while (!errorCount() && !consume("}")) {
- StringRef Tok = next();
- if (Tok == "OVERLAY") {
- for (BaseCommand *Cmd : readOverlay())
- V.push_back(Cmd);
+ StringRef tok = next();
+ if (tok == "OVERLAY") {
+ for (BaseCommand *cmd : readOverlay())
+ v.push_back(cmd);
continue;
- } else if (Tok == "INCLUDE") {
+ } else if (tok == "INCLUDE") {
readInclude();
continue;
}
- if (BaseCommand *Cmd = readAssignment(Tok))
- V.push_back(Cmd);
+ if (BaseCommand *cmd = readAssignment(tok))
+ v.push_back(cmd);
else
- V.push_back(readOutputSectionDescription(Tok));
+ v.push_back(readOutputSectionDescription(tok));
}
if (!atEOF() && consume("INSERT")) {
- std::vector<BaseCommand *> *Dest = nullptr;
+ std::vector<BaseCommand *> *dest = nullptr;
if (consume("AFTER"))
- Dest = &Script->InsertAfterCommands[next()];
+ dest = &script->insertAfterCommands[next()];
else if (consume("BEFORE"))
- Dest = &Script->InsertBeforeCommands[next()];
+ dest = &script->insertBeforeCommands[next()];
else
setError("expected AFTER/BEFORE, but got '" + next() + "'");
- if (Dest)
- Dest->insert(Dest->end(), V.begin(), V.end());
+ if (dest)
+ dest->insert(dest->end(), v.begin(), v.end());
return;
}
- Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(),
- V.end());
+ script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
+ v.end());
}
void ScriptParser::readTarget() {
@@ -578,19 +568,19 @@ void ScriptParser::readTarget() {
// for --format. We recognize only /^elf/ and "binary" in the linker
// script as well.
expect("(");
- StringRef Tok = next();
+ StringRef tok = next();
expect(")");
- if (Tok.startswith("elf"))
- Config->FormatBinary = false;
- else if (Tok == "binary")
- Config->FormatBinary = true;
+ if (tok.startswith("elf"))
+ config->formatBinary = false;
+ else if (tok == "binary")
+ config->formatBinary = true;
else
- setError("unknown target: " + Tok);
+ setError("unknown target: " + tok);
}
-static int precedence(StringRef Op) {
- return StringSwitch<int>(Op)
+static int precedence(StringRef op) {
+ return StringSwitch<int>(op)
.Cases("*", "/", "%", 8)
.Cases("+", "-", 7)
.Cases("<<", ">>", 6)
@@ -603,10 +593,10 @@ static int precedence(StringRef Op) {
}
StringMatcher ScriptParser::readFilePatterns() {
- std::vector<StringRef> V;
+ std::vector<StringRef> v;
while (!errorCount() && !consume(")"))
- V.push_back(next());
- return StringMatcher(V);
+ v.push_back(next());
+ return StringMatcher(v);
}
SortSectionPolicy ScriptParser::readSortKind() {
@@ -635,24 +625,24 @@ SortSectionPolicy ScriptParser::readSortKind() {
// The semantics of that is section .foo in any file, section .bar in
// any file but a.o, and section .baz in any file but b.o.
std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
- std::vector<SectionPattern> Ret;
+ std::vector<SectionPattern> ret;
while (!errorCount() && peek() != ")") {
- StringMatcher ExcludeFilePat;
+ StringMatcher excludeFilePat;
if (consume("EXCLUDE_FILE")) {
expect("(");
- ExcludeFilePat = readFilePatterns();
+ excludeFilePat = readFilePatterns();
}
- std::vector<StringRef> V;
+ std::vector<StringRef> v;
while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE")
- V.push_back(next());
+ v.push_back(unquote(next()));
- if (!V.empty())
- Ret.push_back({std::move(ExcludeFilePat), StringMatcher(V)});
+ if (!v.empty())
+ ret.push_back({std::move(excludeFilePat), StringMatcher(v)});
else
setError("section pattern is expected");
}
- return Ret;
+ return ret;
}
// Reads contents of "SECTIONS" directive. That directive contains a
@@ -667,52 +657,52 @@ 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) {
+ auto *cmd = make<InputSectionDescription>(filePattern);
expect("(");
while (!errorCount() && !consume(")")) {
- SortSectionPolicy Outer = readSortKind();
- SortSectionPolicy Inner = SortSectionPolicy::Default;
- std::vector<SectionPattern> V;
- if (Outer != SortSectionPolicy::Default) {
+ SortSectionPolicy outer = readSortKind();
+ SortSectionPolicy inner = SortSectionPolicy::Default;
+ std::vector<SectionPattern> v;
+ if (outer != SortSectionPolicy::Default) {
expect("(");
- Inner = readSortKind();
- if (Inner != SortSectionPolicy::Default) {
+ inner = readSortKind();
+ if (inner != SortSectionPolicy::Default) {
expect("(");
- V = readInputSectionsList();
+ v = readInputSectionsList();
expect(")");
} else {
- V = readInputSectionsList();
+ v = readInputSectionsList();
}
expect(")");
} else {
- V = readInputSectionsList();
+ v = readInputSectionsList();
}
- for (SectionPattern &Pat : V) {
- Pat.SortInner = Inner;
- Pat.SortOuter = Outer;
+ for (SectionPattern &pat : v) {
+ pat.sortInner = inner;
+ pat.sortOuter = outer;
}
- std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns));
+ std::move(v.begin(), v.end(), std::back_inserter(cmd->sectionPatterns));
}
- return Cmd;
+ return cmd;
}
InputSectionDescription *
-ScriptParser::readInputSectionDescription(StringRef Tok) {
+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
- if (Tok == "KEEP") {
+ if (tok == "KEEP") {
expect("(");
- StringRef FilePattern = next();
- InputSectionDescription *Cmd = readInputSectionRules(FilePattern);
+ StringRef filePattern = next();
+ InputSectionDescription *cmd = readInputSectionRules(filePattern);
expect(")");
- Script->KeptSections.push_back(Cmd);
- return Cmd;
+ script->keptSections.push_back(cmd);
+ return cmd;
}
- return readInputSectionRules(Tok);
+ return readInputSectionRules(tok);
}
void ScriptParser::readSort() {
@@ -723,44 +713,33 @@ void ScriptParser::readSort() {
Expr ScriptParser::readAssert() {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(",");
- StringRef Msg = unquote(next());
+ StringRef msg = unquote(next());
expect(")");
return [=] {
- if (!E().getValue())
- error(Msg);
- return Script->getDot();
+ if (!e().getValue())
+ error(msg);
+ return script->getDot();
};
}
-// Reads a FILL(expr) command. 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
-std::array<uint8_t, 4> ScriptParser::readFill() {
- expect("(");
- std::array<uint8_t, 4> V = parseFill(next());
- expect(")");
- return V;
-}
-
// Tries to read the special directive for an output section definition which
// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)".
// Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below.
-bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2) {
- if (Tok1 != "(")
+bool ScriptParser::readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2) {
+ if (tok1 != "(")
return false;
- if (Tok2 != "NOLOAD" && Tok2 != "COPY" && Tok2 != "INFO" && Tok2 != "OVERLAY")
+ if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" && tok2 != "OVERLAY")
return false;
expect("(");
if (consume("NOLOAD")) {
- Cmd->Noload = true;
+ cmd->noload = true;
} else {
skip(); // This is "COPY", "INFO" or "OVERLAY".
- Cmd->NonAlloc = true;
+ cmd->nonAlloc = true;
}
expect(")");
return true;
@@ -777,178 +756,186 @@ bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, Stri
//
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
-void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
- if (readSectionDirective(Cmd, peek(), peek2()))
+void ScriptParser::readSectionAddressType(OutputSection *cmd) {
+ if (readSectionDirective(cmd, peek(), peek2()))
return;
- Cmd->AddrExpr = readExpr();
- if (peek() == "(" && !readSectionDirective(Cmd, "(", peek2()))
+ cmd->addrExpr = readExpr();
+ if (peek() == "(" && !readSectionDirective(cmd, "(", peek2()))
setError("unknown section directive: " + peek2());
}
-static Expr checkAlignment(Expr E, std::string &Loc) {
+static Expr checkAlignment(Expr e, std::string &loc) {
return [=] {
- uint64_t Alignment = std::max((uint64_t)1, E().getValue());
- if (!isPowerOf2_64(Alignment)) {
- error(Loc + ": alignment must be power of 2");
+ uint64_t alignment = std::max((uint64_t)1, e().getValue());
+ if (!isPowerOf2_64(alignment)) {
+ error(loc + ": alignment must be power of 2");
return (uint64_t)1; // Return a dummy value.
}
- return Alignment;
+ return alignment;
};
}
OutputSection *ScriptParser::readOverlaySectionDescription() {
- OutputSection *Cmd =
- Script->createOutputSection(next(), getCurrentLocation());
- Cmd->InOverlay = true;
+ OutputSection *cmd =
+ script->createOutputSection(next(), getCurrentLocation());
+ cmd->inOverlay = true;
expect("{");
while (!errorCount() && !consume("}"))
- Cmd->SectionCommands.push_back(readInputSectionRules(next()));
- Cmd->Phdrs = readOutputSectionPhdrs();
- return Cmd;
+ cmd->sectionCommands.push_back(readInputSectionRules(next()));
+ cmd->phdrs = readOutputSectionPhdrs();
+ return cmd;
}
-OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
- OutputSection *Cmd =
- Script->createOutputSection(OutSec, getCurrentLocation());
+OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
+ OutputSection *cmd =
+ script->createOutputSection(outSec, getCurrentLocation());
- size_t SymbolsReferenced = Script->ReferencedSymbols.size();
+ size_t symbolsReferenced = script->referencedSymbols.size();
if (peek() != ":")
- readSectionAddressType(Cmd);
+ readSectionAddressType(cmd);
expect(":");
- std::string Location = getCurrentLocation();
+ std::string location = getCurrentLocation();
if (consume("AT"))
- Cmd->LMAExpr = readParenExpr();
+ cmd->lmaExpr = readParenExpr();
if (consume("ALIGN"))
- Cmd->AlignExpr = checkAlignment(readParenExpr(), Location);
+ cmd->alignExpr = checkAlignment(readParenExpr(), location);
if (consume("SUBALIGN"))
- Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location);
+ cmd->subalignExpr = checkAlignment(readParenExpr(), location);
// Parse constraints.
if (consume("ONLY_IF_RO"))
- Cmd->Constraint = ConstraintKind::ReadOnly;
+ cmd->constraint = ConstraintKind::ReadOnly;
if (consume("ONLY_IF_RW"))
- Cmd->Constraint = ConstraintKind::ReadWrite;
+ cmd->constraint = ConstraintKind::ReadWrite;
expect("{");
while (!errorCount() && !consume("}")) {
- StringRef Tok = next();
- if (Tok == ";") {
+ StringRef tok = next();
+ if (tok == ";") {
// Empty commands are allowed. Do nothing here.
- } else if (SymbolAssignment *Assign = readAssignment(Tok)) {
- Cmd->SectionCommands.push_back(Assign);
- } else if (ByteCommand *Data = readByteCommand(Tok)) {
- Cmd->SectionCommands.push_back(Data);
- } else if (Tok == "CONSTRUCTORS") {
+ } else if (SymbolAssignment *assign = readAssignment(tok)) {
+ cmd->sectionCommands.push_back(assign);
+ } else if (ByteCommand *data = readByteCommand(tok)) {
+ cmd->sectionCommands.push_back(data);
+ } else if (tok == "CONSTRUCTORS") {
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
// by name. This is for very old file formats such as ECOFF/XCOFF.
// For ELF, we should ignore.
- } else if (Tok == "FILL") {
- Cmd->Filler = readFill();
- } else if (Tok == "SORT") {
+ } else if (tok == "FILL") {
+ // 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("(");
+ cmd->filler = readFill();
+ expect(")");
+ } else if (tok == "SORT") {
readSort();
- } else if (Tok == "INCLUDE") {
+ } else if (tok == "INCLUDE") {
readInclude();
} else if (peek() == "(") {
- Cmd->SectionCommands.push_back(readInputSectionDescription(Tok));
+ cmd->sectionCommands.push_back(readInputSectionDescription(tok));
} else {
// 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.
- auto *ISD = make<InputSectionDescription>(Tok);
- ISD->SectionPatterns.push_back({{}, StringMatcher({"*"})});
- Cmd->SectionCommands.push_back(ISD);
+ auto *isd = make<InputSectionDescription>(tok);
+ isd->sectionPatterns.push_back({{}, StringMatcher({"*"})});
+ cmd->sectionCommands.push_back(isd);
}
}
if (consume(">"))
- Cmd->MemoryRegionName = next();
+ cmd->memoryRegionName = next();
if (consume("AT")) {
expect(">");
- Cmd->LMARegionName = next();
+ cmd->lmaRegionName = next();
}
- if (Cmd->LMAExpr && !Cmd->LMARegionName.empty())
+ if (cmd->lmaExpr && !cmd->lmaRegionName.empty())
error("section can't have both LMA and a load region");
- Cmd->Phdrs = readOutputSectionPhdrs();
+ cmd->phdrs = readOutputSectionPhdrs();
- if (consume("="))
- Cmd->Filler = parseFill(next());
- else if (peek().startswith("="))
- Cmd->Filler = parseFill(next().drop_front());
+ if (peek() == "=" || peek().startswith("=")) {
+ inExpr = true;
+ consume("=");
+ cmd->filler = readFill();
+ inExpr = false;
+ }
// Consume optional comma following output section command.
consume(",");
- if (Script->ReferencedSymbols.size() > SymbolsReferenced)
- Cmd->ExpressionsUseSymbols = true;
- return Cmd;
+ if (script->referencedSymbols.size() > symbolsReferenced)
+ cmd->expressionsUseSymbols = true;
+ return cmd;
}
-// Parses a given string as a octal/decimal/hexadecimal number and
-// returns it as a big-endian number. Used for `=<fillexp>`.
+// Reads a `=<fillexp>` expression and returns its value as a big-endian number.
// https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html
+// We do not support using symbols in such expressions.
//
// 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.
-std::array<uint8_t, 4> ScriptParser::parseFill(StringRef Tok) {
- uint32_t V = 0;
- if (!to_integer(Tok, V))
- setError("invalid filler expression: " + Tok);
+std::array<uint8_t, 4> ScriptParser::readFill() {
+ uint64_t value = readExpr()().val;
+ if (value > UINT32_MAX)
+ setError("filler expression result does not fit 32-bit: 0x" +
+ Twine::utohexstr(value));
- std::array<uint8_t, 4> Buf;
- write32be(Buf.data(), V);
- return Buf;
+ std::array<uint8_t, 4> buf;
+ write32be(buf.data(), (uint32_t)value);
+ return buf;
}
-SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) {
+SymbolAssignment *ScriptParser::readProvideHidden(bool provide, bool hidden) {
expect("(");
- SymbolAssignment *Cmd = readSymbolAssignment(next());
- Cmd->Provide = Provide;
- Cmd->Hidden = Hidden;
+ SymbolAssignment *cmd = readSymbolAssignment(next());
+ cmd->provide = provide;
+ cmd->hidden = hidden;
expect(")");
- return Cmd;
+ return cmd;
}
-SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) {
+SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
// Assert expression returns Dot, so this is equal to ".=."
- if (Tok == "ASSERT")
+ if (tok == "ASSERT")
return make<SymbolAssignment>(".", readAssert(), getCurrentLocation());
- size_t OldPos = Pos;
- SymbolAssignment *Cmd = nullptr;
+ size_t oldPos = pos;
+ SymbolAssignment *cmd = nullptr;
if (peek() == "=" || peek() == "+=")
- Cmd = readSymbolAssignment(Tok);
- else if (Tok == "PROVIDE")
- Cmd = readProvideHidden(true, false);
- else if (Tok == "HIDDEN")
- Cmd = readProvideHidden(false, true);
- else if (Tok == "PROVIDE_HIDDEN")
- Cmd = readProvideHidden(true, true);
-
- if (Cmd) {
- Cmd->CommandString =
- Tok.str() + " " +
- llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
+ cmd = readSymbolAssignment(tok);
+ else if (tok == "PROVIDE")
+ cmd = readProvideHidden(true, false);
+ else if (tok == "HIDDEN")
+ cmd = readProvideHidden(false, true);
+ else if (tok == "PROVIDE_HIDDEN")
+ cmd = readProvideHidden(true, true);
+
+ if (cmd) {
+ cmd->commandString =
+ tok.str() + " " +
+ llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " ");
expect(";");
}
- return Cmd;
+ return cmd;
}
-SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
- StringRef Op = next();
- assert(Op == "=" || Op == "+=");
- Expr E = readExpr();
- if (Op == "+=") {
- std::string Loc = getCurrentLocation();
- E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); };
+SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
+ StringRef op = next();
+ assert(op == "=" || op == "+=");
+ Expr e = readExpr();
+ if (op == "+=") {
+ std::string loc = getCurrentLocation();
+ e = [=] { return add(script->getSymbolValue(name, loc), e()); };
}
- return make<SymbolAssignment>(Name, E, getCurrentLocation());
+ return make<SymbolAssignment>(name, e, getCurrentLocation());
}
// This is an operator-precedence parser to parse a linker
@@ -956,178 +943,178 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
Expr ScriptParser::readExpr() {
// Our lexer is context-aware. Set the in-expression bit so that
// they apply different tokenization rules.
- bool Orig = InExpr;
- InExpr = true;
- Expr E = readExpr1(readPrimary(), 0);
- InExpr = Orig;
- return E;
-}
-
-Expr ScriptParser::combine(StringRef Op, Expr L, Expr R) {
- if (Op == "+")
- return [=] { return add(L(), R()); };
- if (Op == "-")
- return [=] { return sub(L(), R()); };
- if (Op == "*")
- return [=] { return L().getValue() * R().getValue(); };
- if (Op == "/") {
- std::string Loc = getCurrentLocation();
+ bool orig = inExpr;
+ inExpr = true;
+ Expr e = readExpr1(readPrimary(), 0);
+ inExpr = orig;
+ return e;
+}
+
+Expr ScriptParser::combine(StringRef op, Expr l, Expr r) {
+ if (op == "+")
+ return [=] { return add(l(), r()); };
+ if (op == "-")
+ return [=] { return sub(l(), r()); };
+ if (op == "*")
+ return [=] { return l().getValue() * r().getValue(); };
+ if (op == "/") {
+ std::string loc = getCurrentLocation();
return [=]() -> uint64_t {
- if (uint64_t RV = R().getValue())
- return L().getValue() / RV;
- error(Loc + ": division by zero");
+ if (uint64_t rv = r().getValue())
+ return l().getValue() / rv;
+ error(loc + ": division by zero");
return 0;
};
}
- if (Op == "%") {
- std::string Loc = getCurrentLocation();
+ if (op == "%") {
+ std::string loc = getCurrentLocation();
return [=]() -> uint64_t {
- if (uint64_t RV = R().getValue())
- return L().getValue() % RV;
- error(Loc + ": modulo by zero");
+ if (uint64_t rv = r().getValue())
+ return l().getValue() % rv;
+ error(loc + ": modulo by zero");
return 0;
};
}
- if (Op == "<<")
- return [=] { return L().getValue() << R().getValue(); };
- if (Op == ">>")
- return [=] { return L().getValue() >> R().getValue(); };
- if (Op == "<")
- return [=] { return L().getValue() < R().getValue(); };
- if (Op == ">")
- return [=] { return L().getValue() > R().getValue(); };
- if (Op == ">=")
- return [=] { return L().getValue() >= R().getValue(); };
- if (Op == "<=")
- return [=] { return L().getValue() <= R().getValue(); };
- if (Op == "==")
- return [=] { return L().getValue() == R().getValue(); };
- if (Op == "!=")
- return [=] { return L().getValue() != R().getValue(); };
- if (Op == "||")
- return [=] { return L().getValue() || R().getValue(); };
- if (Op == "&&")
- return [=] { return L().getValue() && R().getValue(); };
- if (Op == "&")
- return [=] { return bitAnd(L(), R()); };
- if (Op == "|")
- return [=] { return bitOr(L(), R()); };
+ if (op == "<<")
+ return [=] { return l().getValue() << r().getValue(); };
+ if (op == ">>")
+ return [=] { return l().getValue() >> r().getValue(); };
+ if (op == "<")
+ return [=] { return l().getValue() < r().getValue(); };
+ if (op == ">")
+ return [=] { return l().getValue() > r().getValue(); };
+ if (op == ">=")
+ return [=] { return l().getValue() >= r().getValue(); };
+ if (op == "<=")
+ return [=] { return l().getValue() <= r().getValue(); };
+ if (op == "==")
+ return [=] { return l().getValue() == r().getValue(); };
+ if (op == "!=")
+ return [=] { return l().getValue() != r().getValue(); };
+ if (op == "||")
+ return [=] { return l().getValue() || r().getValue(); };
+ if (op == "&&")
+ return [=] { return l().getValue() && r().getValue(); };
+ if (op == "&")
+ return [=] { return bitAnd(l(), r()); };
+ if (op == "|")
+ return [=] { return bitOr(l(), r()); };
llvm_unreachable("invalid operator");
}
// This is a part of the operator-precedence parser. This function
// assumes that the remaining token stream starts with an operator.
-Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
+Expr ScriptParser::readExpr1(Expr lhs, int minPrec) {
while (!atEOF() && !errorCount()) {
// Read an operator and an expression.
if (consume("?"))
- return readTernary(Lhs);
- StringRef Op1 = peek();
- if (precedence(Op1) < MinPrec)
+ return readTernary(lhs);
+ StringRef op1 = peek();
+ if (precedence(op1) < minPrec)
break;
skip();
- Expr Rhs = readPrimary();
+ Expr rhs = readPrimary();
// Evaluate the remaining part of the expression first if the
// next operator has greater precedence than the previous one.
// For example, if we have read "+" and "3", and if the next
// operator is "*", then we'll evaluate 3 * ... part first.
while (!atEOF()) {
- StringRef Op2 = peek();
- if (precedence(Op2) <= precedence(Op1))
+ StringRef op2 = peek();
+ if (precedence(op2) <= precedence(op1))
break;
- Rhs = readExpr1(Rhs, precedence(Op2));
+ rhs = readExpr1(rhs, precedence(op2));
}
- Lhs = combine(Op1, Lhs, Rhs);
+ lhs = combine(op1, lhs, rhs);
}
- return Lhs;
+ return lhs;
}
Expr ScriptParser::getPageSize() {
- std::string Location = getCurrentLocation();
+ std::string location = getCurrentLocation();
return [=]() -> uint64_t {
- if (Target)
- return Target->PageSize;
- error(Location + ": unable to calculate page size");
+ if (target)
+ return config->commonPageSize;
+ error(location + ": unable to calculate page size");
return 4096; // Return a dummy value.
};
}
Expr ScriptParser::readConstant() {
- StringRef S = readParenLiteral();
- if (S == "COMMONPAGESIZE")
+ StringRef s = readParenLiteral();
+ if (s == "COMMONPAGESIZE")
return getPageSize();
- if (S == "MAXPAGESIZE")
- return [] { return Config->MaxPageSize; };
- setError("unknown constant: " + S);
+ if (s == "MAXPAGESIZE")
+ return [] { return config->maxPageSize; };
+ setError("unknown constant: " + s);
return [] { return 0; };
}
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
// "0x" or suffixed with "H") and decimal numbers. Decimal numbers may
// have "K" (Ki) or "M" (Mi) suffixes.
-static Optional<uint64_t> parseInt(StringRef Tok) {
+static Optional<uint64_t> parseInt(StringRef tok) {
// Hexadecimal
- uint64_t Val;
- if (Tok.startswith_lower("0x")) {
- if (!to_integer(Tok.substr(2), Val, 16))
+ uint64_t val;
+ if (tok.startswith_lower("0x")) {
+ if (!to_integer(tok.substr(2), val, 16))
return None;
- return Val;
+ return val;
}
- if (Tok.endswith_lower("H")) {
- if (!to_integer(Tok.drop_back(), Val, 16))
+ if (tok.endswith_lower("H")) {
+ if (!to_integer(tok.drop_back(), val, 16))
return None;
- return Val;
+ return val;
}
// Decimal
- if (Tok.endswith_lower("K")) {
- if (!to_integer(Tok.drop_back(), Val, 10))
+ if (tok.endswith_lower("K")) {
+ if (!to_integer(tok.drop_back(), val, 10))
return None;
- return Val * 1024;
+ return val * 1024;
}
- if (Tok.endswith_lower("M")) {
- if (!to_integer(Tok.drop_back(), Val, 10))
+ if (tok.endswith_lower("M")) {
+ if (!to_integer(tok.drop_back(), val, 10))
return None;
- return Val * 1024 * 1024;
+ return val * 1024 * 1024;
}
- if (!to_integer(Tok, Val, 10))
+ if (!to_integer(tok, val, 10))
return None;
- return Val;
+ return val;
}
-ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
- int Size = StringSwitch<int>(Tok)
+ByteCommand *ScriptParser::readByteCommand(StringRef tok) {
+ int size = StringSwitch<int>(tok)
.Case("BYTE", 1)
.Case("SHORT", 2)
.Case("LONG", 4)
.Case("QUAD", 8)
.Default(-1);
- if (Size == -1)
+ if (size == -1)
return nullptr;
- size_t OldPos = Pos;
- Expr E = readParenExpr();
- std::string CommandString =
- Tok.str() + " " +
- llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
- return make<ByteCommand>(E, Size, CommandString);
+ size_t oldPos = pos;
+ Expr e = readParenExpr();
+ std::string commandString =
+ tok.str() + " " +
+ llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " ");
+ return make<ByteCommand>(e, size, commandString);
}
StringRef ScriptParser::readParenLiteral() {
expect("(");
- bool Orig = InExpr;
- InExpr = false;
- StringRef Tok = next();
- InExpr = Orig;
+ bool orig = inExpr;
+ inExpr = false;
+ StringRef tok = next();
+ inExpr = orig;
expect(")");
- return Tok;
+ return tok;
}
-static void checkIfExists(OutputSection *Cmd, StringRef Location) {
- if (Cmd->Location.empty() && Script->ErrorOnMissingSection)
- error(Location + ": undefined section " + Cmd->Name);
+static void checkIfExists(OutputSection *cmd, StringRef location) {
+ if (cmd->location.empty() && script->errorOnMissingSection)
+ error(location + ": undefined section " + cmd->name);
}
Expr ScriptParser::readPrimary() {
@@ -1135,84 +1122,85 @@ Expr ScriptParser::readPrimary() {
return readParenExpr();
if (consume("~")) {
- Expr E = readPrimary();
- return [=] { return ~E().getValue(); };
+ Expr e = readPrimary();
+ return [=] { return ~e().getValue(); };
}
if (consume("!")) {
- Expr E = readPrimary();
- return [=] { return !E().getValue(); };
+ Expr e = readPrimary();
+ return [=] { return !e().getValue(); };
}
if (consume("-")) {
- Expr E = readPrimary();
- return [=] { return -E().getValue(); };
+ Expr e = readPrimary();
+ return [=] { return -e().getValue(); };
}
- StringRef Tok = next();
- std::string Location = getCurrentLocation();
+ StringRef tok = next();
+ std::string location = getCurrentLocation();
// Built-in functions are parsed here.
// https://sourceware.org/binutils/docs/ld/Builtin-Functions.html.
- if (Tok == "ABSOLUTE") {
- Expr Inner = readParenExpr();
+ if (tok == "ABSOLUTE") {
+ Expr inner = readParenExpr();
return [=] {
- ExprValue I = Inner();
- I.ForceAbsolute = true;
- return I;
+ ExprValue i = inner();
+ i.forceAbsolute = true;
+ return i;
};
}
- if (Tok == "ADDR") {
- StringRef Name = readParenLiteral();
- OutputSection *Sec = Script->getOrCreateOutputSection(Name);
+ if (tok == "ADDR") {
+ StringRef name = readParenLiteral();
+ OutputSection *sec = script->getOrCreateOutputSection(name);
+ sec->usedInExpression = true;
return [=]() -> ExprValue {
- checkIfExists(Sec, Location);
- return {Sec, false, 0, Location};
+ checkIfExists(sec, location);
+ return {sec, false, 0, location};
};
}
- if (Tok == "ALIGN") {
+ if (tok == "ALIGN") {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
if (consume(")")) {
- E = checkAlignment(E, Location);
- return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ e = checkAlignment(e, location);
+ return [=] { return alignTo(script->getDot(), e().getValue()); };
}
expect(",");
- Expr E2 = checkAlignment(readExpr(), Location);
+ Expr e2 = checkAlignment(readExpr(), location);
expect(")");
return [=] {
- ExprValue V = E();
- V.Alignment = E2().getValue();
- return V;
+ ExprValue v = e();
+ v.alignment = e2().getValue();
+ return v;
};
}
- if (Tok == "ALIGNOF") {
- StringRef Name = readParenLiteral();
- OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ if (tok == "ALIGNOF") {
+ StringRef name = readParenLiteral();
+ OutputSection *cmd = script->getOrCreateOutputSection(name);
return [=] {
- checkIfExists(Cmd, Location);
- return Cmd->Alignment;
+ checkIfExists(cmd, location);
+ return cmd->alignment;
};
}
- if (Tok == "ASSERT")
+ if (tok == "ASSERT")
return readAssert();
- if (Tok == "CONSTANT")
+ if (tok == "CONSTANT")
return readConstant();
- if (Tok == "DATA_SEGMENT_ALIGN") {
+ if (tok == "DATA_SEGMENT_ALIGN") {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(",");
readExpr();
expect(")");
return [=] {
- return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue()));
+ return alignTo(script->getDot(), std::max((uint64_t)1, e().getValue()));
};
}
- if (Tok == "DATA_SEGMENT_END") {
+ if (tok == "DATA_SEGMENT_END") {
expect("(");
expect(".");
expect(")");
- return [] { return Script->getDot(); };
+ return [] { return script->getDot(); };
}
- if (Tok == "DATA_SEGMENT_RELRO_END") {
+ if (tok == "DATA_SEGMENT_RELRO_END") {
// GNU linkers implements more complicated logic to handle
// DATA_SEGMENT_RELRO_END. We instead ignore the arguments and
// just align to the next page boundary for simplicity.
@@ -1221,112 +1209,113 @@ Expr ScriptParser::readPrimary() {
expect(",");
readExpr();
expect(")");
- Expr E = getPageSize();
- return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ Expr e = getPageSize();
+ return [=] { return alignTo(script->getDot(), e().getValue()); };
}
- if (Tok == "DEFINED") {
- StringRef Name = readParenLiteral();
- return [=] { return Symtab->find(Name) ? 1 : 0; };
+ if (tok == "DEFINED") {
+ StringRef name = readParenLiteral();
+ return [=] { return symtab->find(name) ? 1 : 0; };
}
- if (Tok == "LENGTH") {
- StringRef Name = readParenLiteral();
- if (Script->MemoryRegions.count(Name) == 0) {
- setError("memory region not defined: " + Name);
+ if (tok == "LENGTH") {
+ StringRef name = readParenLiteral();
+ if (script->memoryRegions.count(name) == 0) {
+ setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return Script->MemoryRegions[Name]->Length; };
+ return [=] { return script->memoryRegions[name]->length; };
}
- if (Tok == "LOADADDR") {
- StringRef Name = readParenLiteral();
- OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ if (tok == "LOADADDR") {
+ StringRef name = readParenLiteral();
+ OutputSection *cmd = script->getOrCreateOutputSection(name);
+ cmd->usedInExpression = true;
return [=] {
- checkIfExists(Cmd, Location);
- return Cmd->getLMA();
+ checkIfExists(cmd, location);
+ return cmd->getLMA();
};
}
- if (Tok == "MAX" || Tok == "MIN") {
+ if (tok == "MAX" || tok == "MIN") {
expect("(");
- Expr A = readExpr();
+ Expr a = readExpr();
expect(",");
- Expr B = readExpr();
+ Expr b = readExpr();
expect(")");
- if (Tok == "MIN")
- return [=] { return std::min(A().getValue(), B().getValue()); };
- return [=] { return std::max(A().getValue(), B().getValue()); };
+ if (tok == "MIN")
+ return [=] { return std::min(a().getValue(), b().getValue()); };
+ return [=] { return std::max(a().getValue(), b().getValue()); };
}
- if (Tok == "ORIGIN") {
- StringRef Name = readParenLiteral();
- if (Script->MemoryRegions.count(Name) == 0) {
- setError("memory region not defined: " + Name);
+ if (tok == "ORIGIN") {
+ StringRef name = readParenLiteral();
+ if (script->memoryRegions.count(name) == 0) {
+ setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return Script->MemoryRegions[Name]->Origin; };
+ return [=] { return script->memoryRegions[name]->origin; };
}
- if (Tok == "SEGMENT_START") {
+ if (tok == "SEGMENT_START") {
expect("(");
skip();
expect(",");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(")");
- return [=] { return E(); };
+ return [=] { return e(); };
}
- if (Tok == "SIZEOF") {
- StringRef Name = readParenLiteral();
- OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ if (tok == "SIZEOF") {
+ StringRef name = readParenLiteral();
+ OutputSection *cmd = script->getOrCreateOutputSection(name);
// Linker script does not create an output section if its content is empty.
// We want to allow SIZEOF(.foo) where .foo is a section which happened to
// be empty.
- return [=] { return Cmd->Size; };
+ return [=] { return cmd->size; };
}
- if (Tok == "SIZEOF_HEADERS")
+ if (tok == "SIZEOF_HEADERS")
return [=] { return elf::getHeaderSize(); };
// Tok is the dot.
- if (Tok == ".")
- return [=] { return Script->getSymbolValue(Tok, Location); };
+ if (tok == ".")
+ return [=] { return script->getSymbolValue(tok, location); };
// Tok is a literal number.
- if (Optional<uint64_t> Val = parseInt(Tok))
- return [=] { return *Val; };
+ if (Optional<uint64_t> val = parseInt(tok))
+ return [=] { return *val; };
// Tok is a symbol name.
- if (!isValidCIdentifier(Tok))
- setError("malformed number: " + Tok);
- Script->ReferencedSymbols.push_back(Tok);
- return [=] { return Script->getSymbolValue(Tok, Location); };
+ if (!isValidCIdentifier(tok))
+ setError("malformed number: " + tok);
+ script->referencedSymbols.push_back(tok);
+ return [=] { return script->getSymbolValue(tok, location); };
}
-Expr ScriptParser::readTernary(Expr Cond) {
- Expr L = readExpr();
+Expr ScriptParser::readTernary(Expr cond) {
+ Expr l = readExpr();
expect(":");
- Expr R = readExpr();
- return [=] { return Cond().getValue() ? L() : R(); };
+ Expr r = readExpr();
+ return [=] { return cond().getValue() ? l() : r(); };
}
Expr ScriptParser::readParenExpr() {
expect("(");
- Expr E = readExpr();
+ Expr e = readExpr();
expect(")");
- return E;
+ return e;
}
std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
- std::vector<StringRef> Phdrs;
+ std::vector<StringRef> phdrs;
while (!errorCount() && peek().startswith(":")) {
- StringRef Tok = next();
- Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1));
+ StringRef tok = next();
+ phdrs.push_back((tok.size() == 1) ? next() : tok.substr(1));
}
- return Phdrs;
+ return phdrs;
}
// Read a program header type name. The next token must be a
// name of a program header type or a constant (e.g. "0x3").
unsigned ScriptParser::readPhdrType() {
- StringRef Tok = next();
- if (Optional<uint64_t> Val = parseInt(Tok))
- return *Val;
+ StringRef tok = next();
+ if (Optional<uint64_t> val = parseInt(tok))
+ return *val;
- unsigned Ret = StringSwitch<unsigned>(Tok)
+ unsigned ret = StringSwitch<unsigned>(tok)
.Case("PT_NULL", PT_NULL)
.Case("PT_LOAD", PT_LOAD)
.Case("PT_DYNAMIC", PT_DYNAMIC)
@@ -1343,56 +1332,56 @@ unsigned ScriptParser::readPhdrType() {
.Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA)
.Default(-1);
- if (Ret == (unsigned)-1) {
- setError("invalid program header type: " + Tok);
+ if (ret == (unsigned)-1) {
+ setError("invalid program header type: " + tok);
return PT_NULL;
}
- return Ret;
+ return ret;
}
// Reads an anonymous version declaration.
void ScriptParser::readAnonymousDeclaration() {
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::tie(Locals, Globals) = readSymbols();
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::tie(locals, globals) = readSymbols();
- for (SymbolVersion V : Locals) {
- if (V.Name == "*")
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ for (SymbolVersion v : locals) {
+ if (v.name == "*")
+ config->defaultSymbolVersion = VER_NDX_LOCAL;
else
- Config->VersionScriptLocals.push_back(V);
+ config->versionScriptLocals.push_back(v);
}
- for (SymbolVersion V : Globals)
- Config->VersionScriptGlobals.push_back(V);
+ for (SymbolVersion v : globals)
+ config->versionScriptGlobals.push_back(v);
expect(";");
}
// Reads a non-anonymous version definition,
// e.g. "VerStr { global: foo; bar; local: *; };".
-void ScriptParser::readVersionDeclaration(StringRef VerStr) {
+void ScriptParser::readVersionDeclaration(StringRef verStr) {
// Read a symbol list.
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::tie(Locals, Globals) = readSymbols();
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::tie(locals, globals) = readSymbols();
- for (SymbolVersion V : Locals) {
- if (V.Name == "*")
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ for (SymbolVersion v : locals) {
+ if (v.name == "*")
+ config->defaultSymbolVersion = VER_NDX_LOCAL;
else
- Config->VersionScriptLocals.push_back(V);
+ config->versionScriptLocals.push_back(v);
}
// Create a new version definition and add that to the global symbols.
- VersionDefinition Ver;
- Ver.Name = VerStr;
- Ver.Globals = Globals;
+ VersionDefinition ver;
+ ver.name = verStr;
+ ver.globals = globals;
// User-defined version number starts from 2 because 0 and 1 are
// reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively.
- Ver.Id = Config->VersionDefinitions.size() + 2;
- Config->VersionDefinitions.push_back(Ver);
+ ver.id = config->versionDefinitions.size() + 2;
+ config->versionDefinitions.push_back(ver);
// Each version may have a parent version. For example, "Ver2"
// defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1"
@@ -1404,39 +1393,39 @@ void ScriptParser::readVersionDeclaration(StringRef VerStr) {
expect(";");
}
-static bool hasWildcard(StringRef S) {
- return S.find_first_of("?*[") != StringRef::npos;
+static bool hasWildcard(StringRef s) {
+ return s.find_first_of("?*[") != StringRef::npos;
}
// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };".
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
ScriptParser::readSymbols() {
- std::vector<SymbolVersion> Locals;
- std::vector<SymbolVersion> Globals;
- std::vector<SymbolVersion> *V = &Globals;
+ std::vector<SymbolVersion> locals;
+ std::vector<SymbolVersion> globals;
+ std::vector<SymbolVersion> *v = &globals;
while (!errorCount()) {
if (consume("}"))
break;
if (consumeLabel("local")) {
- V = &Locals;
+ v = &locals;
continue;
}
if (consumeLabel("global")) {
- V = &Globals;
+ v = &globals;
continue;
}
if (consume("extern")) {
- std::vector<SymbolVersion> Ext = readVersionExtern();
- V->insert(V->end(), Ext.begin(), Ext.end());
+ std::vector<SymbolVersion> ext = readVersionExtern();
+ v->insert(v->end(), ext.begin(), ext.end());
} else {
- StringRef Tok = next();
- V->push_back({unquote(Tok), false, hasWildcard(Tok)});
+ StringRef tok = next();
+ v->push_back({unquote(tok), false, hasWildcard(tok)});
}
expect(";");
}
- return {Locals, Globals};
+ return {locals, globals};
}
// Reads an "extern C++" directive, e.g.,
@@ -1445,30 +1434,30 @@ ScriptParser::readSymbols() {
// The last semicolon is optional. E.g. this is OK:
// "extern "C++" { ns::*; "f(int, double)" };"
std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
- StringRef Tok = next();
- bool IsCXX = Tok == "\"C++\"";
- if (!IsCXX && Tok != "\"C\"")
+ StringRef tok = next();
+ bool isCXX = tok == "\"C++\"";
+ if (!isCXX && tok != "\"C\"")
setError("Unknown language");
expect("{");
- std::vector<SymbolVersion> Ret;
+ std::vector<SymbolVersion> ret;
while (!errorCount() && peek() != "}") {
- StringRef Tok = next();
- bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
- Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
+ StringRef tok = next();
+ ret.push_back(
+ {unquote(tok), isCXX, !tok.startswith("\"") && hasWildcard(tok)});
if (consume("}"))
- return Ret;
+ return ret;
expect(";");
}
expect("}");
- return Ret;
+ return ret;
}
-uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
- StringRef S3) {
- if (!consume(S1) && !consume(S2) && !consume(S3)) {
- setError("expected one of: " + S1 + ", " + S2 + ", or " + S3);
+uint64_t 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;
}
expect("=");
@@ -1482,28 +1471,28 @@ uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
void ScriptParser::readMemory() {
expect("{");
while (!errorCount() && !consume("}")) {
- StringRef Tok = next();
- if (Tok == "INCLUDE") {
+ StringRef tok = next();
+ if (tok == "INCLUDE") {
readInclude();
continue;
}
- uint32_t Flags = 0;
- uint32_t NegFlags = 0;
+ uint32_t flags = 0;
+ uint32_t negFlags = 0;
if (consume("(")) {
- std::tie(Flags, NegFlags) = readMemoryAttributes();
+ std::tie(flags, negFlags) = readMemoryAttributes();
expect(")");
}
expect(":");
- uint64_t Origin = readMemoryAssignment("ORIGIN", "org", "o");
+ uint64_t origin = readMemoryAssignment("ORIGIN", "org", "o");
expect(",");
- uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
+ uint64_t length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
- MemoryRegion *MR = make<MemoryRegion>(Tok, Origin, Length, Flags, NegFlags);
- if (!Script->MemoryRegions.insert({Tok, MR}).second)
- setError("region '" + Tok + "' already defined");
+ MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
+ if (!script->memoryRegions.insert({tok, mr}).second)
+ setError("region '" + tok + "' already defined");
}
}
@@ -1511,43 +1500,43 @@ void ScriptParser::readMemory() {
// flags when placing output sections in a memory region. These flags
// are only used when an explicit memory region name is not used.
std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
- uint32_t Flags = 0;
- uint32_t NegFlags = 0;
- bool Invert = false;
-
- for (char C : next().lower()) {
- uint32_t Flag = 0;
- if (C == '!')
- Invert = !Invert;
- else if (C == 'w')
- Flag = SHF_WRITE;
- else if (C == 'x')
- Flag = SHF_EXECINSTR;
- else if (C == 'a')
- Flag = SHF_ALLOC;
- else if (C != 'r')
+ uint32_t flags = 0;
+ uint32_t negFlags = 0;
+ bool invert = false;
+
+ for (char c : next().lower()) {
+ uint32_t flag = 0;
+ if (c == '!')
+ invert = !invert;
+ else if (c == 'w')
+ flag = SHF_WRITE;
+ else if (c == 'x')
+ flag = SHF_EXECINSTR;
+ else if (c == 'a')
+ flag = SHF_ALLOC;
+ else if (c != 'r')
setError("invalid memory region attribute");
- if (Invert)
- NegFlags |= Flag;
+ if (invert)
+ negFlags |= flag;
else
- Flags |= Flag;
+ flags |= flag;
}
- return {Flags, NegFlags};
+ return {flags, negFlags};
}
-void elf::readLinkerScript(MemoryBufferRef MB) {
- ScriptParser(MB).readLinkerScript();
+void elf::readLinkerScript(MemoryBufferRef mb) {
+ ScriptParser(mb).readLinkerScript();
}
-void elf::readVersionScript(MemoryBufferRef MB) {
- ScriptParser(MB).readVersionScript();
+void elf::readVersionScript(MemoryBufferRef mb) {
+ ScriptParser(mb).readVersionScript();
}
-void elf::readDynamicList(MemoryBufferRef MB) {
- ScriptParser(MB).readDynamicList();
+void elf::readDynamicList(MemoryBufferRef mb) {
+ ScriptParser(mb).readDynamicList();
}
-void elf::readDefsym(StringRef Name, MemoryBufferRef MB) {
- ScriptParser(MB).readDefsym(Name);
+void elf::readDefsym(StringRef name, MemoryBufferRef mb) {
+ ScriptParser(mb).readDefsym(name);
}
diff --git a/ELF/ScriptParser.h b/ELF/ScriptParser.h
index d48d5aa2115e..c953fb302b9a 100644
--- a/ELF/ScriptParser.h
+++ b/ELF/ScriptParser.h
@@ -1,9 +1,8 @@
//===- ScriptParser.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -17,16 +16,16 @@ namespace lld {
namespace elf {
// Parses a linker script. Calling this function updates
-// Config and ScriptConfig.
-void readLinkerScript(MemoryBufferRef MB);
+// lld::elf::config and lld::elf::script.
+void readLinkerScript(MemoryBufferRef mb);
// Parses a version script.
-void readVersionScript(MemoryBufferRef MB);
+void readVersionScript(MemoryBufferRef mb);
-void readDynamicList(MemoryBufferRef MB);
+void readDynamicList(MemoryBufferRef mb);
// Parses the defsym expression.
-void readDefsym(StringRef Name, MemoryBufferRef MB);
+void readDefsym(StringRef name, MemoryBufferRef mb);
} // namespace elf
} // namespace lld
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index 7615e12199fa..3faeed8c2bdc 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -1,9 +1,8 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -31,576 +30,76 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-SymbolTable *elf::Symtab;
-
-static InputFile *getFirstElf() {
- if (!ObjectFiles.empty())
- return ObjectFiles[0];
- if (!SharedFiles.empty())
- return SharedFiles[0];
- return BitcodeFiles[0];
-}
-
-// All input object files must be for the same architecture
-// (e.g. it does not make sense to link x86 object files with
-// MIPS object files.) This function checks for that error.
-static bool isCompatible(InputFile *F) {
- if (!F->isElf() && !isa<BitcodeFile>(F))
- return true;
-
- if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) {
- if (Config->EMachine != EM_MIPS)
- return true;
- if (isMipsN32Abi(F) == Config->MipsN32Abi)
- return true;
- }
-
- if (!Config->Emulation.empty())
- error(toString(F) + " is incompatible with " + Config->Emulation);
- else
- error(toString(F) + " is incompatible with " + toString(getFirstElf()));
- return false;
-}
-
-// Add symbols in File to the symbol table.
-template <class ELFT> void SymbolTable::addFile(InputFile *File) {
- if (!isCompatible(File))
- return;
-
- // Binary file
- if (auto *F = dyn_cast<BinaryFile>(File)) {
- BinaryFiles.push_back(F);
- F->parse();
- return;
- }
-
- // .a file
- if (auto *F = dyn_cast<ArchiveFile>(File)) {
- F->parse<ELFT>();
- return;
- }
-
- // Lazy object file
- if (auto *F = dyn_cast<LazyObjFile>(File)) {
- LazyObjFiles.push_back(F);
- F->parse<ELFT>();
- return;
- }
-
- if (Config->Trace)
- message(toString(File));
-
- // .so file
- if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
- // DSOs are uniquified not by filename but by soname.
- F->parseSoName();
- if (errorCount())
- return;
-
- // If a DSO appears more than once on the command line with and without
- // --as-needed, --no-as-needed takes precedence over --as-needed because a
- // user can add an extra DSO with --no-as-needed to force it to be added to
- // the dependency list.
- DenseMap<StringRef, InputFile *>::iterator It;
- bool WasInserted;
- std::tie(It, WasInserted) = SoNames.try_emplace(F->SoName, F);
- cast<SharedFile<ELFT>>(It->second)->IsNeeded |= F->IsNeeded;
- if (!WasInserted)
- return;
-
- SharedFiles.push_back(F);
- F->parseRest();
- return;
- }
-
- // LLVM bitcode file
- if (auto *F = dyn_cast<BitcodeFile>(File)) {
- BitcodeFiles.push_back(F);
- F->parse<ELFT>(ComdatGroups);
- return;
- }
-
- // Regular object file
- ObjectFiles.push_back(File);
- cast<ObjFile<ELFT>>(File)->parse(ComdatGroups);
-}
-
-// This function is where all the optimizations of link-time
-// optimization happens. When LTO is in use, some input files are
-// not in native object file format but in the LLVM bitcode format.
-// This function compiles bitcode files into a few big native files
-// using LLVM functions and replaces bitcode symbols with the results.
-// Because all bitcode files that the program consists of are passed
-// to the compiler at once, it can do whole-program optimization.
-template <class ELFT> void SymbolTable::addCombinedLTOObject() {
- if (BitcodeFiles.empty())
- return;
-
- // Compile bitcode files and replace bitcode symbols.
- LTO.reset(new BitcodeCompiler);
- for (BitcodeFile *F : BitcodeFiles)
- LTO->add(*F);
-
- for (InputFile *File : LTO->compile()) {
- DenseSet<CachedHashStringRef> DummyGroups;
- auto *Obj = cast<ObjFile<ELFT>>(File);
- Obj->parse(DummyGroups);
- for (Symbol *Sym : Obj->getGlobalSymbols())
- Sym->parseSymbolVersion();
- ObjectFiles.push_back(File);
- }
-}
-
-// Set a flag for --trace-symbol so that we can print out a log message
-// if a new symbol with the same name is inserted into the symbol table.
-void SymbolTable::trace(StringRef Name) {
- SymMap.insert({CachedHashStringRef(Name), -1});
-}
+SymbolTable *elf::symtab;
-void SymbolTable::wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap) {
+void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
// Swap symbols as instructed by -wrap.
- int &Idx1 = SymMap[CachedHashStringRef(Sym->getName())];
- int &Idx2 = SymMap[CachedHashStringRef(Real->getName())];
- int &Idx3 = SymMap[CachedHashStringRef(Wrap->getName())];
+ int &idx1 = symMap[CachedHashStringRef(sym->getName())];
+ int &idx2 = symMap[CachedHashStringRef(real->getName())];
+ int &idx3 = symMap[CachedHashStringRef(wrap->getName())];
- Idx2 = Idx1;
- Idx1 = Idx3;
+ 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();
- memcpy(Real, Sym, sizeof(SymbolUnion));
- Real->setName(S);
-}
-
-static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
- if (VA == STV_DEFAULT)
- return VB;
- if (VB == STV_DEFAULT)
- return VA;
- return std::min(VA, VB);
+ StringRef s = real->getName();
+ memcpy(real, sym, sizeof(SymbolUnion));
+ real->setName(s);
}
-// Find an existing symbol or create and insert a new one.
-std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) {
+// Find an existing symbol or create a new one.
+Symbol *SymbolTable::insert(StringRef name) {
// <name>@@<version> means the symbol is the default version. In that
// case <name>@@<version> will be used to resolve references to <name>.
//
// Since this is a hot path, the following string search code is
// optimized for speed. StringRef::find(char) is much faster than
// StringRef::find(StringRef).
- size_t Pos = Name.find('@');
- if (Pos != StringRef::npos && Pos + 1 < Name.size() && Name[Pos + 1] == '@')
- Name = Name.take_front(Pos);
-
- auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()});
- int &SymIndex = P.first->second;
- bool IsNew = P.second;
- bool Traced = false;
-
- if (SymIndex == -1) {
- SymIndex = SymVector.size();
- IsNew = true;
- Traced = true;
- }
-
- if (!IsNew)
- return {SymVector[SymIndex], false};
-
- auto *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
- Sym->SymbolKind = Symbol::PlaceholderKind;
- Sym->Visibility = STV_DEFAULT;
- Sym->IsUsedInRegularObj = false;
- Sym->ExportDynamic = false;
- Sym->CanInline = true;
- Sym->Traced = Traced;
- Sym->VersionId = Config->DefaultSymbolVersion;
- SymVector.push_back(Sym);
- return {Sym, true};
+ size_t pos = name.find('@');
+ if (pos != StringRef::npos && pos + 1 < name.size() && name[pos + 1] == '@')
+ name = name.take_front(pos);
+
+ auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()});
+ int &symIndex = p.first->second;
+ bool isNew = p.second;
+
+ if (!isNew)
+ return symVector[symIndex];
+
+ Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ symVector.push_back(sym);
+
+ sym->setName(name);
+ sym->symbolKind = Symbol::PlaceholderKind;
+ sym->versionId = config->defaultSymbolVersion;
+ sym->visibility = STV_DEFAULT;
+ sym->isUsedInRegularObj = false;
+ sym->exportDynamic = false;
+ sym->canInline = true;
+ sym->scriptDefined = false;
+ sym->partition = 1;
+ return sym;
}
-// Find an existing symbol or create and insert a new one, then apply the given
-// attributes.
-std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name,
- uint8_t Visibility,
- bool CanOmitFromDynSym,
- InputFile *File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insertName(Name);
-
- // Merge in the new symbol's visibility.
- S->Visibility = getMinVisibility(S->Visibility, Visibility);
-
- if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
- S->ExportDynamic = true;
-
- if (!File || File->kind() == InputFile::ObjKind)
- S->IsUsedInRegularObj = true;
-
- return {S, WasInserted};
+Symbol *SymbolTable::addSymbol(const Symbol &New) {
+ Symbol *sym = symtab->insert(New.getName());
+ sym->resolve(New);
+ return sym;
}
-static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; }
-
-template <class ELFT>
-Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym, InputFile *File) {
- Symbol *S;
- bool WasInserted;
- uint8_t Visibility = getVisibility(StOther);
- std::tie(S, WasInserted) = insert(Name, Visibility, CanOmitFromDynSym, File);
-
- // An undefined symbol with non default visibility must be satisfied
- // in the same DSO.
- if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) {
- replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type);
- return S;
- }
-
- if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK))
- S->Binding = Binding;
-
- if (S->isLazy()) {
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
- if (Binding == STB_WEAK) {
- S->Type = Type;
- return S;
- }
-
- // Do extra check for --warn-backrefs.
- //
- // --warn-backrefs is an option to prevent an undefined reference from
- // fetching an archive member written earlier in the command line. It can be
- // used to keep compatibility with GNU linkers to some degree.
- // I'll explain the feature and why you may find it useful in this comment.
- //
- // lld's symbol resolution semantics is more relaxed than traditional Unix
- // linkers. For example,
- //
- // ld.lld foo.a bar.o
- //
- // succeeds even if bar.o contains an undefined symbol that has to be
- // resolved by some object file in foo.a. Traditional Unix linkers don't
- // allow this kind of backward reference, as they visit each file only once
- // from left to right in the command line while resolving all undefined
- // symbols at the moment of visiting.
- //
- // In the above case, since there's no undefined symbol when a linker visits
- // foo.a, no files are pulled out from foo.a, and because the linker forgets
- // about foo.a after visiting, it can't resolve undefined symbols in bar.o
- // that could have been resolved otherwise.
- //
- // That lld accepts more relaxed form means that (besides it'd make more
- // sense) you can accidentally write a command line or a build file that
- // works only with lld, even if you have a plan to distribute it to wider
- // users who may be using GNU linkers. With --warn-backrefs, you can detect
- // a library order that doesn't work with other Unix linkers.
- //
- // The option is also useful to detect cyclic dependencies between static
- // archives. Again, lld accepts
- //
- // ld.lld foo.a bar.a
- //
- // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is
- // handled as an error.
- //
- // Here is how the option works. We assign a group ID to each file. A file
- // with a smaller group ID can pull out object files from an archive file
- // with an equal or greater group ID. Otherwise, it is a reverse dependency
- // and an error.
- //
- // A file outside --{start,end}-group gets a fresh ID when instantiated. All
- // files within the same --{start,end}-group get the same group ID. E.g.
- //
- // ld.lld A B --start-group C D --end-group E
- //
- // A forms group 0. B form group 1. C and D (including their member object
- // files) form group 2. E forms group 3. I think that you can see how this
- // group assignment rule simulates the traditional linker's semantics.
- bool Backref =
- Config->WarnBackrefs && File && S->File->GroupId < File->GroupId;
- fetchLazy<ELFT>(S);
-
- // We don't report backward references to weak symbols as they can be
- // overridden later.
- if (Backref && S->Binding != STB_WEAK)
- warn("backward reference detected: " + Name + " in " + toString(File) +
- " refers to " + toString(S->File));
- }
- return S;
-}
-
-// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
-// foo@@VER. We want to effectively ignore foo, so give precedence to
-// foo@@VER.
-// FIXME: If users can transition to using
-// .symver foo,foo@@@VER
-// we can delete this hack.
-static int compareVersion(Symbol *S, StringRef Name) {
- bool A = Name.contains("@@");
- bool B = S->getName().contains("@@");
- if (A && !B)
- return 1;
- if (!A && B)
- return -1;
- return 0;
-}
-
-// We have a new defined symbol with the specified binding. Return 1 if the new
-// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
-// strong defined symbols.
-static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
- StringRef Name) {
- if (WasInserted)
- return 1;
- if (!S->isDefined())
- return 1;
- if (int R = compareVersion(S, Name))
- return R;
- if (Binding == STB_WEAK)
- return -1;
- if (S->isWeak())
- return 1;
- return 0;
-}
-
-// We have a new non-common defined symbol with the specified binding. Return 1
-// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
-// is a conflict. If the new symbol wins, also update the binding.
-static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
- bool IsAbsolute, uint64_t Value,
- StringRef Name) {
- if (int Cmp = compareDefined(S, WasInserted, Binding, Name))
- return Cmp;
- if (auto *R = dyn_cast<Defined>(S)) {
- if (R->Section && isa<BssSection>(R->Section)) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + S->getName() + " is overridden");
- return 1;
- }
- if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
- R->Value == Value)
- return -1;
- }
- return 0;
-}
-
-Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
- uint8_t Binding, uint8_t StOther, uint8_t Type,
- InputFile &File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, getVisibility(StOther),
- /*CanOmitFromDynSym*/ false, &File);
-
- int Cmp = compareDefined(S, WasInserted, Binding, N);
- if (Cmp < 0)
- return S;
-
- if (Cmp > 0) {
- auto *Bss = make<BssSection>("COMMON", Size, Alignment);
- Bss->File = &File;
- Bss->Live = !Config->GcSections;
- InputSections.push_back(Bss);
-
- replaceSymbol<Defined>(S, &File, N, Binding, StOther, Type, 0, Size, Bss);
- return S;
- }
-
- auto *D = cast<Defined>(S);
- auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
- if (!Bss) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + S->getName() + " is overridden");
- return S;
- }
-
- if (Config->WarnCommon)
- warn("multiple common of " + D->getName());
-
- Bss->Alignment = std::max(Bss->Alignment, Alignment);
- if (Size > Bss->Size) {
- D->File = Bss->File = &File;
- D->Size = Bss->Size = Size;
- }
- return S;
-}
-
-static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
- InputSectionBase *ErrSec, uint64_t ErrOffset) {
- if (Config->AllowMultipleDefinition)
- return;
-
- Defined *D = cast<Defined>(Sym);
- if (!D->Section || !ErrSec) {
- error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
- toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
- return;
- }
-
- // Construct and print an error message in the form of:
- //
- // ld.lld: error: duplicate symbol: foo
- // >>> defined at bar.c:30
- // >>> bar.o (/home/alice/src/bar.o)
- // >>> defined at baz.c:563
- // >>> baz.o in archive libbaz.a
- auto *Sec1 = cast<InputSectionBase>(D->Section);
- std::string Src1 = Sec1->getSrcMsg(*Sym, D->Value);
- std::string Obj1 = Sec1->getObjMsg(D->Value);
- std::string Src2 = ErrSec->getSrcMsg(*Sym, ErrOffset);
- std::string Obj2 = ErrSec->getObjMsg(ErrOffset);
-
- std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
- if (!Src1.empty())
- Msg += Src1 + "\n>>> ";
- Msg += Obj1 + "\n>>> defined at ";
- if (!Src2.empty())
- Msg += Src2 + "\n>>> ";
- Msg += Obj2;
- error(Msg);
-}
-
-Defined *SymbolTable::addDefined(StringRef Name, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, uint8_t Binding,
- SectionBase *Section, InputFile *File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, getVisibility(StOther),
- /*CanOmitFromDynSym*/ false, File);
- int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, Section == nullptr,
- Value, Name);
- if (Cmp > 0)
- replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size,
- Section);
- else if (Cmp == 0)
- reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section),
- Value);
- return cast<Defined>(S);
-}
-
-template <typename ELFT>
-void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File,
- const typename ELFT::Sym &Sym, uint32_t Alignment,
- uint32_t VerdefIndex) {
- // DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
- // as the visibility, which will leave the visibility in the symbol table
- // unchanged.
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, STV_DEFAULT,
- /*CanOmitFromDynSym*/ true, &File);
- // Make sure we preempt DSO symbols with default visibility.
- if (Sym.getVisibility() == STV_DEFAULT)
- S->ExportDynamic = true;
-
- // An undefined symbol with non default visibility must be satisfied
- // in the same DSO.
- auto Replace = [&](uint8_t Binding) {
- replaceSymbol<SharedSymbol>(S, File, Name, Binding, Sym.st_other,
- Sym.getType(), Sym.st_value, Sym.st_size,
- Alignment, VerdefIndex);
- };
-
- if (WasInserted)
- Replace(Sym.getBinding());
- else if (S->Visibility == STV_DEFAULT && (S->isUndefined() || S->isLazy()))
- Replace(S->Binding);
-}
-
-Symbol *SymbolTable::addBitcode(StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym, BitcodeFile &F) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) =
- insert(Name, getVisibility(StOther), CanOmitFromDynSym, &F);
- int Cmp = compareDefinedNonCommon(S, WasInserted, Binding,
- /*IsAbs*/ false, /*Value*/ 0, Name);
- if (Cmp > 0)
- replaceSymbol<Defined>(S, &F, Name, Binding, StOther, Type, 0, 0, nullptr);
- else if (Cmp == 0)
- reportDuplicate(S, &F, nullptr, 0);
- return S;
-}
-
-Symbol *SymbolTable::find(StringRef Name) {
- auto It = SymMap.find(CachedHashStringRef(Name));
- if (It == SymMap.end())
+Symbol *SymbolTable::find(StringRef name) {
+ auto it = symMap.find(CachedHashStringRef(name));
+ if (it == symMap.end())
return nullptr;
- if (It->second == -1)
+ Symbol *sym = symVector[it->second];
+ if (sym->isPlaceholder())
return nullptr;
- return SymVector[It->second];
+ return sym;
}
-template <class ELFT>
-void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &File,
- const object::Archive::Symbol Sym) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insertName(Name);
- if (WasInserted) {
- replaceSymbol<LazyArchive>(S, File, STT_NOTYPE, Sym);
- return;
- }
- if (!S->isUndefined())
- return;
-
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
- if (S->isWeak()) {
- replaceSymbol<LazyArchive>(S, File, S->Type, Sym);
- S->Binding = STB_WEAK;
- return;
- }
-
- if (InputFile *F = File.fetch(Sym))
- addFile<ELFT>(F);
-}
-
-template <class ELFT>
-void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &File) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insertName(Name);
- if (WasInserted) {
- replaceSymbol<LazyObject>(S, File, STT_NOTYPE, Name);
- return;
- }
- if (!S->isUndefined())
- return;
-
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
- if (S->isWeak()) {
- replaceSymbol<LazyObject>(S, File, S->Type, Name);
- S->Binding = STB_WEAK;
- return;
- }
-
- if (InputFile *F = File.fetch())
- addFile<ELFT>(F);
-}
-
-template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) {
- if (auto *S = dyn_cast<LazyArchive>(Sym)) {
- if (InputFile *File = S->fetch())
- addFile<ELFT>(File);
- return;
- }
-
- auto *S = cast<LazyObject>(Sym);
- if (InputFile *File = cast<LazyObjFile>(S->File)->fetch())
- addFile<ELFT>(File);
-}
-
-// Initialize DemangledSyms with a map from demangled symbols to symbol
+// Initialize demangledSyms with a map from demangled symbols to symbol
// objects. Used to handle "extern C++" directive in version scripts.
//
// The map will contain all demangled symbols. That can be very large,
@@ -614,204 +113,156 @@ template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) {
// So, if "extern C++" feature is used, we need to demangle all known
// symbols.
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
- if (!DemangledSyms) {
- DemangledSyms.emplace();
- for (Symbol *Sym : SymVector) {
- if (!Sym->isDefined())
+ if (!demangledSyms) {
+ demangledSyms.emplace();
+ for (Symbol *sym : symVector) {
+ if (!sym->isDefined() && !sym->isCommon())
continue;
- if (Optional<std::string> S = demangleItanium(Sym->getName()))
- (*DemangledSyms)[*S].push_back(Sym);
+ if (Optional<std::string> s = demangleItanium(sym->getName()))
+ (*demangledSyms)[*s].push_back(sym);
else
- (*DemangledSyms)[Sym->getName()].push_back(Sym);
+ (*demangledSyms)[sym->getName()].push_back(sym);
}
}
- return *DemangledSyms;
+ return *demangledSyms;
}
-std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion Ver) {
- if (Ver.IsExternCpp)
- return getDemangledSyms().lookup(Ver.Name);
- if (Symbol *B = find(Ver.Name))
- if (B->isDefined())
- return {B};
+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};
return {};
}
-std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion Ver) {
- std::vector<Symbol *> Res;
- StringMatcher M(Ver.Name);
+std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
+ std::vector<Symbol *> res;
+ StringMatcher m(ver.name);
- if (Ver.IsExternCpp) {
- for (auto &P : getDemangledSyms())
- if (M.match(P.first()))
- Res.insert(Res.end(), P.second.begin(), P.second.end());
- return Res;
+ if (ver.isExternCpp) {
+ for (auto &p : getDemangledSyms())
+ if (m.match(p.first()))
+ res.insert(res.end(), p.second.begin(), p.second.end());
+ return res;
}
- for (Symbol *Sym : SymVector)
- if (Sym->isDefined() && M.match(Sym->getName()))
- Res.push_back(Sym);
- return Res;
-}
-
-// If there's only one anonymous version definition in a version
-// script file, the script does not actually define any symbol version,
-// but just specifies symbols visibilities.
-void SymbolTable::handleAnonymousVersion() {
- for (SymbolVersion &Ver : Config->VersionScriptGlobals)
- assignExactVersion(Ver, VER_NDX_GLOBAL, "global");
- for (SymbolVersion &Ver : Config->VersionScriptGlobals)
- assignWildcardVersion(Ver, VER_NDX_GLOBAL);
- for (SymbolVersion &Ver : Config->VersionScriptLocals)
- assignExactVersion(Ver, VER_NDX_LOCAL, "local");
- for (SymbolVersion &Ver : Config->VersionScriptLocals)
- assignWildcardVersion(Ver, VER_NDX_LOCAL);
+ for (Symbol *sym : symVector)
+ if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName()))
+ res.push_back(sym);
+ return res;
}
// Handles -dynamic-list.
void SymbolTable::handleDynamicList() {
- for (SymbolVersion &Ver : Config->DynamicList) {
- std::vector<Symbol *> Syms;
- if (Ver.HasWildcard)
- Syms = findAllByVersion(Ver);
+ for (SymbolVersion &ver : config->dynamicList) {
+ std::vector<Symbol *> syms;
+ if (ver.hasWildcard)
+ syms = findAllByVersion(ver);
else
- Syms = findByVersion(Ver);
+ syms = findByVersion(ver);
- for (Symbol *B : Syms) {
- if (!Config->Shared)
- B->ExportDynamic = true;
- else if (B->includeInDynsym())
- B->IsPreemptible = true;
+ for (Symbol *b : syms) {
+ if (!config->shared)
+ b->exportDynamic = true;
+ else if (b->includeInDynsym())
+ b->isPreemptible = true;
}
}
}
// Set symbol versions to symbols. This function handles patterns
// containing no wildcard characters.
-void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
- StringRef VersionName) {
- if (Ver.HasWildcard)
+void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
+ StringRef versionName) {
+ if (ver.hasWildcard)
return;
// Get a list of symbols which we need to assign the version to.
- std::vector<Symbol *> Syms = findByVersion(Ver);
- if (Syms.empty()) {
- if (!Config->UndefinedVersion)
- error("version script assignment of '" + VersionName + "' to symbol '" +
- Ver.Name + "' failed: symbol not defined");
+ std::vector<Symbol *> syms = findByVersion(ver);
+ if (syms.empty()) {
+ if (!config->undefinedVersion)
+ error("version script assignment of '" + versionName + "' to symbol '" +
+ ver.name + "' failed: symbol not defined");
return;
}
+ auto getName = [](uint16_t ver) -> std::string {
+ if (ver == VER_NDX_LOCAL)
+ return "VER_NDX_LOCAL";
+ if (ver == VER_NDX_GLOBAL)
+ return "VER_NDX_GLOBAL";
+ return ("version '" + config->versionDefinitions[ver - 2].name + "'").str();
+ };
+
// Assign the version.
- for (Symbol *Sym : Syms) {
+ for (Symbol *sym : syms) {
// Skip symbols containing version info because symbol versions
// specified by symbol names take precedence over version scripts.
// See parseSymbolVersion().
- if (Sym->getName().contains('@'))
+ if (sym->getName().contains('@'))
+ continue;
+
+ if (sym->versionId == config->defaultSymbolVersion)
+ sym->versionId = versionId;
+ if (sym->versionId == versionId)
continue;
- if (Sym->VersionId != Config->DefaultSymbolVersion &&
- Sym->VersionId != VersionId)
- error("duplicate symbol '" + Ver.Name + "' in version script");
- Sym->VersionId = VersionId;
+ warn("attempt to reassign symbol '" + ver.name + "' of " +
+ getName(sym->versionId) + " to " + getName(versionId));
}
}
-void SymbolTable::assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId) {
- if (!Ver.HasWildcard)
+void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
+ if (!ver.hasWildcard)
return;
// Exact matching takes precendence over fuzzy matching,
// so we set a version to a symbol only if no version has been assigned
// to the symbol. This behavior is compatible with GNU.
- for (Symbol *B : findAllByVersion(Ver))
- if (B->VersionId == Config->DefaultSymbolVersion)
- B->VersionId = VersionId;
+ for (Symbol *b : findAllByVersion(ver))
+ if (b->versionId == config->defaultSymbolVersion)
+ b->versionId = versionId;
}
-// This function processes version scripts by updating VersionId
+// This function processes version scripts by updating the versionId
// member of symbols.
+// If there's only one anonymous version definition in a version
+// script file, the script does not actually define any symbol version,
+// but just specifies symbols visibilities.
void SymbolTable::scanVersionScript() {
- // Handle edge cases first.
- handleAnonymousVersion();
- handleDynamicList();
-
- // Now we have version definitions, so we need to set version ids to symbols.
- // Each version definition has a glob pattern, and all symbols that match
- // with the pattern get that version.
-
// First, we assign versions to exact matching symbols,
// i.e. version definitions not containing any glob meta-characters.
- for (VersionDefinition &V : Config->VersionDefinitions)
- for (SymbolVersion &Ver : V.Globals)
- assignExactVersion(Ver, V.Id, V.Name);
+ for (SymbolVersion &ver : config->versionScriptGlobals)
+ assignExactVersion(ver, VER_NDX_GLOBAL, "global");
+ for (SymbolVersion &ver : config->versionScriptLocals)
+ assignExactVersion(ver, VER_NDX_LOCAL, "local");
+ for (VersionDefinition &v : config->versionDefinitions)
+ for (SymbolVersion &ver : v.globals)
+ assignExactVersion(ver, v.id, v.name);
// Next, we assign versions to fuzzy matching symbols,
// i.e. version definitions containing glob meta-characters.
+ for (SymbolVersion &ver : config->versionScriptGlobals)
+ assignWildcardVersion(ver, VER_NDX_GLOBAL);
+ for (SymbolVersion &ver : config->versionScriptLocals)
+ assignWildcardVersion(ver, VER_NDX_LOCAL);
+
// Note that because the last match takes precedence over previous matches,
// we iterate over the definitions in the reverse order.
- for (VersionDefinition &V : llvm::reverse(Config->VersionDefinitions))
- for (SymbolVersion &Ver : V.Globals)
- assignWildcardVersion(Ver, V.Id);
+ for (VersionDefinition &v : llvm::reverse(config->versionDefinitions))
+ for (SymbolVersion &ver : v.globals)
+ assignWildcardVersion(ver, v.id);
// Symbol themselves might know their versions because symbols
// can contain versions in the form of <name>@<version>.
// Let them parse and update their names to exclude version suffix.
- for (Symbol *Sym : SymVector)
- Sym->parseSymbolVersion();
-}
+ for (Symbol *sym : symVector)
+ sym->parseSymbolVersion();
-template void SymbolTable::addFile<ELF32LE>(InputFile *);
-template void SymbolTable::addFile<ELF32BE>(InputFile *);
-template void SymbolTable::addFile<ELF64LE>(InputFile *);
-template void SymbolTable::addFile<ELF64BE>(InputFile *);
-
-template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef, uint8_t, uint8_t,
- uint8_t, bool, InputFile *);
-
-template void SymbolTable::addCombinedLTOObject<ELF32LE>();
-template void SymbolTable::addCombinedLTOObject<ELF32BE>();
-template void SymbolTable::addCombinedLTOObject<ELF64LE>();
-template void SymbolTable::addCombinedLTOObject<ELF64BE>();
-
-template void
-SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-template void
-SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-template void
-SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-template void
-SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile &,
- const object::Archive::Symbol);
-
-template void SymbolTable::addLazyObject<ELF32LE>(StringRef, LazyObjFile &);
-template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &);
-template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &);
-template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &);
-
-template void SymbolTable::fetchLazy<ELF32LE>(Symbol *);
-template void SymbolTable::fetchLazy<ELF32BE>(Symbol *);
-template void SymbolTable::fetchLazy<ELF64LE>(Symbol *);
-template void SymbolTable::fetchLazy<ELF64BE>(Symbol *);
-
-template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> &,
- const typename ELF32LE::Sym &,
- uint32_t Alignment, uint32_t);
-template void SymbolTable::addShared<ELF32BE>(StringRef, SharedFile<ELF32BE> &,
- const typename ELF32BE::Sym &,
- uint32_t Alignment, uint32_t);
-template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> &,
- const typename ELF64LE::Sym &,
- uint32_t Alignment, uint32_t);
-template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> &,
- const typename ELF64BE::Sym &,
- uint32_t Alignment, uint32_t);
+ // isPreemptible is false at this point. To correctly compute the binding of a
+ // Defined (which is used by includeInDynsym()), we need to know if it is
+ // VER_NDX_LOCAL or not. If defaultSymbolVersion is VER_NDX_LOCAL, we should
+ // compute symbol versions before handling --dynamic-list.
+ handleDynamicList();
+}
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
index 898185fc9612..b64707f4ab02 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -1,9 +1,8 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -11,15 +10,14 @@
#define LLD_ELF_SYMBOL_TABLE_H
#include "InputFiles.h"
-#include "LTO.h"
+#include "Symbols.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
namespace lld {
namespace elf {
-class Defined;
-class SectionBase;
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
@@ -35,62 +33,40 @@ class SectionBase;
// is one add* function per symbol type.
class SymbolTable {
public:
- template <class ELFT> void addFile(InputFile *File);
- template <class ELFT> void addCombinedLTOObject();
- void wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap);
-
- ArrayRef<Symbol *> getSymbols() const { return SymVector; }
-
- template <class ELFT>
- Symbol *addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym, InputFile *File);
-
- Defined *addDefined(StringRef Name, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, uint8_t Binding,
- SectionBase *Section, InputFile *File);
-
- template <class ELFT>
- void addShared(StringRef Name, SharedFile<ELFT> &F,
- const typename ELFT::Sym &Sym, uint32_t Alignment,
- uint32_t VerdefIndex);
-
- template <class ELFT>
- void addLazyArchive(StringRef Name, ArchiveFile &F,
- const llvm::object::Archive::Symbol S);
+ void wrap(Symbol *sym, Symbol *real, Symbol *wrap);
- template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj);
+ void forEachSymbol(llvm::function_ref<void(Symbol *)> fn) {
+ for (Symbol *sym : symVector)
+ if (!sym->isPlaceholder())
+ fn(sym);
+ }
- Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym, BitcodeFile &File);
+ Symbol *insert(StringRef name);
- Symbol *addCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
- uint8_t Binding, uint8_t StOther, uint8_t Type,
- InputFile &File);
-
- std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Visibility,
- bool CanOmitFromDynSym, InputFile *File);
-
- template <class ELFT> void fetchLazy(Symbol *Sym);
+ Symbol *addSymbol(const Symbol &New);
void scanVersionScript();
- Symbol *find(StringRef Name);
-
- void trace(StringRef Name);
+ Symbol *find(StringRef name);
void handleDynamicList();
-private:
- std::pair<Symbol *, bool> insertName(StringRef Name);
+ // Set of .so files to not link the same shared object file more than once.
+ llvm::DenseMap<StringRef, SharedFile *> soNames;
- std::vector<Symbol *> findByVersion(SymbolVersion Ver);
- std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
+ // Comdat groups define "link once" sections. If two comdat groups have the
+ // same name, only one of them is linked, and the other is ignored. This map
+ // is used to uniquify them.
+ llvm::DenseMap<llvm::CachedHashStringRef, const InputFile *> comdatGroups;
+
+private:
+ std::vector<Symbol *> findByVersion(SymbolVersion ver);
+ std::vector<Symbol *> findAllByVersion(SymbolVersion ver);
llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
- void handleAnonymousVersion();
- void assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
- StringRef VersionName);
- void assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId);
+ void assignExactVersion(SymbolVersion ver, uint16_t versionId,
+ StringRef versionName);
+ void assignWildcardVersion(SymbolVersion ver, uint16_t versionId);
// The order the global symbols are in is not defined. We can use an arbitrary
// order, but it has to be reproducible. That is true even when cross linking.
@@ -99,28 +75,18 @@ private:
// but a bit inefficient.
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
// once symbol resolution is finished.
- llvm::DenseMap<llvm::CachedHashStringRef, int> SymMap;
- std::vector<Symbol *> SymVector;
-
- // Comdat groups define "link once" sections. If two comdat groups have the
- // same name, only one of them is linked, and the other is ignored. This set
- // is used to uniquify them.
- llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
-
- // Set of .so files to not link the same shared object file more than once.
- llvm::DenseMap<StringRef, InputFile *> SoNames;
+ llvm::DenseMap<llvm::CachedHashStringRef, int> symMap;
+ std::vector<Symbol *> symVector;
// A map from demangled symbol names to their symbol objects.
// This mapping is 1:N because two symbols with different versions
// can have the same name. We use this map to handle "extern C++ {}"
// directive in version scripts.
- llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> DemangledSyms;
-
- // For LTO.
- std::unique_ptr<BitcodeCompiler> LTO;
+ llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> demangledSyms;
};
-extern SymbolTable *Symtab;
+extern SymbolTable *symtab;
+
} // namespace elf
} // namespace lld
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index a713ec539d82..62c552e04828 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -1,9 +1,8 @@
//===- Symbols.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -27,40 +26,36 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-Defined *ElfSym::Bss;
-Defined *ElfSym::Etext1;
-Defined *ElfSym::Etext2;
-Defined *ElfSym::Edata1;
-Defined *ElfSym::Edata2;
-Defined *ElfSym::End1;
-Defined *ElfSym::End2;
-Defined *ElfSym::GlobalOffsetTable;
-Defined *ElfSym::MipsGp;
-Defined *ElfSym::MipsGpDisp;
-Defined *ElfSym::MipsLocalGp;
-Defined *ElfSym::RelaIpltStart;
-Defined *ElfSym::RelaIpltEnd;
-
-static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
- switch (Sym.kind()) {
+Defined *ElfSym::bss;
+Defined *ElfSym::etext1;
+Defined *ElfSym::etext2;
+Defined *ElfSym::edata1;
+Defined *ElfSym::edata2;
+Defined *ElfSym::end1;
+Defined *ElfSym::end2;
+Defined *ElfSym::globalOffsetTable;
+Defined *ElfSym::mipsGp;
+Defined *ElfSym::mipsGpDisp;
+Defined *ElfSym::mipsLocalGp;
+Defined *ElfSym::relaIpltStart;
+Defined *ElfSym::relaIpltEnd;
+Defined *ElfSym::riscvGlobalPointer;
+Defined *ElfSym::tlsModuleBase;
+
+static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
+ switch (sym.kind()) {
case Symbol::DefinedKind: {
- auto &D = cast<Defined>(Sym);
- SectionBase *IS = D.Section;
-
- // According to the ELF spec reference to a local symbol from outside
- // the group are not allowed. Unfortunately .eh_frame breaks that rule
- // and must be treated specially. For now we just replace the symbol with
- // 0.
- if (IS == &InputSection::Discarded)
- return 0;
+ auto &d = cast<Defined>(sym);
+ SectionBase *isec = d.section;
// This is an absolute symbol.
- if (!IS)
- return D.Value;
+ if (!isec)
+ return d.value;
- IS = IS->Repl;
+ assert(isec != &InputSection::discarded);
+ isec = isec->repl;
- uint64_t Offset = D.Value;
+ uint64_t offset = d.value;
// An object in an SHF_MERGE section might be referenced via a
// section symbol (as a hack for reducing the number of local
@@ -73,9 +68,9 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
// To make this work, we incorporate the addend into the section
// offset (and zero out the addend for later processing) so that
// we find the right object in the section.
- if (D.isSection()) {
- Offset += Addend;
- Addend = 0;
+ if (d.isSection()) {
+ offset += addend;
+ addend = 0;
}
// In the typical case, this is actually very simple and boils
@@ -88,83 +83,107 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
// If you understand the data structures involved with this next
// line (and how they get built), then you have a pretty good
// understanding of the linker.
- uint64_t VA = IS->getVA(Offset);
-
- if (D.isTls() && !Config->Relocatable) {
+ uint64_t va = isec->getVA(offset);
+
+ // 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
+ // 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`
+ // field etc) do the same trick as compiler uses to mark microMIPS
+ // for CPU - set the less-significant bit.
+ if (config->emachine == EM_MIPS && isMicroMips() &&
+ ((sym.stOther & STO_MIPS_MICROMIPS) || sym.needsPltAddr))
+ va |= 1;
+
+ if (d.isTls() && !config->relocatable) {
// Use the address of the TLS segment's first section rather than the
// segment's address, because segment addresses aren't initialized until
// after sections are finalized. (e.g. Measuring the size of .rela.dyn
// for Android relocation packing requires knowing TLS symbol addresses
// during section finalization.)
- if (!Out::TlsPhdr || !Out::TlsPhdr->FirstSec)
- fatal(toString(D.File) +
+ if (!Out::tlsPhdr || !Out::tlsPhdr->firstSec)
+ fatal(toString(d.file) +
" has an STT_TLS symbol but doesn't have an SHF_TLS section");
- return VA - Out::TlsPhdr->FirstSec->Addr;
+ return va - Out::tlsPhdr->firstSec->addr;
}
- return VA;
+ return va;
}
case Symbol::SharedKind:
case Symbol::UndefinedKind:
return 0;
case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
- assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
+ assert(sym.isUsedInRegularObj && "lazy symbol reached writer");
return 0;
+ case Symbol::CommonKind:
+ llvm_unreachable("common symbol reached writer");
case Symbol::PlaceholderKind:
llvm_unreachable("placeholder symbol reached writer");
}
llvm_unreachable("invalid symbol kind");
}
-uint64_t Symbol::getVA(int64_t Addend) const {
- uint64_t OutVA = getSymVA(*this, Addend);
- return OutVA + Addend;
+uint64_t Symbol::getVA(int64_t addend) const {
+ uint64_t outVA = getSymVA(*this, addend);
+ return outVA + addend;
}
-uint64_t Symbol::getGotVA() const { return In.Got->getVA() + getGotOffset(); }
-
-uint64_t Symbol::getGotOffset() const {
- return GotIndex * Target->GotEntrySize;
+uint64_t Symbol::getGotVA() const {
+ if (gotInIgot)
+ return in.igotPlt->getVA() + getGotPltOffset();
+ return in.got->getVA() + getGotOffset();
}
+uint64_t Symbol::getGotOffset() const { return gotIndex * config->wordsize; }
+
uint64_t Symbol::getGotPltVA() const {
- if (this->IsInIgot)
- return In.IgotPlt->getVA() + getGotPltOffset();
- return In.GotPlt->getVA() + getGotPltOffset();
+ if (isInIplt)
+ return in.igotPlt->getVA() + getGotPltOffset();
+ return in.gotPlt->getVA() + getGotPltOffset();
}
uint64_t Symbol::getGotPltOffset() const {
- if (IsInIgot)
- return PltIndex * Target->GotPltEntrySize;
- return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize;
+ if (isInIplt)
+ return pltIndex * config->wordsize;
+ return (pltIndex + target->gotPltHeaderEntriesNum) * config->wordsize;
}
uint64_t Symbol::getPPC64LongBranchOffset() const {
- assert(PPC64BranchltIndex != 0xffff);
- return PPC64BranchltIndex * Target->GotPltEntrySize;
+ assert(ppc64BranchltIndex != 0xffff);
+ return ppc64BranchltIndex * config->wordsize;
}
uint64_t Symbol::getPltVA() const {
- PltSection *Plt = IsInIplt ? In.Iplt : In.Plt;
- return Plt->getVA() + Plt->HeaderSize + PltIndex * Target->PltEntrySize;
+ PltSection *plt = isInIplt ? in.iplt : in.plt;
+ uint64_t outVA =
+ plt->getVA() + plt->headerSize + pltIndex * target->pltEntrySize;
+ // While linking microMIPS code PLT code are always microMIPS
+ // code. Set the less-significant bit to track that fact.
+ // See detailed comment in the `getSymVA` function.
+ if (config->emachine == EM_MIPS && isMicroMips())
+ outVA |= 1;
+ return outVA;
}
uint64_t Symbol::getPPC64LongBranchTableVA() const {
- assert(PPC64BranchltIndex != 0xffff);
- return In.PPC64LongBranchTarget->getVA() +
- PPC64BranchltIndex * Target->GotPltEntrySize;
+ assert(ppc64BranchltIndex != 0xffff);
+ return in.ppc64LongBranchTarget->getVA() +
+ ppc64BranchltIndex * config->wordsize;
}
uint64_t Symbol::getSize() const {
- if (const auto *DR = dyn_cast<Defined>(this))
- return DR->Size;
- return cast<SharedSymbol>(this)->Size;
+ if (const auto *dr = dyn_cast<Defined>(this))
+ return dr->size;
+ return cast<SharedSymbol>(this)->size;
}
OutputSection *Symbol::getOutputSection() const {
- if (auto *S = dyn_cast<Defined>(this)) {
- if (auto *Sec = S->Section)
- return Sec->Repl->getOutputSection();
+ if (auto *s = dyn_cast<Defined>(this)) {
+ if (auto *sec = s->section)
+ return sec->repl->getOutputSection();
return nullptr;
}
return nullptr;
@@ -173,16 +192,16 @@ OutputSection *Symbol::getOutputSection() const {
// If a symbol name contains '@', the characters after that is
// a symbol version name. This function parses that.
void Symbol::parseSymbolVersion() {
- StringRef S = getName();
- size_t Pos = S.find('@');
- if (Pos == 0 || Pos == StringRef::npos)
+ StringRef s = getName();
+ size_t pos = s.find('@');
+ if (pos == 0 || pos == StringRef::npos)
return;
- StringRef Verstr = S.substr(Pos + 1);
- if (Verstr.empty())
+ StringRef verstr = s.substr(pos + 1);
+ if (verstr.empty())
return;
// Truncate the symbol name so that it doesn't include the version string.
- NameSize = Pos;
+ nameSize = pos;
// If this is not in this DSO, it is not a definition.
if (!isDefined())
@@ -190,18 +209,18 @@ void Symbol::parseSymbolVersion() {
// '@@' in a symbol name means the default version.
// It is usually the most recent one.
- bool IsDefault = (Verstr[0] == '@');
- if (IsDefault)
- Verstr = Verstr.substr(1);
+ bool isDefault = (verstr[0] == '@');
+ if (isDefault)
+ verstr = verstr.substr(1);
- for (VersionDefinition &Ver : Config->VersionDefinitions) {
- if (Ver.Name != Verstr)
+ for (VersionDefinition &ver : config->versionDefinitions) {
+ if (ver.name != verstr)
continue;
- if (IsDefault)
- VersionId = Ver.Id;
+ if (isDefault)
+ versionId = ver.id;
else
- VersionId = Ver.Id | VERSYM_HIDDEN;
+ versionId = ver.id | VERSYM_HIDDEN;
return;
}
@@ -211,63 +230,79 @@ void Symbol::parseSymbolVersion() {
// so we do not report error in this case. We also do not error
// if the symbol has a local version as it won't be in the dynamic
// symbol table.
- if (Config->Shared && VersionId != VER_NDX_LOCAL)
- error(toString(File) + ": symbol " + S + " has undefined version " +
- Verstr);
+ if (config->shared && versionId != VER_NDX_LOCAL)
+ error(toString(file) + ": symbol " + s + " has undefined version " +
+ verstr);
}
-InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }
+void Symbol::fetch() const {
+ if (auto *sym = dyn_cast<LazyArchive>(this)) {
+ cast<ArchiveFile>(sym->file)->fetch(sym->sym);
+ return;
+ }
+
+ if (auto *sym = dyn_cast<LazyObject>(this)) {
+ dyn_cast<LazyObjFile>(sym->file)->fetch();
+ return;
+ }
+
+ llvm_unreachable("Symbol::fetch() is called on a non-lazy symbol");
+}
MemoryBufferRef LazyArchive::getMemberBuffer() {
- Archive::Child C = CHECK(
- Sym.getMember(), "could not get the member for symbol " + Sym.getName());
+ Archive::Child c = CHECK(
+ sym.getMember(), "could not get the member for symbol " + sym.getName());
- return CHECK(C.getMemoryBufferRef(),
+ return CHECK(c.getMemoryBufferRef(),
"could not get the buffer for the member defining symbol " +
- Sym.getName());
+ sym.getName());
}
uint8_t Symbol::computeBinding() const {
- if (Config->Relocatable)
- return Binding;
- if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
+ if (config->relocatable)
+ return binding;
+ if (visibility != STV_DEFAULT && visibility != STV_PROTECTED)
return STB_LOCAL;
- if (VersionId == VER_NDX_LOCAL && isDefined() && !IsPreemptible)
+ if (versionId == VER_NDX_LOCAL && isDefined() && !isPreemptible)
return STB_LOCAL;
- if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE)
+ if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
- return Binding;
+ return binding;
}
bool Symbol::includeInDynsym() const {
- if (!Config->HasDynSymTab)
+ if (!config->hasDynSymTab)
return false;
if (computeBinding() == STB_LOCAL)
return false;
- if (!isDefined())
- return true;
- return ExportDynamic;
+
+ // If a PIE binary was not linked against any shared libraries, then we can
+ // safely drop weak undef symbols from .dynsym.
+ if (isUndefWeak() && config->pie && sharedFiles.empty())
+ return false;
+
+ return isUndefined() || isShared() || exportDynamic;
}
// Print out a log message for --trace-symbol.
-void elf::printTraceSymbol(Symbol *Sym) {
- std::string S;
- if (Sym->isUndefined())
- S = ": reference to ";
- else if (Sym->isLazy())
- S = ": lazy definition of ";
- else if (Sym->isShared())
- S = ": shared definition of ";
- else if (dyn_cast_or_null<BssSection>(cast<Defined>(Sym)->Section))
- S = ": common definition of ";
+void elf::printTraceSymbol(const Symbol *sym) {
+ std::string s;
+ if (sym->isUndefined())
+ s = ": reference to ";
+ else if (sym->isLazy())
+ s = ": lazy definition of ";
+ else if (sym->isShared())
+ s = ": shared definition of ";
+ else if (sym->isCommon())
+ s = ": common definition of ";
else
- S = ": definition of ";
+ s = ": definition of ";
- message(toString(Sym->File) + S + Sym->getName());
+ message(toString(sym->file) + s + sym->getName());
}
-void elf::maybeWarnUnorderableSymbol(const Symbol *Sym) {
- if (!Config->WarnSymbolOrdering)
+void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
+ if (!config->warnSymbolOrdering)
return;
// If UnresolvedPolicy::Ignore is used, no "undefined symbol" error/warning
@@ -275,31 +310,347 @@ void elf::maybeWarnUnorderableSymbol(const Symbol *Sym) {
//
// Note, ld.bfd --symbol-ordering-file= does not warn on undefined symbols,
// but we don't have to be compatible here.
- if (Sym->isUndefined() &&
- Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
+ if (sym->isUndefined() &&
+ config->unresolvedSymbols == UnresolvedPolicy::Ignore)
return;
- const InputFile *File = Sym->File;
- auto *D = dyn_cast<Defined>(Sym);
-
- auto Warn = [&](StringRef S) { warn(toString(File) + S + Sym->getName()); };
-
- if (Sym->isUndefined())
- Warn(": unable to order undefined symbol: ");
- else if (Sym->isShared())
- Warn(": unable to order shared symbol: ");
- else if (D && !D->Section)
- Warn(": unable to order absolute symbol: ");
- else if (D && isa<OutputSection>(D->Section))
- Warn(": unable to order synthetic symbol: ");
- else if (D && !D->Section->Repl->Live)
- Warn(": unable to order discarded symbol: ");
+ const InputFile *file = sym->file;
+ auto *d = dyn_cast<Defined>(sym);
+
+ auto report = [&](StringRef s) { warn(toString(file) + s + sym->getName()); };
+
+ if (sym->isUndefined())
+ report(": unable to order undefined symbol: ");
+ else if (sym->isShared())
+ report(": unable to order shared symbol: ");
+ else if (d && !d->section)
+ report(": unable to order absolute symbol: ");
+ else if (d && isa<OutputSection>(d->section))
+ report(": unable to order synthetic symbol: ");
+ else if (d && !d->section->repl->isLive())
+ report(": unable to order discarded symbol: ");
}
// Returns a symbol for an error message.
-std::string lld::toString(const Symbol &B) {
- if (Config->Demangle)
- if (Optional<std::string> S = demangleItanium(B.getName()))
- return *S;
- return B.getName();
+std::string lld::toString(const Symbol &b) {
+ if (config->demangle)
+ if (Optional<std::string> s = demangleItanium(b.getName()))
+ return *s;
+ return b.getName();
+}
+
+static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
+ if (va == STV_DEFAULT)
+ return vb;
+ if (vb == STV_DEFAULT)
+ return va;
+ return std::min(va, vb);
+}
+
+// Merge symbol properties.
+//
+// When we have many symbols of the same name, we choose one of them,
+// and that's the result of symbol resolution. However, symbols that
+// were not chosen still affect some symbol properties.
+void Symbol::mergeProperties(const Symbol &other) {
+ if (other.exportDynamic)
+ exportDynamic = true;
+ if (other.isUsedInRegularObj)
+ isUsedInRegularObj = true;
+
+ // DSO symbols do not affect visibility in the output.
+ if (!other.isShared())
+ visibility = getMinVisibility(visibility, other.visibility);
+}
+
+void Symbol::resolve(const Symbol &other) {
+ mergeProperties(other);
+
+ if (isPlaceholder()) {
+ replace(other);
+ return;
+ }
+
+ switch (other.kind()) {
+ case Symbol::UndefinedKind:
+ resolveUndefined(cast<Undefined>(other));
+ break;
+ case Symbol::CommonKind:
+ resolveCommon(cast<CommonSymbol>(other));
+ break;
+ case Symbol::DefinedKind:
+ resolveDefined(cast<Defined>(other));
+ break;
+ case Symbol::LazyArchiveKind:
+ resolveLazy(cast<LazyArchive>(other));
+ break;
+ case Symbol::LazyObjectKind:
+ resolveLazy(cast<LazyObject>(other));
+ break;
+ case Symbol::SharedKind:
+ resolveShared(cast<SharedSymbol>(other));
+ break;
+ case Symbol::PlaceholderKind:
+ llvm_unreachable("bad symbol kind");
+ }
+}
+
+void Symbol::resolveUndefined(const Undefined &other) {
+ // An undefined symbol with non default visibility must be satisfied
+ // in the same DSO.
+ //
+ // If this is a non-weak defined symbol in a discarded section, override the
+ // existing undefined symbol for better error message later.
+ if ((isShared() && other.visibility != STV_DEFAULT) ||
+ (isUndefined() && other.binding != STB_WEAK && other.discardedSecIdx)) {
+ replace(other);
+ return;
+ }
+
+ if (traced)
+ printTraceSymbol(&other);
+
+ if (isLazy()) {
+ // An undefined weak will not fetch archive members. See comment on Lazy in
+ // Symbols.h for the details.
+ if (other.binding == STB_WEAK) {
+ binding = STB_WEAK;
+ type = other.type;
+ return;
+ }
+
+ // Do extra check for --warn-backrefs.
+ //
+ // --warn-backrefs is an option to prevent an undefined reference from
+ // fetching an archive member written earlier in the command line. It can be
+ // used to keep compatibility with GNU linkers to some degree.
+ // I'll explain the feature and why you may find it useful in this comment.
+ //
+ // lld's symbol resolution semantics is more relaxed than traditional Unix
+ // linkers. For example,
+ //
+ // ld.lld foo.a bar.o
+ //
+ // succeeds even if bar.o contains an undefined symbol that has to be
+ // resolved by some object file in foo.a. Traditional Unix linkers don't
+ // allow this kind of backward reference, as they visit each file only once
+ // from left to right in the command line while resolving all undefined
+ // symbols at the moment of visiting.
+ //
+ // In the above case, since there's no undefined symbol when a linker visits
+ // foo.a, no files are pulled out from foo.a, and because the linker forgets
+ // about foo.a after visiting, it can't resolve undefined symbols in bar.o
+ // that could have been resolved otherwise.
+ //
+ // That lld accepts more relaxed form means that (besides it'd make more
+ // sense) you can accidentally write a command line or a build file that
+ // works only with lld, even if you have a plan to distribute it to wider
+ // users who may be using GNU linkers. With --warn-backrefs, you can detect
+ // a library order that doesn't work with other Unix linkers.
+ //
+ // The option is also useful to detect cyclic dependencies between static
+ // archives. Again, lld accepts
+ //
+ // ld.lld foo.a bar.a
+ //
+ // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is
+ // handled as an error.
+ //
+ // Here is how the option works. We assign a group ID to each file. A file
+ // with a smaller group ID can pull out object files from an archive file
+ // with an equal or greater group ID. Otherwise, it is a reverse dependency
+ // and an error.
+ //
+ // A file outside --{start,end}-group gets a fresh ID when instantiated. All
+ // files within the same --{start,end}-group get the same group ID. E.g.
+ //
+ // ld.lld A B --start-group C D --end-group E
+ //
+ // A forms group 0. B form group 1. C and D (including their member object
+ // files) form group 2. E forms group 3. I think that you can see how this
+ // group assignment rule simulates the traditional linker's semantics.
+ bool backref = config->warnBackrefs && other.file &&
+ file->groupId < other.file->groupId;
+ fetch();
+
+ // We don't report backward references to weak symbols as they can be
+ // overridden later.
+ if (backref && !isWeak())
+ warn("backward reference detected: " + other.getName() + " in " +
+ toString(other.file) + " refers to " + toString(file));
+ return;
+ }
+
+ // Undefined symbols in a SharedFile do not change the binding.
+ if (dyn_cast_or_null<SharedFile>(other.file))
+ return;
+
+ if (isUndefined()) {
+ // The binding may "upgrade" from weak to non-weak.
+ if (other.binding != STB_WEAK)
+ binding = other.binding;
+ } else if (auto *s = dyn_cast<SharedSymbol>(this)) {
+ // The binding of a SharedSymbol will be weak if there is at least one
+ // reference and all are weak. The binding has one opportunity to change to
+ // weak: if the first reference is weak.
+ if (other.binding != STB_WEAK || !s->referenced)
+ binding = other.binding;
+ s->referenced = true;
+ }
+}
+
+// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
+// foo@@VER. We want to effectively ignore foo, so give precedence to
+// foo@@VER.
+// FIXME: If users can transition to using
+// .symver foo,foo@@@VER
+// we can delete this hack.
+static int compareVersion(StringRef a, StringRef b) {
+ bool x = a.contains("@@");
+ bool y = b.contains("@@");
+ if (!x && y)
+ return 1;
+ if (x && !y)
+ return -1;
+ return 0;
+}
+
+// Compare two symbols. Return 1 if the new symbol should win, -1 if
+// the new symbol should lose, or 0 if there is a conflict.
+int Symbol::compare(const Symbol *other) const {
+ assert(other->isDefined() || other->isCommon());
+
+ if (!isDefined() && !isCommon())
+ return 1;
+
+ if (int cmp = compareVersion(getName(), other->getName()))
+ return cmp;
+
+ if (other->isWeak())
+ return -1;
+
+ if (isWeak())
+ return 1;
+
+ if (isCommon() && other->isCommon()) {
+ if (config->warnCommon)
+ warn("multiple common of " + getName());
+ return 0;
+ }
+
+ if (isCommon()) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
+ return 1;
+ }
+
+ if (other->isCommon()) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
+ return -1;
+ }
+
+ auto *oldSym = cast<Defined>(this);
+ auto *newSym = cast<Defined>(other);
+
+ if (other->file && isa<BitcodeFile>(other->file))
+ return 0;
+
+ if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
+ newSym->binding == STB_GLOBAL)
+ return -1;
+
+ return 0;
+}
+
+static void reportDuplicate(Symbol *sym, InputFile *newFile,
+ InputSectionBase *errSec, uint64_t errOffset) {
+ if (config->allowMultipleDefinition)
+ return;
+
+ Defined *d = cast<Defined>(sym);
+ if (!d->section || !errSec) {
+ error("duplicate symbol: " + toString(*sym) + "\n>>> defined in " +
+ toString(sym->file) + "\n>>> defined in " + toString(newFile));
+ return;
+ }
+
+ // Construct and print an error message in the form of:
+ //
+ // ld.lld: error: duplicate symbol: foo
+ // >>> defined at bar.c:30
+ // >>> bar.o (/home/alice/src/bar.o)
+ // >>> defined at baz.c:563
+ // >>> baz.o in archive libbaz.a
+ auto *sec1 = cast<InputSectionBase>(d->section);
+ std::string src1 = sec1->getSrcMsg(*sym, d->value);
+ std::string obj1 = sec1->getObjMsg(d->value);
+ std::string src2 = errSec->getSrcMsg(*sym, errOffset);
+ std::string obj2 = errSec->getObjMsg(errOffset);
+
+ std::string msg = "duplicate symbol: " + toString(*sym) + "\n>>> defined at ";
+ if (!src1.empty())
+ msg += src1 + "\n>>> ";
+ msg += obj1 + "\n>>> defined at ";
+ if (!src2.empty())
+ msg += src2 + "\n>>> ";
+ msg += obj2;
+ error(msg);
+}
+
+void Symbol::resolveCommon(const CommonSymbol &other) {
+ int cmp = compare(&other);
+ if (cmp < 0)
+ return;
+
+ if (cmp > 0) {
+ replace(other);
+ return;
+ }
+
+ CommonSymbol *oldSym = cast<CommonSymbol>(this);
+
+ oldSym->alignment = std::max(oldSym->alignment, other.alignment);
+ if (oldSym->size < other.size) {
+ oldSym->file = other.file;
+ oldSym->size = other.size;
+ }
+}
+
+void Symbol::resolveDefined(const Defined &other) {
+ int cmp = compare(&other);
+ if (cmp > 0)
+ replace(other);
+ else if (cmp == 0)
+ reportDuplicate(this, other.file,
+ dyn_cast_or_null<InputSectionBase>(other.section),
+ other.value);
+}
+
+template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
+ if (!isUndefined())
+ return;
+
+ // An undefined weak will not fetch archive members. See comment on Lazy in
+ // Symbols.h for the details.
+ if (isWeak()) {
+ uint8_t ty = type;
+ replace(other);
+ type = ty;
+ binding = STB_WEAK;
+ return;
+ }
+
+ other.fetch();
+}
+
+void Symbol::resolveShared(const SharedSymbol &other) {
+ if (visibility == STV_DEFAULT && (isUndefined() || isLazy())) {
+ // An undefined symbol with non default visibility must be satisfied
+ // in the same DSO.
+ uint8_t bind = binding;
+ replace(other);
+ binding = bind;
+ cast<SharedSymbol>(this)->referenced = true;
+ }
}
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 4d55405d8936..d640495b0e01 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -1,9 +1,8 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +13,7 @@
#ifndef LLD_ELF_SYMBOLS_H
#define LLD_ELF_SYMBOLS_H
+#include "InputFiles.h"
#include "InputSection.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Strings.h"
@@ -22,8 +22,14 @@
namespace lld {
namespace elf {
-class Symbol;
+class CommonSymbol;
+class Defined;
class InputFile;
+class LazyArchive;
+class LazyObject;
+class SharedSymbol;
+class Symbol;
+class Undefined;
} // namespace elf
std::string toString(const elf::Symbol &);
@@ -31,15 +37,6 @@ std::string toString(const elf::InputFile *);
namespace elf {
-class ArchiveFile;
-class BitcodeFile;
-class BssSection;
-class InputFile;
-class LazyObjFile;
-template <class ELFT> class ObjFile;
-class OutputSection;
-template <class ELFT> class SharedFile;
-
// This is a StringRef-like container that doesn't run strlen().
//
// ELF string tables contain a lot of null-terminated strings. Most of them
@@ -47,11 +44,11 @@ template <class ELFT> class SharedFile;
// and the linker doesn't use local symbol names for name resolution. So, we
// use this class to represents strings read from string tables.
struct StringRefZ {
- StringRefZ(const char *S) : Data(S), Size(-1) {}
- StringRefZ(StringRef S) : Data(S.data()), Size(S.size()) {}
+ StringRefZ(const char *s) : data(s), size(-1) {}
+ StringRefZ(StringRef s) : data(s.data()), size(s.size()) {}
- const char *Data;
- const uint32_t Size;
+ const char *data;
+ const uint32_t size;
};
// The base class for real symbol classes.
@@ -60,85 +57,91 @@ public:
enum Kind {
PlaceholderKind,
DefinedKind,
+ CommonKind,
SharedKind,
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
};
- Kind kind() const { return static_cast<Kind>(SymbolKind); }
+ Kind kind() const { return static_cast<Kind>(symbolKind); }
// The file from which this symbol was created.
- InputFile *File;
+ InputFile *file;
protected:
- const char *NameData;
- mutable uint32_t NameSize;
+ const char *nameData;
+ mutable uint32_t nameSize;
public:
- uint32_t DynsymIndex = 0;
- uint32_t GotIndex = -1;
- uint32_t PltIndex = -1;
+ uint32_t dynsymIndex = 0;
+ uint32_t gotIndex = -1;
+ uint32_t pltIndex = -1;
- uint32_t GlobalDynIndex = -1;
+ uint32_t globalDynIndex = -1;
// This field is a index to the symbol's version definition.
- uint32_t VerdefIndex = -1;
+ uint32_t verdefIndex = -1;
// Version definition index.
- uint16_t VersionId;
+ uint16_t versionId;
// An index into the .branch_lt section on PPC64.
- uint16_t PPC64BranchltIndex = -1;
+ uint16_t ppc64BranchltIndex = -1;
- // Symbol binding. This is not overwritten by replaceSymbol to track
+ // Symbol binding. This is not overwritten by replace() to track
// changes during resolution. In particular:
// - An undefined weak is still weak when it resolves to a shared library.
// - An undefined weak will not fetch archive members, but we have to
// remember it is weak.
- uint8_t Binding;
+ uint8_t binding;
// The following fields have the same meaning as the ELF symbol attributes.
- uint8_t Type; // symbol type
- uint8_t StOther; // st_other field value
+ uint8_t type; // symbol type
+ uint8_t stOther; // st_other field value
- uint8_t SymbolKind;
+ uint8_t symbolKind;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
- unsigned Visibility : 2;
+ unsigned visibility : 2;
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
- // unreferenced DSO symbols and bitcode symbols that are unreferenced except
- // by other bitcode objects.
- unsigned IsUsedInRegularObj : 1;
+ // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
+ // are unreferenced except by other bitcode objects.
+ unsigned isUsedInRegularObj : 1;
// If this flag is true and the symbol has protected or default visibility, it
// will appear in .dynsym. This flag is set by interposable DSO symbols in
// executables, by most symbols in DSOs and executables built with
// --export-dynamic, and by dynamic lists.
- unsigned ExportDynamic : 1;
+ unsigned exportDynamic : 1;
// False if LTO shouldn't inline whatever this symbol points to. If a symbol
// is overwritten after LTO, LTO shouldn't inline the symbol because it
// doesn't know the final contents of the symbol.
- unsigned CanInline : 1;
+ unsigned canInline : 1;
// True if this symbol is specified by --trace-symbol option.
- unsigned Traced : 1;
+ unsigned traced : 1;
+
+ inline void replace(const Symbol &New);
bool includeInDynsym() const;
uint8_t computeBinding() const;
- bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
+ bool isWeak() const { return binding == llvm::ELF::STB_WEAK; }
+
+ bool isUndefined() const { return symbolKind == UndefinedKind; }
+ bool isCommon() const { return symbolKind == CommonKind; }
+ bool isDefined() const { return symbolKind == DefinedKind; }
+ bool isShared() const { return symbolKind == SharedKind; }
+ bool isPlaceholder() const { return symbolKind == PlaceholderKind; }
- bool isUndefined() const { return SymbolKind == UndefinedKind; }
- bool isDefined() const { return SymbolKind == DefinedKind; }
- bool isShared() const { return SymbolKind == SharedKind; }
- bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
+ bool isLocal() const { return binding == llvm::ELF::STB_LOCAL; }
bool isLazy() const {
- return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
+ return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
// True if this is an undefined weak symbol. This only works once
@@ -149,23 +152,23 @@ public:
}
StringRef getName() const {
- if (NameSize == (uint32_t)-1)
- NameSize = strlen(NameData);
- return {NameData, NameSize};
+ if (nameSize == (uint32_t)-1)
+ nameSize = strlen(nameData);
+ return {nameData, nameSize};
}
- void setName(StringRef S) {
- NameData = S.data();
- NameSize = S.size();
+ void setName(StringRef s) {
+ nameData = s.data();
+ nameSize = s.size();
}
void parseSymbolVersion();
- bool isInGot() const { return GotIndex != -1U; }
- bool isInPlt() const { return PltIndex != -1U; }
- bool isInPPC64Branchlt() const { return PPC64BranchltIndex != 0xffff; }
+ bool isInGot() const { return gotIndex != -1U; }
+ bool isInPlt() const { return pltIndex != -1U; }
+ bool isInPPC64Branchlt() const { return ppc64BranchltIndex != 0xffff; }
- uint64_t getVA(int64_t Addend = 0) const;
+ uint64_t getVA(int64_t addend = 0) const;
uint64_t getGotOffset() const;
uint64_t getGotVA() const;
@@ -177,81 +180,164 @@ public:
uint64_t getSize() const;
OutputSection *getOutputSection() const;
+ // The following two functions are used for symbol resolution.
+ //
+ // You are expected to call mergeProperties for all symbols in input
+ // files so that attributes that are attached to names rather than
+ // indivisual symbol (such as visibility) are merged together.
+ //
+ // Every time you read a new symbol from an input, you are supposed
+ // to call resolve() with the new symbol. That function replaces
+ // "this" object as a result of name resolution if the new symbol is
+ // more appropriate to be included in the output.
+ //
+ // For example, if "this" is an undefined symbol and a new symbol is
+ // a defined symbol, "this" is replaced with the new symbol.
+ void mergeProperties(const Symbol &other);
+ void resolve(const Symbol &other);
+
+ // If this is a lazy symbol, fetch an input file and add the symbol
+ // in the file to the symbol table. Calling this function on
+ // non-lazy object causes a runtime error.
+ void fetch() const;
+
+private:
+ static bool isExportDynamic(Kind k, uint8_t visibility) {
+ if (k == SharedKind)
+ return visibility == llvm::ELF::STV_DEFAULT;
+ return config->shared || config->exportDynamic;
+ }
+
+ void resolveUndefined(const Undefined &other);
+ void resolveCommon(const CommonSymbol &other);
+ void resolveDefined(const Defined &other);
+ template <class LazyT> void resolveLazy(const LazyT &other);
+ void resolveShared(const SharedSymbol &other);
+
+ int compare(const Symbol *other) const;
+
+ inline size_t getSymbolSize() const;
+
protected:
- Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type)
- : File(File), NameData(Name.Data), NameSize(Name.Size), Binding(Binding),
- Type(Type), StOther(StOther), SymbolKind(K), NeedsPltAddr(false),
- IsInIplt(false), IsInIgot(false), IsPreemptible(false),
- Used(!Config->GcSections), NeedsTocRestore(false),
- ScriptDefined(false) {}
+ Symbol(Kind k, InputFile *file, StringRefZ name, uint8_t binding,
+ uint8_t stOther, uint8_t type)
+ : file(file), nameData(name.data), nameSize(name.size), binding(binding),
+ type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
+ isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
+ exportDynamic(isExportDynamic(k, visibility)), canInline(false),
+ traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false),
+ isPreemptible(false), used(!config->gcSections), needsTocRestore(false),
+ scriptDefined(false) {}
public:
// True the symbol should point to its PLT entry.
// For SharedSymbol only.
- unsigned NeedsPltAddr : 1;
+ unsigned needsPltAddr : 1;
- // True if this symbol is in the Iplt sub-section of the Plt.
- unsigned IsInIplt : 1;
+ // True if this symbol is in the Iplt sub-section of the Plt and the Igot
+ // sub-section of the .got.plt or .got.
+ unsigned isInIplt : 1;
- // True if this symbol is in the Igot sub-section of the .got.plt or .got.
- unsigned IsInIgot : 1;
+ // True if this symbol needs a GOT entry and its GOT entry is actually in
+ // Igot. This will be true only for certain non-preemptible ifuncs.
+ unsigned gotInIgot : 1;
// True if this symbol is preemptible at load time.
- unsigned IsPreemptible : 1;
+ unsigned isPreemptible : 1;
// True if an undefined or shared symbol is used from a live section.
- unsigned Used : 1;
+ unsigned used : 1;
// True if a call to this symbol needs to be followed by a restore of the
// PPC64 toc pointer.
- unsigned NeedsTocRestore : 1;
+ unsigned needsTocRestore : 1;
// True if this symbol is defined by a linker script.
- unsigned ScriptDefined : 1;
-
- bool isSection() const { return Type == llvm::ELF::STT_SECTION; }
- bool isTls() const { return Type == llvm::ELF::STT_TLS; }
- bool isFunc() const { return Type == llvm::ELF::STT_FUNC; }
- bool isGnuIFunc() const { return Type == llvm::ELF::STT_GNU_IFUNC; }
- bool isObject() const { return Type == llvm::ELF::STT_OBJECT; }
- bool isFile() const { return Type == llvm::ELF::STT_FILE; }
+ unsigned scriptDefined : 1;
+
+ // The partition whose dynamic symbol table contains this symbol's definition.
+ uint8_t partition = 1;
+
+ bool isSection() const { return type == llvm::ELF::STT_SECTION; }
+ bool isTls() const { return type == llvm::ELF::STT_TLS; }
+ bool isFunc() const { return type == llvm::ELF::STT_FUNC; }
+ bool isGnuIFunc() const { return type == llvm::ELF::STT_GNU_IFUNC; }
+ bool isObject() const { return type == llvm::ELF::STT_OBJECT; }
+ bool isFile() const { return type == llvm::ELF::STT_FILE; }
};
// Represents a symbol that is defined in the current output file.
class Defined : public Symbol {
public:
- Defined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, uint64_t Value, uint64_t Size, SectionBase *Section)
- : Symbol(DefinedKind, File, Name, Binding, StOther, Type), Value(Value),
- Size(Size), Section(Section) {}
+ Defined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther,
+ uint8_t type, uint64_t value, uint64_t size, SectionBase *section)
+ : Symbol(DefinedKind, file, name, binding, stOther, type), value(value),
+ size(size), section(section) {}
+
+ static bool classof(const Symbol *s) { return s->isDefined(); }
+
+ uint64_t value;
+ uint64_t size;
+ SectionBase *section;
+};
+
+// Represents a common symbol.
+//
+// On Unix, it is traditionally allowed to write variable definitions
+// without initialization expressions (such as "int foo;") to header
+// files. Such definition is called "tentative definition".
+//
+// Using tentative definition is usually considered a bad practice
+// because you should write only declarations (such as "extern int
+// foo;") to header files. Nevertheless, the linker and the compiler
+// have to do something to support bad code by allowing duplicate
+// definitions for this particular case.
+//
+// Common symbols represent variable definitions without initializations.
+// The compiler creates common symbols when it sees varaible definitions
+// without initialization (you can suppress this behavior and let the
+// compiler create a regular defined symbol by -fno-common).
+//
+// The linker allows common symbols to be replaced by regular defined
+// symbols. If there are remaining common symbols after name resolution is
+// complete, they are converted to regular defined symbols in a .bss
+// section. (Therefore, the later passes don't see any CommonSymbols.)
+class CommonSymbol : public Symbol {
+public:
+ CommonSymbol(InputFile *file, StringRefZ name, uint8_t binding,
+ uint8_t stOther, uint8_t type, uint64_t alignment, uint64_t size)
+ : Symbol(CommonKind, file, name, binding, stOther, type),
+ alignment(alignment), size(size) {}
- static bool classof(const Symbol *S) { return S->isDefined(); }
+ static bool classof(const Symbol *s) { return s->isCommon(); }
- uint64_t Value;
- uint64_t Size;
- SectionBase *Section;
+ uint32_t alignment;
+ uint64_t size;
};
class Undefined : public Symbol {
public:
- Undefined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type)
- : Symbol(UndefinedKind, File, Name, Binding, StOther, Type) {}
+ Undefined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther,
+ uint8_t type, uint32_t discardedSecIdx = 0)
+ : Symbol(UndefinedKind, file, name, binding, stOther, type),
+ discardedSecIdx(discardedSecIdx) {}
- static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
+ static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
+
+ // The section index if in a discarded section, 0 otherwise.
+ uint32_t discardedSecIdx;
};
class SharedSymbol : public Symbol {
public:
- static bool classof(const Symbol *S) { return S->kind() == SharedKind; }
-
- SharedSymbol(InputFile &File, StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
- uint32_t Alignment, uint32_t VerdefIndex)
- : Symbol(SharedKind, &File, Name, Binding, StOther, Type),
- Alignment(Alignment), Value(Value), Size(Size) {
- this->VerdefIndex = VerdefIndex;
+ static bool classof(const Symbol *s) { return s->kind() == SharedKind; }
+
+ SharedSymbol(InputFile &file, StringRef name, uint8_t binding,
+ uint8_t stOther, uint8_t type, uint64_t value, uint64_t size,
+ uint32_t alignment, uint32_t verdefIndex)
+ : Symbol(SharedKind, &file, name, binding, stOther, type), value(value),
+ size(size), alignment(alignment) {
+ this->verdefIndex = verdefIndex;
// GNU ifunc is a mechanism to allow user-supplied functions to
// resolve PLT slot values at load-time. This is contrary to the
// regular symbol resolution scheme in which symbols are resolved just
@@ -268,18 +354,20 @@ public:
// For DSO symbols, we always call them through PLT slots anyway.
// So there's no difference between GNU ifunc and regular function
// symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC.
- if (this->Type == llvm::ELF::STT_GNU_IFUNC)
- this->Type = llvm::ELF::STT_FUNC;
+ if (this->type == llvm::ELF::STT_GNU_IFUNC)
+ this->type = llvm::ELF::STT_FUNC;
}
- template <class ELFT> SharedFile<ELFT> &getFile() const {
- return *cast<SharedFile<ELFT>>(File);
- }
+ SharedFile &getFile() const { return *cast<SharedFile>(file); }
- uint32_t Alignment;
+ uint64_t value; // st_value
+ uint64_t size; // st_size
+ uint32_t alignment;
- uint64_t Value; // st_value
- uint64_t Size; // st_size
+ // This is true if there has been at least one undefined reference to the
+ // symbol. The binding may change to STB_WEAK if the first undefined reference
+ // is weak.
+ bool referenced = false;
};
// LazyArchive and LazyObject represent a symbols that is not yet in the link,
@@ -298,121 +386,168 @@ public:
// symbol.
class LazyArchive : public Symbol {
public:
- LazyArchive(InputFile &File, uint8_t Type,
- const llvm::object::Archive::Symbol S)
- : Symbol(LazyArchiveKind, &File, S.getName(), llvm::ELF::STB_GLOBAL,
- llvm::ELF::STV_DEFAULT, Type),
- Sym(S) {}
+ LazyArchive(InputFile &file, const llvm::object::Archive::Symbol s)
+ : Symbol(LazyArchiveKind, &file, s.getName(), llvm::ELF::STB_GLOBAL,
+ llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE),
+ sym(s) {}
- static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
+ static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
- InputFile *fetch();
MemoryBufferRef getMemberBuffer();
-private:
- const llvm::object::Archive::Symbol Sym;
+ const llvm::object::Archive::Symbol sym;
};
// LazyObject symbols represents symbols in object files between
// --start-lib and --end-lib options.
class LazyObject : public Symbol {
public:
- LazyObject(InputFile &File, uint8_t Type, StringRef Name)
- : Symbol(LazyObjectKind, &File, Name, llvm::ELF::STB_GLOBAL,
- llvm::ELF::STV_DEFAULT, Type) {}
+ LazyObject(InputFile &file, StringRef name)
+ : Symbol(LazyObjectKind, &file, name, llvm::ELF::STB_GLOBAL,
+ llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE) {}
- static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
+ static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
};
// Some linker-generated symbols need to be created as
// Defined symbols.
struct ElfSym {
// __bss_start
- static Defined *Bss;
+ static Defined *bss;
// etext and _etext
- static Defined *Etext1;
- static Defined *Etext2;
+ static Defined *etext1;
+ static Defined *etext2;
// edata and _edata
- static Defined *Edata1;
- static Defined *Edata2;
+ static Defined *edata1;
+ static Defined *edata2;
// end and _end
- static Defined *End1;
- static Defined *End2;
+ static Defined *end1;
+ static Defined *end2;
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
// be at some offset from the base of the .got section, usually 0 or
// the end of the .got.
- static Defined *GlobalOffsetTable;
+ static Defined *globalOffsetTable;
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
- static Defined *MipsGp;
- static Defined *MipsGpDisp;
- static Defined *MipsLocalGp;
+ static Defined *mipsGp;
+ static Defined *mipsGpDisp;
+ static Defined *mipsLocalGp;
// __rel{,a}_iplt_{start,end} symbols.
- static Defined *RelaIpltStart;
- static Defined *RelaIpltEnd;
+ static Defined *relaIpltStart;
+ static Defined *relaIpltEnd;
+
+ // __global_pointer$ for RISC-V.
+ static Defined *riscvGlobalPointer;
+
+ // _TLS_MODULE_BASE_ on targets that support TLSDESC.
+ static Defined *tlsModuleBase;
};
// A buffer class that is large enough to hold any Symbol-derived
// object. We allocate memory using this class and instantiate a symbol
// using the placement new.
union SymbolUnion {
- alignas(Defined) char A[sizeof(Defined)];
- alignas(Undefined) char C[sizeof(Undefined)];
- alignas(SharedSymbol) char D[sizeof(SharedSymbol)];
- alignas(LazyArchive) char E[sizeof(LazyArchive)];
- alignas(LazyObject) char F[sizeof(LazyObject)];
+ alignas(Defined) char a[sizeof(Defined)];
+ alignas(CommonSymbol) char b[sizeof(CommonSymbol)];
+ alignas(Undefined) char c[sizeof(Undefined)];
+ alignas(SharedSymbol) char d[sizeof(SharedSymbol)];
+ alignas(LazyArchive) char e[sizeof(LazyArchive)];
+ alignas(LazyObject) char f[sizeof(LazyObject)];
};
-void printTraceSymbol(Symbol *Sym);
-
-template <typename T, typename... ArgT>
-void replaceSymbol(Symbol *S, ArgT &&... Arg) {
- using llvm::ELF::STT_TLS;
+// It is important to keep the size of SymbolUnion small for performance and
+// memory usage reasons. 80 bytes is a soft limit based on the size of Defined
+// on a 64-bit system.
+static_assert(sizeof(SymbolUnion) <= 80, "SymbolUnion too large");
+template <typename T> struct AssertSymbol {
static_assert(std::is_trivially_destructible<T>(),
"Symbol types must be trivially destructible");
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");
+};
- Symbol Sym = *S;
+static inline void assertSymbols() {
+ AssertSymbol<Defined>();
+ AssertSymbol<CommonSymbol>();
+ AssertSymbol<Undefined>();
+ AssertSymbol<SharedSymbol>();
+ AssertSymbol<LazyArchive>();
+ AssertSymbol<LazyObject>();
+}
- new (S) T(std::forward<ArgT>(Arg)...);
+void printTraceSymbol(const Symbol *sym);
+
+size_t Symbol::getSymbolSize() const {
+ switch (kind()) {
+ case CommonKind:
+ return sizeof(CommonSymbol);
+ case DefinedKind:
+ return sizeof(Defined);
+ case LazyArchiveKind:
+ return sizeof(LazyArchive);
+ case LazyObjectKind:
+ return sizeof(LazyObject);
+ case SharedKind:
+ return sizeof(SharedSymbol);
+ case UndefinedKind:
+ return sizeof(Undefined);
+ case PlaceholderKind:
+ return sizeof(Symbol);
+ }
+ llvm_unreachable("unknown symbol kind");
+}
- S->VersionId = Sym.VersionId;
- S->Visibility = Sym.Visibility;
- S->IsUsedInRegularObj = Sym.IsUsedInRegularObj;
- S->ExportDynamic = Sym.ExportDynamic;
- S->CanInline = Sym.CanInline;
- S->Traced = Sym.Traced;
- S->ScriptDefined = Sym.ScriptDefined;
+// replace() replaces "this" object with a given symbol by memcpy'ing
+// it over to "this". This function is called as a result of name
+// resolution, e.g. to replace an undefind symbol with a defined symbol.
+void Symbol::replace(const Symbol &New) {
+ 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.
- bool TlsMismatch = (Sym.Type == STT_TLS && S->Type != STT_TLS) ||
- (Sym.Type != STT_TLS && S->Type == STT_TLS);
+ if (symbolKind != PlaceholderKind && !isLazy() && !New.isLazy()) {
+ bool tlsMismatch = (type == STT_TLS && New.type != STT_TLS) ||
+ (type != STT_TLS && New.type == STT_TLS);
+ if (tlsMismatch)
+ error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
+ toString(New.file) + "\n>>> defined in " + toString(file));
+ }
+
+ Symbol old = *this;
+ memcpy(this, &New, New.getSymbolSize());
+
+ versionId = old.versionId;
+ visibility = old.visibility;
+ isUsedInRegularObj = old.isUsedInRegularObj;
+ exportDynamic = old.exportDynamic;
+ canInline = old.canInline;
+ traced = old.traced;
+ isPreemptible = old.isPreemptible;
+ scriptDefined = old.scriptDefined;
+ partition = old.partition;
- if (Sym.SymbolKind != Symbol::PlaceholderKind && TlsMismatch && !Sym.isLazy())
- error("TLS attribute mismatch: " + toString(Sym) + "\n>>> defined in " +
- toString(Sym.File) + "\n>>> defined in " + toString(S->File));
+ // Symbol length is computed lazily. If we already know a symbol length,
+ // propagate it.
+ if (nameData == old.nameData && nameSize == 0 && old.nameSize != 0)
+ nameSize = old.nameSize;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
- if (S->Traced)
- printTraceSymbol(S);
+ if (traced)
+ printTraceSymbol(this);
}
-void maybeWarnUnorderableSymbol(const Symbol *Sym);
+void maybeWarnUnorderableSymbol(const Symbol *sym);
} // namespace elf
} // namespace lld
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index f459c1b6b479..f6d0f190d84d 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -1,9 +1,8 @@
//===- SyntheticSections.cpp ----------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -15,7 +14,6 @@
//===----------------------------------------------------------------------===//
#include "SyntheticSections.h"
-#include "Bits.h"
#include "Config.h"
#include "InputFiles.h"
#include "LinkerScript.h"
@@ -38,9 +36,6 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MD5.h"
-#include "llvm/Support/RandomNumberGenerator.h"
-#include "llvm/Support/SHA1.h"
-#include "llvm/Support/xxhash.h"
#include <cstdlib>
#include <thread>
@@ -57,19 +52,30 @@ using llvm::support::endian::read32le;
using llvm::support::endian::write32le;
using llvm::support::endian::write64le;
-constexpr size_t MergeNoTailSection::NumShards;
+constexpr size_t MergeNoTailSection::numShards;
+
+static uint64_t readUint(uint8_t *buf) {
+ return config->is64 ? read64(buf) : read32(buf);
+}
+
+static void writeUint(uint8_t *buf, uint64_t val) {
+ if (config->is64)
+ write64(buf, val);
+ else
+ write32(buf, val);
+}
// Returns an LLD version string.
static ArrayRef<uint8_t> getVersion() {
// Check LLD_VERSION first for ease of testing.
// You can get consistent output by using the environment variable.
// This is only for testing.
- StringRef S = getenv("LLD_VERSION");
- if (S.empty())
- S = Saver.save(Twine("Linker: ") + getLLDVersion());
+ StringRef s = getenv("LLD_VERSION");
+ if (s.empty())
+ s = saver.save(Twine("Linker: ") + getLLDVersion());
// +1 to include the terminating '\0'.
- return {(const uint8_t *)S.data(), S.size() + 1};
+ return {(const uint8_t *)s.data(), s.size() + 1};
}
// Creates a .comment section containing LLD version info.
@@ -83,79 +89,79 @@ MergeInputSection *elf::createCommentSection() {
// .MIPS.abiflags section.
template <class ELFT>
-MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags)
+MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection(Elf_Mips_ABIFlags flags)
: SyntheticSection(SHF_ALLOC, SHT_MIPS_ABIFLAGS, 8, ".MIPS.abiflags"),
- Flags(Flags) {
- this->Entsize = sizeof(Elf_Mips_ABIFlags);
+ flags(flags) {
+ this->entsize = sizeof(Elf_Mips_ABIFlags);
}
-template <class ELFT> void MipsAbiFlagsSection<ELFT>::writeTo(uint8_t *Buf) {
- memcpy(Buf, &Flags, sizeof(Flags));
+template <class ELFT> void MipsAbiFlagsSection<ELFT>::writeTo(uint8_t *buf) {
+ memcpy(buf, &flags, sizeof(flags));
}
template <class ELFT>
MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
- Elf_Mips_ABIFlags Flags = {};
- bool Create = false;
+ Elf_Mips_ABIFlags flags = {};
+ bool create = false;
- for (InputSectionBase *Sec : InputSections) {
- if (Sec->Type != SHT_MIPS_ABIFLAGS)
+ for (InputSectionBase *sec : inputSections) {
+ if (sec->type != SHT_MIPS_ABIFLAGS)
continue;
- Sec->Live = false;
- Create = true;
+ sec->markDead();
+ create = true;
- std::string Filename = toString(Sec->File);
- const size_t Size = Sec->data().size();
+ std::string filename = toString(sec->file);
+ const size_t size = sec->data().size();
// Older version of BFD (such as the default FreeBSD linker) concatenate
// .MIPS.abiflags instead of merging. To allow for this case (or potential
// zero padding) we ignore everything after the first Elf_Mips_ABIFlags
- if (Size < sizeof(Elf_Mips_ABIFlags)) {
- error(Filename + ": invalid size of .MIPS.abiflags section: got " +
- Twine(Size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
+ if (size < sizeof(Elf_Mips_ABIFlags)) {
+ error(filename + ": invalid size of .MIPS.abiflags section: got " +
+ Twine(size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
return nullptr;
}
- auto *S = reinterpret_cast<const Elf_Mips_ABIFlags *>(Sec->data().data());
- if (S->version != 0) {
- error(Filename + ": unexpected .MIPS.abiflags version " +
- Twine(S->version));
+ auto *s = reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->data().data());
+ if (s->version != 0) {
+ error(filename + ": unexpected .MIPS.abiflags version " +
+ Twine(s->version));
return nullptr;
}
// LLD checks ISA compatibility in calcMipsEFlags(). Here we just
// select the highest number of ISA/Rev/Ext.
- Flags.isa_level = std::max(Flags.isa_level, S->isa_level);
- Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev);
- Flags.isa_ext = std::max(Flags.isa_ext, S->isa_ext);
- Flags.gpr_size = std::max(Flags.gpr_size, S->gpr_size);
- Flags.cpr1_size = std::max(Flags.cpr1_size, S->cpr1_size);
- Flags.cpr2_size = std::max(Flags.cpr2_size, S->cpr2_size);
- Flags.ases |= S->ases;
- Flags.flags1 |= S->flags1;
- Flags.flags2 |= S->flags2;
- Flags.fp_abi = elf::getMipsFpAbiFlag(Flags.fp_abi, S->fp_abi, Filename);
+ flags.isa_level = std::max(flags.isa_level, s->isa_level);
+ flags.isa_rev = std::max(flags.isa_rev, s->isa_rev);
+ flags.isa_ext = std::max(flags.isa_ext, s->isa_ext);
+ flags.gpr_size = std::max(flags.gpr_size, s->gpr_size);
+ flags.cpr1_size = std::max(flags.cpr1_size, s->cpr1_size);
+ flags.cpr2_size = std::max(flags.cpr2_size, s->cpr2_size);
+ flags.ases |= s->ases;
+ flags.flags1 |= s->flags1;
+ flags.flags2 |= s->flags2;
+ flags.fp_abi = elf::getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
};
- if (Create)
- return make<MipsAbiFlagsSection<ELFT>>(Flags);
+ if (create)
+ return make<MipsAbiFlagsSection<ELFT>>(flags);
return nullptr;
}
// .MIPS.options section.
template <class ELFT>
-MipsOptionsSection<ELFT>::MipsOptionsSection(Elf_Mips_RegInfo Reginfo)
+MipsOptionsSection<ELFT>::MipsOptionsSection(Elf_Mips_RegInfo reginfo)
: SyntheticSection(SHF_ALLOC, SHT_MIPS_OPTIONS, 8, ".MIPS.options"),
- Reginfo(Reginfo) {
- this->Entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
+ reginfo(reginfo) {
+ this->entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
}
-template <class ELFT> void MipsOptionsSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *Options = reinterpret_cast<Elf_Mips_Options *>(Buf);
- Options->kind = ODK_REGINFO;
- Options->size = getSize();
+template <class ELFT> void MipsOptionsSection<ELFT>::writeTo(uint8_t *buf) {
+ auto *options = reinterpret_cast<Elf_Mips_Options *>(buf);
+ options->kind = ODK_REGINFO;
+ options->size = getSize();
- if (!Config->Relocatable)
- Reginfo.ri_gp_value = In.MipsGot->getGp();
- memcpy(Buf + sizeof(Elf_Mips_Options), &Reginfo, sizeof(Reginfo));
+ if (!config->relocatable)
+ reginfo.ri_gp_value = in.mipsGot->getGp();
+ memcpy(buf + sizeof(Elf_Mips_Options), &reginfo, sizeof(reginfo));
}
template <class ELFT>
@@ -164,55 +170,55 @@ MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
if (!ELFT::Is64Bits)
return nullptr;
- std::vector<InputSectionBase *> Sections;
- for (InputSectionBase *Sec : InputSections)
- if (Sec->Type == SHT_MIPS_OPTIONS)
- Sections.push_back(Sec);
+ std::vector<InputSectionBase *> sections;
+ for (InputSectionBase *sec : inputSections)
+ if (sec->type == SHT_MIPS_OPTIONS)
+ sections.push_back(sec);
- if (Sections.empty())
+ if (sections.empty())
return nullptr;
- Elf_Mips_RegInfo Reginfo = {};
- for (InputSectionBase *Sec : Sections) {
- Sec->Live = false;
+ Elf_Mips_RegInfo reginfo = {};
+ for (InputSectionBase *sec : sections) {
+ sec->markDead();
- std::string Filename = toString(Sec->File);
- ArrayRef<uint8_t> D = Sec->data();
+ std::string filename = toString(sec->file);
+ ArrayRef<uint8_t> d = sec->data();
- while (!D.empty()) {
- if (D.size() < sizeof(Elf_Mips_Options)) {
- error(Filename + ": invalid size of .MIPS.options section");
+ while (!d.empty()) {
+ if (d.size() < sizeof(Elf_Mips_Options)) {
+ error(filename + ": invalid size of .MIPS.options section");
break;
}
- auto *Opt = reinterpret_cast<const Elf_Mips_Options *>(D.data());
- if (Opt->kind == ODK_REGINFO) {
- Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask;
- Sec->getFile<ELFT>()->MipsGp0 = Opt->getRegInfo().ri_gp_value;
+ auto *opt = reinterpret_cast<const Elf_Mips_Options *>(d.data());
+ if (opt->kind == ODK_REGINFO) {
+ reginfo.ri_gprmask |= opt->getRegInfo().ri_gprmask;
+ sec->getFile<ELFT>()->mipsGp0 = opt->getRegInfo().ri_gp_value;
break;
}
- if (!Opt->size)
- fatal(Filename + ": zero option descriptor size");
- D = D.slice(Opt->size);
+ if (!opt->size)
+ fatal(filename + ": zero option descriptor size");
+ d = d.slice(opt->size);
}
};
- return make<MipsOptionsSection<ELFT>>(Reginfo);
+ return make<MipsOptionsSection<ELFT>>(reginfo);
}
// MIPS .reginfo section.
template <class ELFT>
-MipsReginfoSection<ELFT>::MipsReginfoSection(Elf_Mips_RegInfo Reginfo)
+MipsReginfoSection<ELFT>::MipsReginfoSection(Elf_Mips_RegInfo reginfo)
: SyntheticSection(SHF_ALLOC, SHT_MIPS_REGINFO, 4, ".reginfo"),
- Reginfo(Reginfo) {
- this->Entsize = sizeof(Elf_Mips_RegInfo);
+ reginfo(reginfo) {
+ this->entsize = sizeof(Elf_Mips_RegInfo);
}
-template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *Buf) {
- if (!Config->Relocatable)
- Reginfo.ri_gp_value = In.MipsGot->getGp();
- memcpy(Buf, &Reginfo, sizeof(Reginfo));
+template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *buf) {
+ if (!config->relocatable)
+ reginfo.ri_gp_value = in.mipsGot->getGp();
+ memcpy(buf, &reginfo, sizeof(reginfo));
}
template <class ELFT>
@@ -221,53 +227,53 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
if (ELFT::Is64Bits)
return nullptr;
- std::vector<InputSectionBase *> Sections;
- for (InputSectionBase *Sec : InputSections)
- if (Sec->Type == SHT_MIPS_REGINFO)
- Sections.push_back(Sec);
+ std::vector<InputSectionBase *> sections;
+ for (InputSectionBase *sec : inputSections)
+ if (sec->type == SHT_MIPS_REGINFO)
+ sections.push_back(sec);
- if (Sections.empty())
+ if (sections.empty())
return nullptr;
- Elf_Mips_RegInfo Reginfo = {};
- for (InputSectionBase *Sec : Sections) {
- Sec->Live = false;
+ Elf_Mips_RegInfo reginfo = {};
+ for (InputSectionBase *sec : sections) {
+ sec->markDead();
- if (Sec->data().size() != sizeof(Elf_Mips_RegInfo)) {
- error(toString(Sec->File) + ": invalid size of .reginfo section");
+ if (sec->data().size() != sizeof(Elf_Mips_RegInfo)) {
+ error(toString(sec->file) + ": invalid size of .reginfo section");
return nullptr;
}
- auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->data().data());
- Reginfo.ri_gprmask |= R->ri_gprmask;
- Sec->getFile<ELFT>()->MipsGp0 = R->ri_gp_value;
+ auto *r = reinterpret_cast<const Elf_Mips_RegInfo *>(sec->data().data());
+ reginfo.ri_gprmask |= r->ri_gprmask;
+ sec->getFile<ELFT>()->mipsGp0 = r->ri_gp_value;
};
- return make<MipsReginfoSection<ELFT>>(Reginfo);
+ return make<MipsReginfoSection<ELFT>>(reginfo);
}
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};
+ StringRef s = saver.save(config->dynamicLinker);
+ ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
- auto *Sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, Contents,
+ auto *sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
".interp");
- Sec->Live = true;
- return Sec;
+ sec->markLive();
+ return sec;
}
-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)
- In.SymTab->addSymbol(S);
- return S;
+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)
+ in.symTab->addSymbol(s);
+ return s;
}
static size_t getHashSize() {
- switch (Config->BuildId) {
+ switch (config->buildId) {
case BuildIdKind::Fast:
return 8;
case BuildIdKind::Md5:
@@ -276,89 +282,67 @@ static size_t getHashSize() {
case BuildIdKind::Sha1:
return 20;
case BuildIdKind::Hexstring:
- return Config->BuildIdVector.size();
+ return config->buildIdVector.size();
default:
llvm_unreachable("unknown BuildIdKind");
}
}
+// This class represents a linker-synthesized .note.gnu.property section.
+//
+// In x86 and AArch64, object files may contain feature flags indicating the
+// features that they have used. The flags are stored in a .note.gnu.property
+// section.
+//
+// lld reads the sections from input files and merges them by computing AND of
+// the flags. The result is written as a new .note.gnu.property section.
+//
+// If the flag is zero (which indicates that the intersection of the feature
+// sets is empty, or some input files didn't have .note.gnu.property sections),
+// we don't create this section.
+GnuPropertySection::GnuPropertySection()
+ : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, 4,
+ ".note.gnu.property") {}
+
+void GnuPropertySection::writeTo(uint8_t *buf) {
+ uint32_t featureAndType = config->emachine == EM_AARCH64
+ ? GNU_PROPERTY_AARCH64_FEATURE_1_AND
+ : GNU_PROPERTY_X86_FEATURE_1_AND;
+
+ write32(buf, 4); // Name size
+ write32(buf + 4, config->is64 ? 16 : 12); // Content size
+ write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
+ memcpy(buf + 12, "GNU", 4); // Name string
+ write32(buf + 16, featureAndType); // Feature type
+ write32(buf + 20, 4); // Feature size
+ write32(buf + 24, config->andFeatures); // Feature flags
+ if (config->is64)
+ write32(buf + 28, 0); // Padding
+}
+
+size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
+
BuildIdSection::BuildIdSection()
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
- HashSize(getHashSize()) {}
-
-void BuildIdSection::writeTo(uint8_t *Buf) {
- write32(Buf, 4); // Name size
- write32(Buf + 4, HashSize); // Content size
- write32(Buf + 8, NT_GNU_BUILD_ID); // Type
- memcpy(Buf + 12, "GNU", 4); // Name string
- HashBuf = Buf + 16;
-}
-
-// Split one uint8 array into small pieces of uint8 arrays.
-static std::vector<ArrayRef<uint8_t>> split(ArrayRef<uint8_t> Arr,
- size_t ChunkSize) {
- std::vector<ArrayRef<uint8_t>> Ret;
- while (Arr.size() > ChunkSize) {
- Ret.push_back(Arr.take_front(ChunkSize));
- Arr = Arr.drop_front(ChunkSize);
- }
- if (!Arr.empty())
- Ret.push_back(Arr);
- return Ret;
-}
-
-// Computes a hash value of Data using a given hash function.
-// In order to utilize multiple cores, we first split data into 1MB
-// chunks, compute a hash for each chunk, and then compute a hash value
-// of the hash values.
-void BuildIdSection::computeHash(
- llvm::ArrayRef<uint8_t> Data,
- std::function<void(uint8_t *Dest, ArrayRef<uint8_t> Arr)> HashFn) {
- std::vector<ArrayRef<uint8_t>> Chunks = split(Data, 1024 * 1024);
- std::vector<uint8_t> Hashes(Chunks.size() * HashSize);
-
- // Compute hash values.
- parallelForEachN(0, Chunks.size(), [&](size_t I) {
- HashFn(Hashes.data() + I * HashSize, Chunks[I]);
- });
+ hashSize(getHashSize()) {}
- // Write to the final output buffer.
- HashFn(HashBuf, Hashes);
+void BuildIdSection::writeTo(uint8_t *buf) {
+ write32(buf, 4); // Name size
+ write32(buf + 4, hashSize); // Content size
+ write32(buf + 8, NT_GNU_BUILD_ID); // Type
+ memcpy(buf + 12, "GNU", 4); // Name string
+ hashBuf = buf + 16;
}
-BssSection::BssSection(StringRef Name, uint64_t Size, uint32_t Alignment)
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, Alignment, Name) {
- this->Bss = true;
- this->Size = Size;
+void BuildIdSection::writeBuildId(ArrayRef<uint8_t> buf) {
+ assert(buf.size() == hashSize);
+ memcpy(hashBuf, buf.data(), hashSize);
}
-void BuildIdSection::writeBuildId(ArrayRef<uint8_t> Buf) {
- switch (Config->BuildId) {
- case BuildIdKind::Fast:
- computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
- write64le(Dest, xxHash64(Arr));
- });
- break;
- case BuildIdKind::Md5:
- computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
- memcpy(Dest, MD5::hash(Arr).data(), 16);
- });
- break;
- case BuildIdKind::Sha1:
- computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
- memcpy(Dest, SHA1::hash(Arr).data(), 20);
- });
- break;
- case BuildIdKind::Uuid:
- if (auto EC = getRandomBytes(HashBuf, HashSize))
- error("entropy source failure: " + EC.message());
- break;
- case BuildIdKind::Hexstring:
- memcpy(HashBuf, Config->BuildIdVector.data(), Config->BuildIdVector.size());
- break;
- default:
- llvm_unreachable("unknown BuildIdKind");
- }
+BssSection::BssSection(StringRef name, uint64_t size, uint32_t alignment)
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, alignment, name) {
+ this->bss = true;
+ this->size = size;
}
EhFrameSection::EhFrameSection()
@@ -368,47 +352,48 @@ EhFrameSection::EhFrameSection()
// CIE records from input object files are uniquified by their contents
// and where their relocations point to.
template <class ELFT, class RelTy>
-CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef<RelTy> Rels) {
- Symbol *Personality = nullptr;
- unsigned FirstRelI = Cie.FirstRelocation;
- if (FirstRelI != (unsigned)-1)
- Personality =
- &Cie.Sec->template getFile<ELFT>()->getRelocTargetSym(Rels[FirstRelI]);
+CieRecord *EhFrameSection::addCie(EhSectionPiece &cie, ArrayRef<RelTy> rels) {
+ Symbol *personality = nullptr;
+ unsigned firstRelI = cie.firstRelocation;
+ if (firstRelI != (unsigned)-1)
+ personality =
+ &cie.sec->template getFile<ELFT>()->getRelocTargetSym(rels[firstRelI]);
// Search for an existing CIE by CIE contents/relocation target pair.
- CieRecord *&Rec = CieMap[{Cie.data(), Personality}];
+ CieRecord *&rec = cieMap[{cie.data(), personality}];
// If not found, create a new one.
- if (!Rec) {
- Rec = make<CieRecord>();
- Rec->Cie = &Cie;
- CieRecords.push_back(Rec);
+ if (!rec) {
+ rec = make<CieRecord>();
+ rec->cie = &cie;
+ cieRecords.push_back(rec);
}
- return Rec;
+ return rec;
}
// There is one FDE per function. Returns true if a given FDE
// points to a live function.
template <class ELFT, class RelTy>
-bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Fde.Sec);
- unsigned FirstRelI = Fde.FirstRelocation;
+bool EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
+ auto *sec = cast<EhInputSection>(fde.sec);
+ unsigned firstRelI = fde.firstRelocation;
// An FDE should point to some function because FDEs are to describe
// functions. That's however not always the case due to an issue of
// ld.gold with -r. ld.gold may discard only functions and leave their
// corresponding FDEs, which results in creating bad .eh_frame sections.
// To deal with that, we ignore such FDEs.
- if (FirstRelI == (unsigned)-1)
+ if (firstRelI == (unsigned)-1)
return false;
- const RelTy &Rel = Rels[FirstRelI];
- Symbol &B = Sec->template getFile<ELFT>()->getRelocTargetSym(Rel);
+ const RelTy &rel = rels[firstRelI];
+ Symbol &b = sec->template getFile<ELFT>()->getRelocTargetSym(rel);
- // FDEs for garbage-collected or merged-by-ICF sections are dead.
- if (auto *D = dyn_cast<Defined>(&B))
- if (SectionBase *Sec = D->Section)
- return Sec->Live;
+ // FDEs for garbage-collected or merged-by-ICF sections, or sections in
+ // another partition, are dead.
+ if (auto *d = dyn_cast<Defined>(&b))
+ if (SectionBase *sec = d->section)
+ return sec->partition == partition;
return false;
}
@@ -417,73 +402,73 @@ bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) {
// a list of FDEs. This function searches an existing CIE or create a new
// one and associates FDEs to the CIE.
template <class ELFT, class RelTy>
-void EhFrameSection::addSectionAux(EhInputSection *Sec, ArrayRef<RelTy> Rels) {
- OffsetToCie.clear();
- for (EhSectionPiece &Piece : Sec->Pieces) {
+void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) {
+ offsetToCie.clear();
+ for (EhSectionPiece &piece : sec->pieces) {
// The empty record is the end marker.
- if (Piece.Size == 4)
+ if (piece.size == 4)
return;
- size_t Offset = Piece.InputOff;
- uint32_t ID = read32(Piece.data().data() + 4);
- if (ID == 0) {
- OffsetToCie[Offset] = addCie<ELFT>(Piece, Rels);
+ size_t offset = piece.inputOff;
+ uint32_t id = read32(piece.data().data() + 4);
+ if (id == 0) {
+ offsetToCie[offset] = addCie<ELFT>(piece, rels);
continue;
}
- uint32_t CieOffset = Offset + 4 - ID;
- CieRecord *Rec = OffsetToCie[CieOffset];
- if (!Rec)
- fatal(toString(Sec) + ": invalid CIE reference");
+ uint32_t cieOffset = offset + 4 - id;
+ CieRecord *rec = offsetToCie[cieOffset];
+ if (!rec)
+ fatal(toString(sec) + ": invalid CIE reference");
- if (!isFdeLive<ELFT>(Piece, Rels))
+ if (!isFdeLive<ELFT>(piece, rels))
continue;
- Rec->Fdes.push_back(&Piece);
- NumFdes++;
+ rec->fdes.push_back(&piece);
+ numFdes++;
}
}
-template <class ELFT> void EhFrameSection::addSection(InputSectionBase *C) {
- auto *Sec = cast<EhInputSection>(C);
- Sec->Parent = this;
+template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) {
+ auto *sec = cast<EhInputSection>(c);
+ sec->parent = this;
- Alignment = std::max(Alignment, Sec->Alignment);
- Sections.push_back(Sec);
+ alignment = std::max(alignment, sec->alignment);
+ sections.push_back(sec);
- for (auto *DS : Sec->DependentSections)
- DependentSections.push_back(DS);
+ for (auto *ds : sec->dependentSections)
+ dependentSections.push_back(ds);
- if (Sec->Pieces.empty())
+ if (sec->pieces.empty())
return;
- if (Sec->AreRelocsRela)
- addSectionAux<ELFT>(Sec, Sec->template relas<ELFT>());
+ if (sec->areRelocsRela)
+ addSectionAux<ELFT>(sec, sec->template relas<ELFT>());
else
- addSectionAux<ELFT>(Sec, Sec->template rels<ELFT>());
+ addSectionAux<ELFT>(sec, sec->template rels<ELFT>());
}
-static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) {
- memcpy(Buf, D.data(), D.size());
+static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
+ memcpy(buf, d.data(), d.size());
- size_t Aligned = alignTo(D.size(), Config->Wordsize);
+ size_t aligned = alignTo(d.size(), config->wordsize);
// Zero-clear trailing padding if it exists.
- memset(Buf + D.size(), 0, Aligned - D.size());
+ memset(buf + d.size(), 0, aligned - d.size());
// Fix the size field. -4 since size does not include the size field itself.
- write32(Buf, Aligned - 4);
+ write32(buf, aligned - 4);
}
void EhFrameSection::finalizeContents() {
- assert(!this->Size); // Not finalized.
- size_t Off = 0;
- for (CieRecord *Rec : CieRecords) {
- Rec->Cie->OutputOff = Off;
- Off += alignTo(Rec->Cie->Size, Config->Wordsize);
-
- for (EhSectionPiece *Fde : Rec->Fdes) {
- Fde->OutputOff = Off;
- Off += alignTo(Fde->Size, Config->Wordsize);
+ assert(!this->size); // Not finalized.
+ size_t off = 0;
+ for (CieRecord *rec : cieRecords) {
+ rec->cie->outputOff = off;
+ off += alignTo(rec->cie->size, config->wordsize);
+
+ for (EhSectionPiece *fde : rec->fdes) {
+ fde->outputOff = off;
+ off += alignTo(fde->size, config->wordsize);
}
}
@@ -491,375 +476,374 @@ void EhFrameSection::finalizeContents() {
// Call Frame Information records. glibc unwind-dw2-fde.c
// classify_object_over_fdes expects there is a CIE record length 0 as a
// terminator. Thus we add one unconditionally.
- Off += 4;
+ off += 4;
- this->Size = Off;
+ this->size = off;
}
// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table
// to get an FDE from an address to which FDE is applied. This function
// returns a list of such pairs.
std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const {
- uint8_t *Buf = getParent()->Loc + OutSecOff;
- std::vector<FdeData> Ret;
-
- uint64_t VA = In.EhFrameHdr->getVA();
- for (CieRecord *Rec : CieRecords) {
- uint8_t Enc = getFdeEncoding(Rec->Cie);
- for (EhSectionPiece *Fde : Rec->Fdes) {
- uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
- uint64_t FdeVA = getParent()->Addr + Fde->OutputOff;
- if (!isInt<32>(Pc - VA))
- fatal(toString(Fde->Sec) + ": PC offset is too large: 0x" +
- Twine::utohexstr(Pc - VA));
- Ret.push_back({uint32_t(Pc - VA), uint32_t(FdeVA - VA)});
+ uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff;
+ std::vector<FdeData> ret;
+
+ uint64_t va = getPartition().ehFrameHdr->getVA();
+ for (CieRecord *rec : cieRecords) {
+ uint8_t enc = getFdeEncoding(rec->cie);
+ for (EhSectionPiece *fde : rec->fdes) {
+ uint64_t pc = getFdePc(buf, fde->outputOff, enc);
+ uint64_t fdeVA = getParent()->addr + fde->outputOff;
+ if (!isInt<32>(pc - va))
+ fatal(toString(fde->sec) + ": PC offset is too large: 0x" +
+ Twine::utohexstr(pc - va));
+ ret.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)});
}
}
// Sort the FDE list by their PC and uniqueify. Usually there is only
// one FDE for a PC (i.e. function), but if ICF merges two functions
// into one, there can be more than one FDEs pointing to the address.
- auto Less = [](const FdeData &A, const FdeData &B) {
- return A.PcRel < B.PcRel;
+ auto less = [](const FdeData &a, const FdeData &b) {
+ return a.pcRel < b.pcRel;
};
- std::stable_sort(Ret.begin(), Ret.end(), Less);
- auto Eq = [](const FdeData &A, const FdeData &B) {
- return A.PcRel == B.PcRel;
+ llvm::stable_sort(ret, less);
+ auto eq = [](const FdeData &a, const FdeData &b) {
+ return a.pcRel == b.pcRel;
};
- Ret.erase(std::unique(Ret.begin(), Ret.end(), Eq), Ret.end());
+ ret.erase(std::unique(ret.begin(), ret.end(), eq), ret.end());
- return Ret;
+ return ret;
}
-static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
- switch (Size) {
+static uint64_t readFdeAddr(uint8_t *buf, int size) {
+ switch (size) {
case DW_EH_PE_udata2:
- return read16(Buf);
+ return read16(buf);
case DW_EH_PE_sdata2:
- return (int16_t)read16(Buf);
+ return (int16_t)read16(buf);
case DW_EH_PE_udata4:
- return read32(Buf);
+ return read32(buf);
case DW_EH_PE_sdata4:
- return (int32_t)read32(Buf);
+ return (int32_t)read32(buf);
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
- return read64(Buf);
+ return read64(buf);
case DW_EH_PE_absptr:
- return readUint(Buf);
+ return readUint(buf);
}
fatal("unknown FDE size encoding");
}
// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to.
// We need it to create .eh_frame_hdr section.
-uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff,
- uint8_t Enc) const {
+uint64_t EhFrameSection::getFdePc(uint8_t *buf, size_t fdeOff,
+ uint8_t enc) const {
// The starting address to which this FDE applies is
// stored at FDE + 8 byte.
- size_t Off = FdeOff + 8;
- uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0xf);
- if ((Enc & 0x70) == DW_EH_PE_absptr)
- return Addr;
- if ((Enc & 0x70) == DW_EH_PE_pcrel)
- return Addr + getParent()->Addr + Off;
+ size_t off = fdeOff + 8;
+ uint64_t addr = readFdeAddr(buf + off, enc & 0xf);
+ if ((enc & 0x70) == DW_EH_PE_absptr)
+ return addr;
+ if ((enc & 0x70) == DW_EH_PE_pcrel)
+ return addr + getParent()->addr + off;
fatal("unknown FDE size relative encoding");
}
-void EhFrameSection::writeTo(uint8_t *Buf) {
+void EhFrameSection::writeTo(uint8_t *buf) {
// Write CIE and FDE records.
- for (CieRecord *Rec : CieRecords) {
- size_t CieOffset = Rec->Cie->OutputOff;
- writeCieFde(Buf + CieOffset, Rec->Cie->data());
+ for (CieRecord *rec : cieRecords) {
+ size_t cieOffset = rec->cie->outputOff;
+ writeCieFde(buf + cieOffset, rec->cie->data());
- for (EhSectionPiece *Fde : Rec->Fdes) {
- size_t Off = Fde->OutputOff;
- writeCieFde(Buf + Off, Fde->data());
+ for (EhSectionPiece *fde : rec->fdes) {
+ size_t off = fde->outputOff;
+ writeCieFde(buf + off, fde->data());
// FDE's second word should have the offset to an associated CIE.
// Write it.
- write32(Buf + Off + 4, Off + 4 - CieOffset);
+ write32(buf + off + 4, off + 4 - cieOffset);
}
}
// Apply relocations. .eh_frame section contents are not contiguous
// in the output buffer, but relocateAlloc() still works because
// getOffset() takes care of discontiguous section pieces.
- for (EhInputSection *S : Sections)
- S->relocateAlloc(Buf, nullptr);
+ for (EhInputSection *s : sections)
+ s->relocateAlloc(buf, nullptr);
+
+ if (getPartition().ehFrameHdr && getPartition().ehFrameHdr->getParent())
+ getPartition().ehFrameHdr->write();
}
GotSection::GotSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotEntrySize, ".got") {
- // PPC64 saves the ElfSym::GlobalOffsetTable .TOC. as the first entry in the
- // .got. If there are no references to .TOC. in the symbol table,
- // ElfSym::GlobalOffsetTable will not be defined and we won't need to save
- // .TOC. in the .got. When it is defined, we increase NumEntries by the number
- // of entries used to emit ElfSym::GlobalOffsetTable.
- if (ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt)
- NumEntries += Target->GotHeaderEntriesNum;
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
+ ".got") {
+ // If ElfSym::globalOffsetTable is relative to .got and is referenced,
+ // increase numEntries by the number of entries used to emit
+ // ElfSym::globalOffsetTable.
+ if (ElfSym::globalOffsetTable && !target->gotBaseSymInGotPlt)
+ numEntries += target->gotHeaderEntriesNum;
}
-void GotSection::addEntry(Symbol &Sym) {
- Sym.GotIndex = NumEntries;
- ++NumEntries;
+void GotSection::addEntry(Symbol &sym) {
+ sym.gotIndex = numEntries;
+ ++numEntries;
}
-bool GotSection::addDynTlsEntry(Symbol &Sym) {
- if (Sym.GlobalDynIndex != -1U)
+bool GotSection::addDynTlsEntry(Symbol &sym) {
+ if (sym.globalDynIndex != -1U)
return false;
- Sym.GlobalDynIndex = NumEntries;
+ sym.globalDynIndex = numEntries;
// Global Dynamic TLS entries take two GOT slots.
- NumEntries += 2;
+ numEntries += 2;
return true;
}
// Reserves TLS entries for a TLS module ID and a TLS block offset.
// In total it takes two GOT slots.
bool GotSection::addTlsIndex() {
- if (TlsIndexOff != uint32_t(-1))
+ if (tlsIndexOff != uint32_t(-1))
return false;
- TlsIndexOff = NumEntries * Config->Wordsize;
- NumEntries += 2;
+ tlsIndexOff = numEntries * config->wordsize;
+ numEntries += 2;
return true;
}
-uint64_t GotSection::getGlobalDynAddr(const Symbol &B) const {
- return this->getVA() + B.GlobalDynIndex * Config->Wordsize;
+uint64_t GotSection::getGlobalDynAddr(const Symbol &b) const {
+ return this->getVA() + b.globalDynIndex * config->wordsize;
}
-uint64_t GotSection::getGlobalDynOffset(const Symbol &B) const {
- return B.GlobalDynIndex * Config->Wordsize;
+uint64_t GotSection::getGlobalDynOffset(const Symbol &b) const {
+ return b.globalDynIndex * config->wordsize;
}
void GotSection::finalizeContents() {
- Size = NumEntries * Config->Wordsize;
+ size = numEntries * config->wordsize;
}
-bool GotSection::empty() const {
+bool GotSection::isNeeded() const {
// We need to emit a GOT even if it's empty if there's a relocation that is
- // relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT
- // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got.
- return NumEntries == 0 && !HasGotOffRel &&
- !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt);
+ // relative to GOT(such as GOTOFFREL).
+ return numEntries || hasGotOffRel;
}
-void GotSection::writeTo(uint8_t *Buf) {
+void GotSection::writeTo(uint8_t *buf) {
// Buf points to the start of this section's buffer,
// whereas InputSectionBase::relocateAlloc() expects its argument
// to point to the start of the output section.
- Target->writeGotHeader(Buf);
- relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size);
+ target->writeGotHeader(buf);
+ relocateAlloc(buf - outSecOff, buf - outSecOff + size);
}
-static uint64_t getMipsPageAddr(uint64_t Addr) {
- return (Addr + 0x8000) & ~0xffff;
+static uint64_t getMipsPageAddr(uint64_t addr) {
+ return (addr + 0x8000) & ~0xffff;
}
-static uint64_t getMipsPageCount(uint64_t Size) {
- return (Size + 0xfffe) / 0xffff + 1;
+static uint64_t getMipsPageCount(uint64_t size) {
+ return (size + 0xfffe) / 0xffff + 1;
}
MipsGotSection::MipsGotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16,
".got") {}
-void MipsGotSection::addEntry(InputFile &File, Symbol &Sym, int64_t Addend,
- RelExpr Expr) {
- FileGot &G = getGot(File);
- if (Expr == R_MIPS_GOT_LOCAL_PAGE) {
- if (const OutputSection *OS = Sym.getOutputSection())
- G.PagesMap.insert({OS, {}});
+void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend,
+ RelExpr expr) {
+ FileGot &g = getGot(file);
+ if (expr == R_MIPS_GOT_LOCAL_PAGE) {
+ if (const OutputSection *os = sym.getOutputSection())
+ g.pagesMap.insert({os, {}});
else
- G.Local16.insert({{nullptr, getMipsPageAddr(Sym.getVA(Addend))}, 0});
- } else if (Sym.isTls())
- G.Tls.insert({&Sym, 0});
- else if (Sym.IsPreemptible && Expr == R_ABS)
- G.Relocs.insert({&Sym, 0});
- else if (Sym.IsPreemptible)
- G.Global.insert({&Sym, 0});
- else if (Expr == R_MIPS_GOT_OFF32)
- G.Local32.insert({{&Sym, Addend}, 0});
+ g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(addend))}, 0});
+ } else if (sym.isTls())
+ g.tls.insert({&sym, 0});
+ else if (sym.isPreemptible && expr == R_ABS)
+ g.relocs.insert({&sym, 0});
+ else if (sym.isPreemptible)
+ g.global.insert({&sym, 0});
+ else if (expr == R_MIPS_GOT_OFF32)
+ g.local32.insert({{&sym, addend}, 0});
else
- G.Local16.insert({{&Sym, Addend}, 0});
+ g.local16.insert({{&sym, addend}, 0});
}
-void MipsGotSection::addDynTlsEntry(InputFile &File, Symbol &Sym) {
- getGot(File).DynTlsSymbols.insert({&Sym, 0});
+void MipsGotSection::addDynTlsEntry(InputFile &file, Symbol &sym) {
+ getGot(file).dynTlsSymbols.insert({&sym, 0});
}
-void MipsGotSection::addTlsIndex(InputFile &File) {
- getGot(File).DynTlsSymbols.insert({nullptr, 0});
+void MipsGotSection::addTlsIndex(InputFile &file) {
+ getGot(file).dynTlsSymbols.insert({nullptr, 0});
}
size_t MipsGotSection::FileGot::getEntriesNum() const {
- return getPageEntriesNum() + Local16.size() + Global.size() + Relocs.size() +
- Tls.size() + DynTlsSymbols.size() * 2;
+ return getPageEntriesNum() + local16.size() + global.size() + relocs.size() +
+ tls.size() + dynTlsSymbols.size() * 2;
}
size_t MipsGotSection::FileGot::getPageEntriesNum() const {
- size_t Num = 0;
- for (const std::pair<const OutputSection *, FileGot::PageBlock> &P : PagesMap)
- Num += P.second.Count;
- return Num;
+ size_t num = 0;
+ for (const std::pair<const OutputSection *, FileGot::PageBlock> &p : pagesMap)
+ num += p.second.count;
+ return num;
}
size_t MipsGotSection::FileGot::getIndexedEntriesNum() const {
- size_t Count = getPageEntriesNum() + Local16.size() + Global.size();
+ size_t count = getPageEntriesNum() + local16.size() + global.size();
// If there are relocation-only entries in the GOT, TLS entries
// are allocated after them. TLS entries should be addressable
// by 16-bit index so count both reloc-only and TLS entries.
- if (!Tls.empty() || !DynTlsSymbols.empty())
- Count += Relocs.size() + Tls.size() + DynTlsSymbols.size() * 2;
- return Count;
+ if (!tls.empty() || !dynTlsSymbols.empty())
+ count += relocs.size() + tls.size() + dynTlsSymbols.size() * 2;
+ return count;
}
-MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &F) {
- if (!F.MipsGotIndex.hasValue()) {
- Gots.emplace_back();
- Gots.back().File = &F;
- F.MipsGotIndex = Gots.size() - 1;
+MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &f) {
+ if (!f.mipsGotIndex.hasValue()) {
+ gots.emplace_back();
+ gots.back().file = &f;
+ f.mipsGotIndex = gots.size() - 1;
}
- return Gots[*F.MipsGotIndex];
+ return gots[*f.mipsGotIndex];
}
-uint64_t MipsGotSection::getPageEntryOffset(const InputFile *F,
- const Symbol &Sym,
- int64_t Addend) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- uint64_t Index = 0;
- if (const OutputSection *OutSec = Sym.getOutputSection()) {
- uint64_t SecAddr = getMipsPageAddr(OutSec->Addr);
- uint64_t SymAddr = getMipsPageAddr(Sym.getVA(Addend));
- Index = G.PagesMap.lookup(OutSec).FirstIndex + (SymAddr - SecAddr) / 0xffff;
+uint64_t MipsGotSection::getPageEntryOffset(const InputFile *f,
+ const Symbol &sym,
+ int64_t addend) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ uint64_t index = 0;
+ if (const OutputSection *outSec = sym.getOutputSection()) {
+ uint64_t secAddr = getMipsPageAddr(outSec->addr);
+ uint64_t symAddr = getMipsPageAddr(sym.getVA(addend));
+ index = g.pagesMap.lookup(outSec).firstIndex + (symAddr - secAddr) / 0xffff;
} else {
- Index = G.Local16.lookup({nullptr, getMipsPageAddr(Sym.getVA(Addend))});
+ index = g.local16.lookup({nullptr, getMipsPageAddr(sym.getVA(addend))});
}
- return Index * Config->Wordsize;
+ return index * config->wordsize;
}
-uint64_t MipsGotSection::getSymEntryOffset(const InputFile *F, const Symbol &S,
- int64_t Addend) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- Symbol *Sym = const_cast<Symbol *>(&S);
- if (Sym->isTls())
- return G.Tls.lookup(Sym) * Config->Wordsize;
- if (Sym->IsPreemptible)
- return G.Global.lookup(Sym) * Config->Wordsize;
- return G.Local16.lookup({Sym, Addend}) * Config->Wordsize;
+uint64_t MipsGotSection::getSymEntryOffset(const InputFile *f, const Symbol &s,
+ int64_t addend) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ Symbol *sym = const_cast<Symbol *>(&s);
+ if (sym->isTls())
+ return g.tls.lookup(sym) * config->wordsize;
+ if (sym->isPreemptible)
+ return g.global.lookup(sym) * config->wordsize;
+ return g.local16.lookup({sym, addend}) * config->wordsize;
}
-uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *F) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- return G.DynTlsSymbols.lookup(nullptr) * Config->Wordsize;
+uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *f) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ return g.dynTlsSymbols.lookup(nullptr) * config->wordsize;
}
-uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *F,
- const Symbol &S) const {
- const FileGot &G = Gots[*F->MipsGotIndex];
- Symbol *Sym = const_cast<Symbol *>(&S);
- return G.DynTlsSymbols.lookup(Sym) * Config->Wordsize;
+uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *f,
+ const Symbol &s) const {
+ const FileGot &g = gots[*f->mipsGotIndex];
+ Symbol *sym = const_cast<Symbol *>(&s);
+ return g.dynTlsSymbols.lookup(sym) * config->wordsize;
}
const Symbol *MipsGotSection::getFirstGlobalEntry() const {
- if (Gots.empty())
+ if (gots.empty())
return nullptr;
- const FileGot &PrimGot = Gots.front();
- if (!PrimGot.Global.empty())
- return PrimGot.Global.front().first;
- if (!PrimGot.Relocs.empty())
- return PrimGot.Relocs.front().first;
+ const FileGot &primGot = gots.front();
+ if (!primGot.global.empty())
+ return primGot.global.front().first;
+ if (!primGot.relocs.empty())
+ return primGot.relocs.front().first;
return nullptr;
}
unsigned MipsGotSection::getLocalEntriesNum() const {
- if (Gots.empty())
- return HeaderEntriesNum;
- return HeaderEntriesNum + Gots.front().getPageEntriesNum() +
- Gots.front().Local16.size();
+ if (gots.empty())
+ return headerEntriesNum;
+ return headerEntriesNum + gots.front().getPageEntriesNum() +
+ gots.front().local16.size();
}
-bool MipsGotSection::tryMergeGots(FileGot &Dst, FileGot &Src, bool IsPrimary) {
- FileGot Tmp = Dst;
- set_union(Tmp.PagesMap, Src.PagesMap);
- set_union(Tmp.Local16, Src.Local16);
- set_union(Tmp.Global, Src.Global);
- set_union(Tmp.Relocs, Src.Relocs);
- set_union(Tmp.Tls, Src.Tls);
- set_union(Tmp.DynTlsSymbols, Src.DynTlsSymbols);
+bool MipsGotSection::tryMergeGots(FileGot &dst, FileGot &src, bool isPrimary) {
+ FileGot tmp = dst;
+ set_union(tmp.pagesMap, src.pagesMap);
+ set_union(tmp.local16, src.local16);
+ set_union(tmp.global, src.global);
+ set_union(tmp.relocs, src.relocs);
+ set_union(tmp.tls, src.tls);
+ set_union(tmp.dynTlsSymbols, src.dynTlsSymbols);
- size_t Count = IsPrimary ? HeaderEntriesNum : 0;
- Count += Tmp.getIndexedEntriesNum();
+ size_t count = isPrimary ? headerEntriesNum : 0;
+ count += tmp.getIndexedEntriesNum();
- if (Count * Config->Wordsize > Config->MipsGotSize)
+ if (count * config->wordsize > config->mipsGotSize)
return false;
- std::swap(Tmp, Dst);
+ std::swap(tmp, dst);
return true;
}
void MipsGotSection::finalizeContents() { updateAllocSize(); }
bool MipsGotSection::updateAllocSize() {
- Size = HeaderEntriesNum * Config->Wordsize;
- for (const FileGot &G : Gots)
- Size += G.getEntriesNum() * Config->Wordsize;
+ size = headerEntriesNum * config->wordsize;
+ for (const FileGot &g : gots)
+ size += g.getEntriesNum() * config->wordsize;
return false;
}
-template <class ELFT> void MipsGotSection::build() {
- if (Gots.empty())
+void MipsGotSection::build() {
+ if (gots.empty())
return;
- std::vector<FileGot> MergedGots(1);
+ std::vector<FileGot> mergedGots(1);
// For each GOT move non-preemptible symbols from the `Global`
// to `Local16` list. Preemptible symbol might become non-preemptible
// one if, for example, it gets a related copy relocation.
- for (FileGot &Got : Gots) {
- for (auto &P: Got.Global)
- if (!P.first->IsPreemptible)
- Got.Local16.insert({{P.first, 0}, 0});
- Got.Global.remove_if([&](const std::pair<Symbol *, size_t> &P) {
- return !P.first->IsPreemptible;
+ for (FileGot &got : gots) {
+ for (auto &p: got.global)
+ if (!p.first->isPreemptible)
+ got.local16.insert({{p.first, 0}, 0});
+ got.global.remove_if([&](const std::pair<Symbol *, size_t> &p) {
+ return !p.first->isPreemptible;
});
}
// For each GOT remove "reloc-only" entry if there is "global"
// entry for the same symbol. And add local entries which indexed
// using 32-bit value at the end of 16-bit entries.
- for (FileGot &Got : Gots) {
- Got.Relocs.remove_if([&](const std::pair<Symbol *, size_t> &P) {
- return Got.Global.count(P.first);
+ for (FileGot &got : gots) {
+ got.relocs.remove_if([&](const std::pair<Symbol *, size_t> &p) {
+ return got.global.count(p.first);
});
- set_union(Got.Local16, Got.Local32);
- Got.Local32.clear();
+ set_union(got.local16, got.local32);
+ got.local32.clear();
}
// Evaluate number of "reloc-only" entries in the resulting GOT.
// To do that put all unique "reloc-only" and "global" entries
// from all GOTs to the future primary GOT.
- FileGot *PrimGot = &MergedGots.front();
- for (FileGot &Got : Gots) {
- set_union(PrimGot->Relocs, Got.Global);
- set_union(PrimGot->Relocs, Got.Relocs);
- Got.Relocs.clear();
+ FileGot *primGot = &mergedGots.front();
+ for (FileGot &got : gots) {
+ set_union(primGot->relocs, got.global);
+ set_union(primGot->relocs, got.relocs);
+ got.relocs.clear();
}
// Evaluate number of "page" entries in each GOT.
- for (FileGot &Got : Gots) {
- for (std::pair<const OutputSection *, FileGot::PageBlock> &P :
- Got.PagesMap) {
- const OutputSection *OS = P.first;
- uint64_t SecSize = 0;
- for (BaseCommand *Cmd : OS->SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
- for (InputSection *IS : ISD->Sections) {
- uint64_t Off = alignTo(SecSize, IS->Alignment);
- SecSize = Off + IS->getSize();
+ for (FileGot &got : gots) {
+ for (std::pair<const OutputSection *, FileGot::PageBlock> &p :
+ got.pagesMap) {
+ const OutputSection *os = p.first;
+ uint64_t secSize = 0;
+ for (BaseCommand *cmd : os->sectionCommands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
+ for (InputSection *isec : isd->sections) {
+ uint64_t off = alignTo(secSize, isec->alignment);
+ secSize = off + isec->getSize();
}
}
- P.second.Count = getMipsPageCount(SecSize);
+ p.second.count = getMipsPageCount(secSize);
}
}
@@ -868,149 +852,149 @@ template <class ELFT> void MipsGotSection::build() {
// the primary GOT can be accessed in the most effective way. If it
// is not possible, try to fill the last GOT in the list, and finally
// create a new GOT if both attempts failed.
- for (FileGot &SrcGot : Gots) {
- InputFile *File = SrcGot.File;
- if (tryMergeGots(MergedGots.front(), SrcGot, true)) {
- File->MipsGotIndex = 0;
+ for (FileGot &srcGot : gots) {
+ InputFile *file = srcGot.file;
+ if (tryMergeGots(mergedGots.front(), srcGot, true)) {
+ file->mipsGotIndex = 0;
} else {
// If this is the first time we failed to merge with the primary GOT,
// MergedGots.back() will also be the primary GOT. We must make sure not
- // to try to merge again with IsPrimary=false, as otherwise, if the
+ // to try to merge again with isPrimary=false, as otherwise, if the
// inputs are just right, we could allow the primary GOT to become 1 or 2
- // words too big due to ignoring the header size.
- if (MergedGots.size() == 1 ||
- !tryMergeGots(MergedGots.back(), SrcGot, false)) {
- MergedGots.emplace_back();
- std::swap(MergedGots.back(), SrcGot);
+ // words bigger due to ignoring the header size.
+ if (mergedGots.size() == 1 ||
+ !tryMergeGots(mergedGots.back(), srcGot, false)) {
+ mergedGots.emplace_back();
+ std::swap(mergedGots.back(), srcGot);
}
- File->MipsGotIndex = MergedGots.size() - 1;
+ file->mipsGotIndex = mergedGots.size() - 1;
}
}
- std::swap(Gots, MergedGots);
+ std::swap(gots, mergedGots);
// Reduce number of "reloc-only" entries in the primary GOT
// by substracting "global" entries exist in the primary GOT.
- PrimGot = &Gots.front();
- PrimGot->Relocs.remove_if([&](const std::pair<Symbol *, size_t> &P) {
- return PrimGot->Global.count(P.first);
+ primGot = &gots.front();
+ primGot->relocs.remove_if([&](const std::pair<Symbol *, size_t> &p) {
+ return primGot->global.count(p.first);
});
// Calculate indexes for each GOT entry.
- size_t Index = HeaderEntriesNum;
- for (FileGot &Got : Gots) {
- Got.StartIndex = &Got == PrimGot ? 0 : Index;
- for (std::pair<const OutputSection *, FileGot::PageBlock> &P :
- Got.PagesMap) {
+ size_t index = headerEntriesNum;
+ for (FileGot &got : gots) {
+ got.startIndex = &got == primGot ? 0 : index;
+ for (std::pair<const OutputSection *, FileGot::PageBlock> &p :
+ got.pagesMap) {
// For each output section referenced by GOT page relocations calculate
- // and save into PagesMap an upper bound of MIPS GOT entries required
+ // and save into pagesMap an upper bound of MIPS GOT entries required
// to store page addresses of local symbols. We assume the worst case -
// each 64kb page of the output section has at least one GOT relocation
// against it. And take in account the case when the section intersects
// page boundaries.
- P.second.FirstIndex = Index;
- Index += P.second.Count;
+ p.second.firstIndex = index;
+ index += p.second.count;
}
- for (auto &P: Got.Local16)
- P.second = Index++;
- for (auto &P: Got.Global)
- P.second = Index++;
- for (auto &P: Got.Relocs)
- P.second = Index++;
- for (auto &P: Got.Tls)
- P.second = Index++;
- for (auto &P: Got.DynTlsSymbols) {
- P.second = Index;
- Index += 2;
+ for (auto &p: got.local16)
+ p.second = index++;
+ for (auto &p: got.global)
+ p.second = index++;
+ for (auto &p: got.relocs)
+ p.second = index++;
+ for (auto &p: got.tls)
+ p.second = index++;
+ for (auto &p: got.dynTlsSymbols) {
+ p.second = index;
+ index += 2;
}
}
- // Update Symbol::GotIndex field to use this
+ // Update Symbol::gotIndex field to use this
// value later in the `sortMipsSymbols` function.
- for (auto &P : PrimGot->Global)
- P.first->GotIndex = P.second;
- for (auto &P : PrimGot->Relocs)
- P.first->GotIndex = P.second;
+ for (auto &p : primGot->global)
+ p.first->gotIndex = p.second;
+ for (auto &p : primGot->relocs)
+ p.first->gotIndex = p.second;
// Create dynamic relocations.
- for (FileGot &Got : Gots) {
+ for (FileGot &got : gots) {
// Create dynamic relocations for TLS entries.
- for (std::pair<Symbol *, size_t> &P : Got.Tls) {
- Symbol *S = P.first;
- uint64_t Offset = P.second * Config->Wordsize;
- if (S->IsPreemptible)
- In.RelaDyn->addReloc(Target->TlsGotRel, this, Offset, S);
+ for (std::pair<Symbol *, size_t> &p : got.tls) {
+ Symbol *s = p.first;
+ uint64_t offset = p.second * config->wordsize;
+ if (s->isPreemptible)
+ mainPart->relaDyn->addReloc(target->tlsGotRel, this, offset, s);
}
- for (std::pair<Symbol *, size_t> &P : Got.DynTlsSymbols) {
- Symbol *S = P.first;
- uint64_t Offset = P.second * Config->Wordsize;
- if (S == nullptr) {
- if (!Config->Pic)
+ for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) {
+ Symbol *s = p.first;
+ uint64_t offset = p.second * config->wordsize;
+ if (s == nullptr) {
+ if (!config->isPic)
continue;
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S);
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s);
} else {
// When building a shared library we still need a dynamic relocation
// for the module index. Therefore only checking for
- // S->IsPreemptible is not sufficient (this happens e.g. for
+ // S->isPreemptible is not sufficient (this happens e.g. for
// thread-locals that have been marked as local through a linker script)
- if (!S->IsPreemptible && !Config->Pic)
+ if (!s->isPreemptible && !config->isPic)
continue;
- In.RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S);
+ mainPart->relaDyn->addReloc(target->tlsModuleIndexRel, this, offset, s);
// However, we can skip writing the TLS offset reloc for non-preemptible
// symbols since it is known even in shared libraries
- if (!S->IsPreemptible)
+ if (!s->isPreemptible)
continue;
- Offset += Config->Wordsize;
- In.RelaDyn->addReloc(Target->TlsOffsetRel, this, Offset, S);
+ offset += config->wordsize;
+ mainPart->relaDyn->addReloc(target->tlsOffsetRel, this, offset, s);
}
}
// Do not create dynamic relocations for non-TLS
// entries in the primary GOT.
- if (&Got == PrimGot)
+ if (&got == primGot)
continue;
// Dynamic relocations for "global" entries.
- for (const std::pair<Symbol *, size_t> &P : Got.Global) {
- uint64_t Offset = P.second * Config->Wordsize;
- In.RelaDyn->addReloc(Target->RelativeRel, this, Offset, P.first);
+ for (const std::pair<Symbol *, size_t> &p : got.global) {
+ uint64_t offset = p.second * config->wordsize;
+ mainPart->relaDyn->addReloc(target->relativeRel, this, offset, p.first);
}
- if (!Config->Pic)
+ if (!config->isPic)
continue;
// Dynamic relocations for "local" entries in case of PIC.
- for (const std::pair<const OutputSection *, FileGot::PageBlock> &L :
- Got.PagesMap) {
- size_t PageCount = L.second.Count;
- for (size_t PI = 0; PI < PageCount; ++PI) {
- uint64_t Offset = (L.second.FirstIndex + PI) * Config->Wordsize;
- In.RelaDyn->addReloc({Target->RelativeRel, this, Offset, L.first,
- int64_t(PI * 0x10000)});
+ for (const std::pair<const OutputSection *, FileGot::PageBlock> &l :
+ got.pagesMap) {
+ size_t pageCount = l.second.count;
+ for (size_t pi = 0; pi < pageCount; ++pi) {
+ uint64_t offset = (l.second.firstIndex + pi) * config->wordsize;
+ mainPart->relaDyn->addReloc({target->relativeRel, this, offset, l.first,
+ int64_t(pi * 0x10000)});
}
}
- for (const std::pair<GotEntry, size_t> &P : Got.Local16) {
- uint64_t Offset = P.second * Config->Wordsize;
- In.RelaDyn->addReloc({Target->RelativeRel, this, Offset, true,
- P.first.first, P.first.second});
+ for (const std::pair<GotEntry, size_t> &p : got.local16) {
+ uint64_t offset = p.second * config->wordsize;
+ mainPart->relaDyn->addReloc({target->relativeRel, this, offset, true,
+ p.first.first, p.first.second});
}
}
}
-bool MipsGotSection::empty() const {
+bool MipsGotSection::isNeeded() const {
// We add the .got section to the result for dynamic MIPS target because
// its address and properties are mentioned in the .dynamic section.
- return Config->Relocatable;
+ return !config->relocatable;
}
-uint64_t MipsGotSection::getGp(const InputFile *F) const {
+uint64_t MipsGotSection::getGp(const InputFile *f) const {
// For files without related GOT or files refer a primary GOT
// returns "common" _gp value. For secondary GOTs calculate
// individual _gp values.
- if (!F || !F->MipsGotIndex.hasValue() || *F->MipsGotIndex == 0)
- return ElfSym::MipsGp->getVA(0);
- return getVA() + Gots[*F->MipsGotIndex].StartIndex * Config->Wordsize +
+ if (!f || !f->mipsGotIndex.hasValue() || *f->mipsGotIndex == 0)
+ return ElfSym::mipsGp->getVA(0);
+ return getVA() + gots[*f->mipsGotIndex].startIndex * config->wordsize +
0x7ff0;
}
-void MipsGotSection::writeTo(uint8_t *Buf) {
+void MipsGotSection::writeTo(uint8_t *buf) {
// Set the MSB of the second GOT slot. This is not required by any
// MIPS ABI documentation, though.
//
@@ -1025,51 +1009,48 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
// we've been doing this for years, it is probably a safe bet to
// keep doing this for now. We really need to revisit this to see
// if we had to do this.
- writeUint(Buf + Config->Wordsize, (uint64_t)1 << (Config->Wordsize * 8 - 1));
- for (const FileGot &G : Gots) {
- auto Write = [&](size_t I, const Symbol *S, int64_t A) {
- uint64_t VA = A;
- if (S) {
- VA = S->getVA(A);
- if (S->StOther & STO_MIPS_MICROMIPS)
- VA |= 1;
- }
- writeUint(Buf + I * Config->Wordsize, VA);
+ writeUint(buf + config->wordsize, (uint64_t)1 << (config->wordsize * 8 - 1));
+ for (const FileGot &g : gots) {
+ auto write = [&](size_t i, const Symbol *s, int64_t a) {
+ uint64_t va = a;
+ if (s)
+ va = s->getVA(a);
+ writeUint(buf + i * config->wordsize, va);
};
// Write 'page address' entries to the local part of the GOT.
- for (const std::pair<const OutputSection *, FileGot::PageBlock> &L :
- G.PagesMap) {
- size_t PageCount = L.second.Count;
- uint64_t FirstPageAddr = getMipsPageAddr(L.first->Addr);
- for (size_t PI = 0; PI < PageCount; ++PI)
- Write(L.second.FirstIndex + PI, nullptr, FirstPageAddr + PI * 0x10000);
+ for (const std::pair<const OutputSection *, FileGot::PageBlock> &l :
+ g.pagesMap) {
+ size_t pageCount = l.second.count;
+ uint64_t firstPageAddr = getMipsPageAddr(l.first->addr);
+ for (size_t pi = 0; pi < pageCount; ++pi)
+ write(l.second.firstIndex + pi, nullptr, firstPageAddr + pi * 0x10000);
}
// Local, global, TLS, reloc-only entries.
// If TLS entry has a corresponding dynamic relocations, leave it
// initialized by zero. Write down adjusted TLS symbol's values otherwise.
// To calculate the adjustments use offsets for thread-local storage.
// https://www.linux-mips.org/wiki/NPTL
- for (const std::pair<GotEntry, size_t> &P : G.Local16)
- Write(P.second, P.first.first, P.first.second);
+ for (const std::pair<GotEntry, size_t> &p : g.local16)
+ write(p.second, p.first.first, p.first.second);
// Write VA to the primary GOT only. For secondary GOTs that
// will be done by REL32 dynamic relocations.
- if (&G == &Gots.front())
- for (const std::pair<const Symbol *, size_t> &P : G.Global)
- Write(P.second, P.first, 0);
- for (const std::pair<Symbol *, size_t> &P : G.Relocs)
- Write(P.second, P.first, 0);
- for (const std::pair<Symbol *, size_t> &P : G.Tls)
- Write(P.second, P.first, P.first->IsPreemptible ? 0 : -0x7000);
- for (const std::pair<Symbol *, size_t> &P : G.DynTlsSymbols) {
- if (P.first == nullptr && !Config->Pic)
- Write(P.second, nullptr, 1);
- else if (P.first && !P.first->IsPreemptible) {
+ if (&g == &gots.front())
+ for (const std::pair<const Symbol *, size_t> &p : g.global)
+ write(p.second, p.first, 0);
+ for (const std::pair<Symbol *, size_t> &p : g.relocs)
+ write(p.second, p.first, 0);
+ for (const std::pair<Symbol *, size_t> &p : g.tls)
+ write(p.second, p.first, p.first->isPreemptible ? 0 : -0x7000);
+ for (const std::pair<Symbol *, size_t> &p : g.dynTlsSymbols) {
+ if (p.first == nullptr && !config->isPic)
+ write(p.second, nullptr, 1);
+ else if (p.first && !p.first->isPreemptible) {
// If we are emitting PIC code with relocations we mustn't write
// anything to the GOT here. When using Elf_Rel relocations the value
// one will be treated as an addend and will cause crashes at runtime
- if (!Config->Pic)
- Write(P.second, nullptr, 1);
- Write(P.second + 1, P.first, -0x8000);
+ if (!config->isPic)
+ write(p.second, nullptr, 1);
+ write(p.second + 1, p.first, -0x8000);
}
}
}
@@ -1080,46 +1061,48 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
// section. I don't know why we have a BSS style type for the section but it is
// consitent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI.
GotPltSection::GotPltSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE,
- Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
- Target->GotPltEntrySize,
- Config->EMachine == EM_PPC64 ? ".plt" : ".got.plt") {}
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
+ ".got.plt") {
+ if (config->emachine == EM_PPC) {
+ name = ".plt";
+ } else if (config->emachine == EM_PPC64) {
+ type = SHT_NOBITS;
+ name = ".plt";
+ }
+}
-void GotPltSection::addEntry(Symbol &Sym) {
- assert(Sym.PltIndex == Entries.size());
- Entries.push_back(&Sym);
+void GotPltSection::addEntry(Symbol &sym) {
+ assert(sym.pltIndex == entries.size());
+ entries.push_back(&sym);
}
size_t GotPltSection::getSize() const {
- return (Target->GotPltHeaderEntriesNum + Entries.size()) *
- Target->GotPltEntrySize;
+ return (target->gotPltHeaderEntriesNum + entries.size()) * config->wordsize;
}
-void GotPltSection::writeTo(uint8_t *Buf) {
- Target->writeGotPltHeader(Buf);
- Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize;
- for (const Symbol *B : Entries) {
- Target->writeGotPlt(Buf, *B);
- Buf += Config->Wordsize;
+void GotPltSection::writeTo(uint8_t *buf) {
+ target->writeGotPltHeader(buf);
+ buf += target->gotPltHeaderEntriesNum * config->wordsize;
+ for (const Symbol *b : entries) {
+ target->writeGotPlt(buf, *b);
+ buf += config->wordsize;
}
}
-bool GotPltSection::empty() const {
- // We need to emit a GOT.PLT even if it's empty if there's a symbol that
- // references the _GLOBAL_OFFSET_TABLE_ and the Target defines the symbol
- // relative to the .got.plt section.
- return Entries.empty() &&
- !(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt);
+bool GotPltSection::isNeeded() const {
+ // We need to emit GOTPLT even if it's empty if there's a relocation relative
+ // to it.
+ return !entries.empty() || hasGotPltOffRel;
}
static StringRef getIgotPltName() {
// On ARM the IgotPltSection is part of the GotSection.
- if (Config->EMachine == EM_ARM)
+ if (config->emachine == EM_ARM)
return ".got";
// On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection
// needs to be named the same.
- if (Config->EMachine == EM_PPC64)
+ if (config->emachine == EM_PPC64)
return ".plt";
return ".got.plt";
@@ -1129,130 +1112,110 @@ static StringRef getIgotPltName() {
// with the IgotPltSection.
IgotPltSection::IgotPltSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE,
- Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
- Target->GotPltEntrySize, getIgotPltName()) {}
+ config->emachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
+ config->wordsize, getIgotPltName()) {}
-void IgotPltSection::addEntry(Symbol &Sym) {
- Sym.IsInIgot = true;
- assert(Sym.PltIndex == Entries.size());
- Entries.push_back(&Sym);
+void IgotPltSection::addEntry(Symbol &sym) {
+ assert(sym.pltIndex == entries.size());
+ entries.push_back(&sym);
}
size_t IgotPltSection::getSize() const {
- return Entries.size() * Target->GotPltEntrySize;
+ return entries.size() * config->wordsize;
}
-void IgotPltSection::writeTo(uint8_t *Buf) {
- for (const Symbol *B : Entries) {
- Target->writeIgotPlt(Buf, *B);
- Buf += Config->Wordsize;
+void IgotPltSection::writeTo(uint8_t *buf) {
+ for (const Symbol *b : entries) {
+ target->writeIgotPlt(buf, *b);
+ buf += config->wordsize;
}
}
-StringTableSection::StringTableSection(StringRef Name, bool Dynamic)
- : SyntheticSection(Dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, Name),
- Dynamic(Dynamic) {
+StringTableSection::StringTableSection(StringRef name, bool dynamic)
+ : SyntheticSection(dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, name),
+ dynamic(dynamic) {
// ELF string tables start with a NUL byte.
addString("");
}
-// Adds a string to the string table. If HashIt is true we hash and check for
+// Adds a string to the string table. If `hashIt` is true we hash and check for
// duplicates. It is optional because the name of global symbols are already
// uniqued and hashing them again has a big cost for a small value: uniquing
// them with some other string that happens to be the same.
-unsigned StringTableSection::addString(StringRef S, bool HashIt) {
- if (HashIt) {
- auto R = StringMap.insert(std::make_pair(S, this->Size));
- if (!R.second)
- return R.first->second;
+unsigned StringTableSection::addString(StringRef s, bool hashIt) {
+ if (hashIt) {
+ auto r = stringMap.insert(std::make_pair(s, this->size));
+ if (!r.second)
+ return r.first->second;
}
- unsigned Ret = this->Size;
- this->Size = this->Size + S.size() + 1;
- Strings.push_back(S);
- return Ret;
+ unsigned ret = this->size;
+ this->size = this->size + s.size() + 1;
+ strings.push_back(s);
+ return ret;
}
-void StringTableSection::writeTo(uint8_t *Buf) {
- for (StringRef S : Strings) {
- memcpy(Buf, S.data(), S.size());
- Buf[S.size()] = '\0';
- Buf += S.size() + 1;
+void StringTableSection::writeTo(uint8_t *buf) {
+ for (StringRef s : strings) {
+ memcpy(buf, s.data(), s.size());
+ buf[s.size()] = '\0';
+ buf += s.size() + 1;
}
}
// Returns the number of version definition entries. Because the first entry
// is for the version definition itself, it is the number of versioned symbols
// plus one. Note that we don't support multiple versions yet.
-static unsigned getVerDefNum() { return Config->VersionDefinitions.size() + 1; }
+static unsigned getVerDefNum() { return config->versionDefinitions.size() + 1; }
template <class ELFT>
DynamicSection<ELFT>::DynamicSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC, Config->Wordsize,
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC, config->wordsize,
".dynamic") {
- this->Entsize = ELFT::Is64Bits ? 16 : 8;
+ this->entsize = ELFT::Is64Bits ? 16 : 8;
// .dynamic section is not writable on MIPS and on Fuchsia OS
// which passes -z rodynamic.
// See "Special Section" in Chapter 4 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Config->EMachine == EM_MIPS || Config->ZRodynamic)
- this->Flags = SHF_ALLOC;
-
- // Add strings to .dynstr early so that .dynstr's size will be
- // fixed early.
- for (StringRef S : Config->FilterList)
- addInt(DT_FILTER, In.DynStrTab->addString(S));
- for (StringRef S : Config->AuxiliaryList)
- addInt(DT_AUXILIARY, In.DynStrTab->addString(S));
-
- if (!Config->Rpath.empty())
- addInt(Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
- In.DynStrTab->addString(Config->Rpath));
-
- for (InputFile *File : SharedFiles) {
- SharedFile<ELFT> *F = cast<SharedFile<ELFT>>(File);
- if (F->IsNeeded)
- addInt(DT_NEEDED, In.DynStrTab->addString(F->SoName));
- }
- if (!Config->SoName.empty())
- addInt(DT_SONAME, In.DynStrTab->addString(Config->SoName));
+ if (config->emachine == EM_MIPS || config->zRodynamic)
+ this->flags = SHF_ALLOC;
}
template <class ELFT>
-void DynamicSection<ELFT>::add(int32_t Tag, std::function<uint64_t()> Fn) {
- Entries.push_back({Tag, Fn});
+void DynamicSection<ELFT>::add(int32_t tag, std::function<uint64_t()> fn) {
+ entries.push_back({tag, fn});
}
template <class ELFT>
-void DynamicSection<ELFT>::addInt(int32_t Tag, uint64_t Val) {
- Entries.push_back({Tag, [=] { return Val; }});
+void DynamicSection<ELFT>::addInt(int32_t tag, uint64_t val) {
+ entries.push_back({tag, [=] { return val; }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addInSec(int32_t Tag, InputSection *Sec) {
- Entries.push_back({Tag, [=] { return Sec->getVA(0); }});
+void DynamicSection<ELFT>::addInSec(int32_t tag, InputSection *sec) {
+ entries.push_back({tag, [=] { return sec->getVA(0); }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addInSecRelative(int32_t Tag, InputSection *Sec) {
- size_t TagOffset = Entries.size() * Entsize;
- Entries.push_back(
- {Tag, [=] { return Sec->getVA(0) - (getVA() + TagOffset); }});
+void DynamicSection<ELFT>::addInSecRelative(int32_t tag, InputSection *sec) {
+ size_t tagOffset = entries.size() * entsize;
+ entries.push_back(
+ {tag, [=] { return sec->getVA(0) - (getVA() + tagOffset); }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addOutSec(int32_t Tag, OutputSection *Sec) {
- Entries.push_back({Tag, [=] { return Sec->Addr; }});
+void DynamicSection<ELFT>::addOutSec(int32_t tag, OutputSection *sec) {
+ entries.push_back({tag, [=] { return sec->addr; }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addSize(int32_t Tag, OutputSection *Sec) {
- Entries.push_back({Tag, [=] { return Sec->Size; }});
+void DynamicSection<ELFT>::addSize(int32_t tag, OutputSection *sec) {
+ entries.push_back({tag, [=] { return sec->size; }});
}
template <class ELFT>
-void DynamicSection<ELFT>::addSym(int32_t Tag, Symbol *Sym) {
- Entries.push_back({Tag, [=] { return Sym->getVA(); }});
+void DynamicSection<ELFT>::addSym(int32_t tag, Symbol *sym) {
+ entries.push_back({tag, [=] { return sym->getVA(); }});
}
// A Linker script may assign the RELA relocation sections to the same
@@ -1260,47 +1223,74 @@ void DynamicSection<ELFT>::addSym(int32_t Tag, Symbol *Sym) {
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
// overlap with the [DT_RELA, DT_RELA + DT_RELASZ).
static uint64_t addPltRelSz() {
- size_t Size = In.RelaPlt->getSize();
- if (In.RelaIplt->getParent() == In.RelaPlt->getParent() &&
- In.RelaIplt->Name == In.RelaPlt->Name)
- Size += In.RelaIplt->getSize();
- return Size;
+ size_t size = in.relaPlt->getSize();
+ if (in.relaIplt->getParent() == in.relaPlt->getParent() &&
+ in.relaIplt->name == in.relaPlt->name)
+ size += in.relaIplt->getSize();
+ return size;
}
// Add remaining entries to complete .dynamic contents.
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
+ elf::Partition &part = getPartition();
+ bool isMain = part.name.empty();
+
+ for (StringRef s : config->filterList)
+ addInt(DT_FILTER, part.dynStrTab->addString(s));
+ for (StringRef s : config->auxiliaryList)
+ addInt(DT_AUXILIARY, part.dynStrTab->addString(s));
+
+ if (!config->rpath.empty())
+ addInt(config->enableNewDtags ? DT_RUNPATH : DT_RPATH,
+ part.dynStrTab->addString(config->rpath));
+
+ for (SharedFile *file : sharedFiles)
+ if (file->isNeeded)
+ addInt(DT_NEEDED, part.dynStrTab->addString(file->soName));
+
+ if (isMain) {
+ if (!config->soName.empty())
+ addInt(DT_SONAME, part.dynStrTab->addString(config->soName));
+ } else {
+ if (!config->soName.empty())
+ addInt(DT_NEEDED, part.dynStrTab->addString(config->soName));
+ addInt(DT_SONAME, part.dynStrTab->addString(part.name));
+ }
+
// Set DT_FLAGS and DT_FLAGS_1.
- uint32_t DtFlags = 0;
- uint32_t DtFlags1 = 0;
- if (Config->Bsymbolic)
- DtFlags |= DF_SYMBOLIC;
- if (Config->ZGlobal)
- DtFlags1 |= DF_1_GLOBAL;
- if (Config->ZInitfirst)
- DtFlags1 |= DF_1_INITFIRST;
- if (Config->ZInterpose)
- DtFlags1 |= DF_1_INTERPOSE;
- if (Config->ZNodefaultlib)
- DtFlags1 |= DF_1_NODEFLIB;
- if (Config->ZNodelete)
- DtFlags1 |= DF_1_NODELETE;
- if (Config->ZNodlopen)
- DtFlags1 |= DF_1_NOOPEN;
- if (Config->ZNow) {
- DtFlags |= DF_BIND_NOW;
- DtFlags1 |= DF_1_NOW;
- }
- if (Config->ZOrigin) {
- DtFlags |= DF_ORIGIN;
- DtFlags1 |= DF_1_ORIGIN;
- }
- if (!Config->ZText)
- DtFlags |= DF_TEXTREL;
-
- if (DtFlags)
- addInt(DT_FLAGS, DtFlags);
- if (DtFlags1)
- addInt(DT_FLAGS_1, DtFlags1);
+ uint32_t dtFlags = 0;
+ uint32_t dtFlags1 = 0;
+ if (config->bsymbolic)
+ dtFlags |= DF_SYMBOLIC;
+ if (config->zGlobal)
+ dtFlags1 |= DF_1_GLOBAL;
+ if (config->zInitfirst)
+ dtFlags1 |= DF_1_INITFIRST;
+ if (config->zInterpose)
+ dtFlags1 |= DF_1_INTERPOSE;
+ if (config->zNodefaultlib)
+ dtFlags1 |= DF_1_NODEFLIB;
+ if (config->zNodelete)
+ dtFlags1 |= DF_1_NODELETE;
+ if (config->zNodlopen)
+ dtFlags1 |= DF_1_NOOPEN;
+ if (config->zNow) {
+ dtFlags |= DF_BIND_NOW;
+ dtFlags1 |= DF_1_NOW;
+ }
+ if (config->zOrigin) {
+ dtFlags |= DF_ORIGIN;
+ dtFlags1 |= DF_1_ORIGIN;
+ }
+ if (!config->zText)
+ dtFlags |= DF_TEXTREL;
+ if (config->hasStaticTlsModel)
+ dtFlags |= DF_STATIC_TLS;
+
+ if (dtFlags)
+ addInt(DT_FLAGS, dtFlags);
+ if (dtFlags1)
+ addInt(DT_FLAGS_1, dtFlags1);
// DT_DEBUG is a pointer to debug informaion used by debuggers at runtime. We
// need it for each process, so we don't write it for DSOs. The loader writes
@@ -1310,266 +1300,287 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
// systems (currently only Fuchsia OS) provide other means to give the
// debugger this information. Such systems may choose make .dynamic read-only.
// If the target is such a system (used -z rodynamic) don't write DT_DEBUG.
- if (!Config->Shared && !Config->Relocatable && !Config->ZRodynamic)
+ if (!config->shared && !config->relocatable && !config->zRodynamic)
addInt(DT_DEBUG, 0);
- if (OutputSection *Sec = In.DynStrTab->getParent())
- this->Link = Sec->SectionIndex;
+ if (OutputSection *sec = part.dynStrTab->getParent())
+ this->link = sec->sectionIndex;
- if (!In.RelaDyn->empty()) {
- addInSec(In.RelaDyn->DynamicTag, In.RelaDyn);
- addSize(In.RelaDyn->SizeDynamicTag, In.RelaDyn->getParent());
+ if (part.relaDyn->isNeeded()) {
+ addInSec(part.relaDyn->dynamicTag, part.relaDyn);
+ addSize(part.relaDyn->sizeDynamicTag, part.relaDyn->getParent());
- bool IsRela = Config->IsRela;
- addInt(IsRela ? DT_RELAENT : DT_RELENT,
- IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel));
+ bool isRela = config->isRela;
+ addInt(isRela ? DT_RELAENT : DT_RELENT,
+ isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel));
// MIPS dynamic loader does not support RELCOUNT tag.
// The problem is in the tight relation between dynamic
// relocations and GOT. So do not emit this tag on MIPS.
- if (Config->EMachine != EM_MIPS) {
- size_t NumRelativeRels = In.RelaDyn->getRelativeRelocCount();
- if (Config->ZCombreloc && NumRelativeRels)
- addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels);
+ if (config->emachine != EM_MIPS) {
+ size_t numRelativeRels = part.relaDyn->getRelativeRelocCount();
+ if (config->zCombreloc && numRelativeRels)
+ addInt(isRela ? DT_RELACOUNT : DT_RELCOUNT, numRelativeRels);
}
}
- if (In.RelrDyn && !In.RelrDyn->Relocs.empty()) {
- addInSec(Config->UseAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
- In.RelrDyn);
- addSize(Config->UseAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
- In.RelrDyn->getParent());
- addInt(Config->UseAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
+ if (part.relrDyn && !part.relrDyn->relocs.empty()) {
+ addInSec(config->useAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
+ part.relrDyn);
+ addSize(config->useAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
+ part.relrDyn->getParent());
+ addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
sizeof(Elf_Relr));
}
// .rel[a].plt section usually consists of two parts, containing plt and
// iplt relocations. It is possible to have only iplt relocations in the
- // output. In that case RelaPlt is empty and have zero offset, the same offset
- // as RelaIplt have. And we still want to emit proper dynamic tags for that
- // case, so here we always use RelaPlt as marker for the begining of
+ // output. In that case relaPlt is empty and have zero offset, the same offset
+ // as relaIplt has. And we still want to emit proper dynamic tags for that
+ // case, so here we always use relaPlt as marker for the begining of
// .rel[a].plt section.
- if (In.RelaPlt->getParent()->Live) {
- addInSec(DT_JMPREL, In.RelaPlt);
- Entries.push_back({DT_PLTRELSZ, addPltRelSz});
- switch (Config->EMachine) {
+ if (isMain && (in.relaPlt->isNeeded() || in.relaIplt->isNeeded())) {
+ addInSec(DT_JMPREL, in.relaPlt);
+ entries.push_back({DT_PLTRELSZ, addPltRelSz});
+ switch (config->emachine) {
case EM_MIPS:
- addInSec(DT_MIPS_PLTGOT, In.GotPlt);
+ addInSec(DT_MIPS_PLTGOT, in.gotPlt);
break;
case EM_SPARCV9:
- addInSec(DT_PLTGOT, In.Plt);
+ addInSec(DT_PLTGOT, in.plt);
break;
default:
- addInSec(DT_PLTGOT, In.GotPlt);
+ addInSec(DT_PLTGOT, in.gotPlt);
break;
}
- addInt(DT_PLTREL, Config->IsRela ? DT_RELA : DT_REL);
+ addInt(DT_PLTREL, config->isRela ? DT_RELA : DT_REL);
+ }
+
+ 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)
+ addInt(DT_AARCH64_PAC_PLT, 0);
}
- addInSec(DT_SYMTAB, In.DynSymTab);
+ addInSec(DT_SYMTAB, part.dynSymTab);
addInt(DT_SYMENT, sizeof(Elf_Sym));
- addInSec(DT_STRTAB, In.DynStrTab);
- addInt(DT_STRSZ, In.DynStrTab->getSize());
- if (!Config->ZText)
+ addInSec(DT_STRTAB, part.dynStrTab);
+ addInt(DT_STRSZ, part.dynStrTab->getSize());
+ if (!config->zText)
addInt(DT_TEXTREL, 0);
- if (In.GnuHashTab)
- addInSec(DT_GNU_HASH, In.GnuHashTab);
- if (In.HashTab)
- addInSec(DT_HASH, In.HashTab);
-
- if (Out::PreinitArray) {
- addOutSec(DT_PREINIT_ARRAY, Out::PreinitArray);
- addSize(DT_PREINIT_ARRAYSZ, Out::PreinitArray);
- }
- if (Out::InitArray) {
- addOutSec(DT_INIT_ARRAY, Out::InitArray);
- addSize(DT_INIT_ARRAYSZ, Out::InitArray);
- }
- if (Out::FiniArray) {
- addOutSec(DT_FINI_ARRAY, Out::FiniArray);
- addSize(DT_FINI_ARRAYSZ, Out::FiniArray);
- }
-
- if (Symbol *B = Symtab->find(Config->Init))
- if (B->isDefined())
- addSym(DT_INIT, B);
- if (Symbol *B = Symtab->find(Config->Fini))
- if (B->isDefined())
- addSym(DT_FINI, B);
-
- bool HasVerNeed = InX<ELFT>::VerNeed->getNeedNum() != 0;
- if (HasVerNeed || In.VerDef)
- addInSec(DT_VERSYM, InX<ELFT>::VerSym);
- if (In.VerDef) {
- addInSec(DT_VERDEF, In.VerDef);
+ if (part.gnuHashTab)
+ addInSec(DT_GNU_HASH, part.gnuHashTab);
+ if (part.hashTab)
+ addInSec(DT_HASH, part.hashTab);
+
+ if (isMain) {
+ if (Out::preinitArray) {
+ addOutSec(DT_PREINIT_ARRAY, Out::preinitArray);
+ addSize(DT_PREINIT_ARRAYSZ, Out::preinitArray);
+ }
+ if (Out::initArray) {
+ addOutSec(DT_INIT_ARRAY, Out::initArray);
+ addSize(DT_INIT_ARRAYSZ, Out::initArray);
+ }
+ if (Out::finiArray) {
+ addOutSec(DT_FINI_ARRAY, Out::finiArray);
+ addSize(DT_FINI_ARRAYSZ, Out::finiArray);
+ }
+
+ if (Symbol *b = symtab->find(config->init))
+ if (b->isDefined())
+ addSym(DT_INIT, b);
+ if (Symbol *b = symtab->find(config->fini))
+ if (b->isDefined())
+ addSym(DT_FINI, b);
+ }
+
+ bool hasVerNeed = SharedFile::vernauxNum != 0;
+ if (hasVerNeed || part.verDef)
+ addInSec(DT_VERSYM, part.verSym);
+ if (part.verDef) {
+ addInSec(DT_VERDEF, part.verDef);
addInt(DT_VERDEFNUM, getVerDefNum());
}
- if (HasVerNeed) {
- addInSec(DT_VERNEED, InX<ELFT>::VerNeed);
- addInt(DT_VERNEEDNUM, InX<ELFT>::VerNeed->getNeedNum());
+ if (hasVerNeed) {
+ addInSec(DT_VERNEED, part.verNeed);
+ unsigned needNum = 0;
+ for (SharedFile *f : sharedFiles)
+ if (!f->vernauxs.empty())
+ ++needNum;
+ addInt(DT_VERNEEDNUM, needNum);
}
- if (Config->EMachine == EM_MIPS) {
+ if (config->emachine == EM_MIPS) {
addInt(DT_MIPS_RLD_VERSION, 1);
addInt(DT_MIPS_FLAGS, RHF_NOTPOT);
- addInt(DT_MIPS_BASE_ADDRESS, Target->getImageBase());
- addInt(DT_MIPS_SYMTABNO, In.DynSymTab->getNumSymbols());
+ addInt(DT_MIPS_BASE_ADDRESS, target->getImageBase());
+ addInt(DT_MIPS_SYMTABNO, part.dynSymTab->getNumSymbols());
- add(DT_MIPS_LOCAL_GOTNO, [] { return In.MipsGot->getLocalEntriesNum(); });
+ add(DT_MIPS_LOCAL_GOTNO, [] { return in.mipsGot->getLocalEntriesNum(); });
- if (const Symbol *B = In.MipsGot->getFirstGlobalEntry())
- addInt(DT_MIPS_GOTSYM, B->DynsymIndex);
+ if (const Symbol *b = in.mipsGot->getFirstGlobalEntry())
+ addInt(DT_MIPS_GOTSYM, b->dynsymIndex);
else
- addInt(DT_MIPS_GOTSYM, In.DynSymTab->getNumSymbols());
- addInSec(DT_PLTGOT, In.MipsGot);
- if (In.MipsRldMap) {
- if (!Config->Pie)
- addInSec(DT_MIPS_RLD_MAP, In.MipsRldMap);
+ addInt(DT_MIPS_GOTSYM, part.dynSymTab->getNumSymbols());
+ addInSec(DT_PLTGOT, in.mipsGot);
+ if (in.mipsRldMap) {
+ if (!config->pie)
+ addInSec(DT_MIPS_RLD_MAP, in.mipsRldMap);
// Store the offset to the .rld_map section
// relative to the address of the tag.
- addInSecRelative(DT_MIPS_RLD_MAP_REL, In.MipsRldMap);
+ addInSecRelative(DT_MIPS_RLD_MAP_REL, in.mipsRldMap);
}
}
+ // DT_PPC_GOT indicates to glibc Secure PLT is used. If DT_PPC_GOT is absent,
+ // glibc assumes the old-style BSS PLT layout which we don't support.
+ if (config->emachine == EM_PPC)
+ add(DT_PPC_GOT, [] { return in.got->getVA(); });
+
// Glink dynamic tag is required by the V2 abi if the plt section isn't empty.
- if (Config->EMachine == EM_PPC64 && !In.Plt->empty()) {
+ if (config->emachine == EM_PPC64 && in.plt->isNeeded()) {
// The Glink tag points to 32 bytes before the first lazy symbol resolution
// stub, which starts directly after the header.
- Entries.push_back({DT_PPC64_GLINK, [=] {
- unsigned Offset = Target->PltHeaderSize - 32;
- return In.Plt->getVA(0) + Offset;
+ entries.push_back({DT_PPC64_GLINK, [=] {
+ unsigned offset = target->pltHeaderSize - 32;
+ return in.plt->getVA(0) + offset;
}});
}
addInt(DT_NULL, 0);
- getParent()->Link = this->Link;
- this->Size = Entries.size() * this->Entsize;
+ getParent()->link = this->link;
+ this->size = entries.size() * this->entsize;
}
-template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *P = reinterpret_cast<Elf_Dyn *>(Buf);
+template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *buf) {
+ auto *p = reinterpret_cast<Elf_Dyn *>(buf);
- for (std::pair<int32_t, std::function<uint64_t()>> &KV : Entries) {
- P->d_tag = KV.first;
- P->d_un.d_val = KV.second();
- ++P;
+ for (std::pair<int32_t, std::function<uint64_t()>> &kv : entries) {
+ p->d_tag = kv.first;
+ p->d_un.d_val = kv.second();
+ ++p;
}
}
uint64_t DynamicReloc::getOffset() const {
- return InputSec->getVA(OffsetInSec);
+ return inputSec->getVA(offsetInSec);
}
int64_t DynamicReloc::computeAddend() const {
- if (UseSymVA)
- return Sym->getVA(Addend);
- if (!OutputSec)
- return Addend;
+ if (useSymVA)
+ return sym->getVA(addend);
+ if (!outputSec)
+ return addend;
// See the comment in the DynamicReloc ctor.
- return getMipsPageAddr(OutputSec->Addr) + Addend;
+ return getMipsPageAddr(outputSec->addr) + addend;
}
-uint32_t DynamicReloc::getSymIndex() const {
- if (Sym && !UseSymVA)
- return Sym->DynsymIndex;
+uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
+ if (sym && !useSymVA)
+ return symTab->getSymbolIndex(sym);
return 0;
}
-RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type,
- int32_t DynamicTag,
- int32_t SizeDynamicTag)
- : SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name),
- DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {}
+RelocationBaseSection::RelocationBaseSection(StringRef name, uint32_t type,
+ int32_t dynamicTag,
+ int32_t sizeDynamicTag)
+ : SyntheticSection(SHF_ALLOC, type, config->wordsize, name),
+ dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag) {}
-void RelocationBaseSection::addReloc(RelType DynType, InputSectionBase *IS,
- uint64_t OffsetInSec, Symbol *Sym) {
- addReloc({DynType, IS, OffsetInSec, false, Sym, 0});
+void RelocationBaseSection::addReloc(RelType dynType, InputSectionBase *isec,
+ uint64_t offsetInSec, Symbol *sym) {
+ addReloc({dynType, isec, offsetInSec, false, sym, 0});
}
-void RelocationBaseSection::addReloc(RelType DynType,
- InputSectionBase *InputSec,
- uint64_t OffsetInSec, Symbol *Sym,
- int64_t Addend, RelExpr Expr,
- RelType Type) {
+void RelocationBaseSection::addReloc(RelType dynType,
+ InputSectionBase *inputSec,
+ uint64_t offsetInSec, Symbol *sym,
+ int64_t addend, RelExpr expr,
+ RelType type) {
// Write the addends to the relocated address if required. We skip
// it if the written value would be zero.
- if (Config->WriteAddends && (Expr != R_ADDEND || Addend != 0))
- InputSec->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
- addReloc({DynType, InputSec, OffsetInSec, Expr != R_ADDEND, Sym, Addend});
+ if (config->writeAddends && (expr != R_ADDEND || addend != 0))
+ inputSec->relocations.push_back({expr, type, offsetInSec, addend, sym});
+ addReloc({dynType, inputSec, offsetInSec, expr != R_ADDEND, sym, addend});
}
-void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) {
- if (Reloc.Type == Target->RelativeRel)
- ++NumRelativeRelocs;
- Relocs.push_back(Reloc);
+void RelocationBaseSection::addReloc(const DynamicReloc &reloc) {
+ if (reloc.type == target->relativeRel)
+ ++numRelativeRelocs;
+ relocs.push_back(reloc);
}
void RelocationBaseSection::finalizeContents() {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
+
// When linking glibc statically, .rel{,a}.plt contains R_*_IRELATIVE
// relocations due to IFUNC (e.g. strcpy). sh_link will be set to 0 in that
// case.
- InputSection *SymTab = Config->Relocatable ? In.SymTab : In.DynSymTab;
- if (SymTab && SymTab->getParent())
- getParent()->Link = SymTab->getParent()->SectionIndex;
+ if (symTab && symTab->getParent())
+ getParent()->link = symTab->getParent()->sectionIndex;
else
- getParent()->Link = 0;
+ getParent()->link = 0;
- if (In.RelaIplt == this || In.RelaPlt == this)
- getParent()->Info = In.GotPlt->getParent()->SectionIndex;
+ if (in.relaPlt == this)
+ getParent()->info = in.gotPlt->getParent()->sectionIndex;
+ if (in.relaIplt == this)
+ getParent()->info = in.igotPlt->getParent()->sectionIndex;
}
RelrBaseSection::RelrBaseSection()
: SyntheticSection(SHF_ALLOC,
- Config->UseAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
- Config->Wordsize, ".relr.dyn") {}
+ config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
+ config->wordsize, ".relr.dyn") {}
template <class ELFT>
-static void encodeDynamicReloc(typename ELFT::Rela *P,
- const DynamicReloc &Rel) {
- if (Config->IsRela)
- P->r_addend = Rel.computeAddend();
- P->r_offset = Rel.getOffset();
- P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
+static void encodeDynamicReloc(SymbolTableBaseSection *symTab,
+ typename ELFT::Rela *p,
+ const DynamicReloc &rel) {
+ if (config->isRela)
+ p->r_addend = rel.computeAddend();
+ p->r_offset = rel.getOffset();
+ p->setSymbolAndType(rel.getSymIndex(symTab), rel.type, config->isMips64EL);
}
template <class ELFT>
-RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
- : RelocationBaseSection(Name, Config->IsRela ? SHT_RELA : SHT_REL,
- Config->IsRela ? DT_RELA : DT_REL,
- Config->IsRela ? DT_RELASZ : DT_RELSZ),
- Sort(Sort) {
- this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+RelocationSection<ELFT>::RelocationSection(StringRef name, bool sort)
+ : RelocationBaseSection(name, config->isRela ? SHT_RELA : SHT_REL,
+ config->isRela ? DT_RELA : DT_REL,
+ config->isRela ? DT_RELASZ : DT_RELSZ),
+ sort(sort) {
+ this->entsize = config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
-static bool compRelocations(const DynamicReloc &A, const DynamicReloc &B) {
- bool AIsRel = A.Type == Target->RelativeRel;
- bool BIsRel = B.Type == Target->RelativeRel;
- if (AIsRel != BIsRel)
- return AIsRel;
- return A.getSymIndex() < B.getSymIndex();
-}
+template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *buf) {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
-template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
- if (Sort)
- std::stable_sort(Relocs.begin(), Relocs.end(), compRelocations);
+ // Sort by (!IsRelative,SymIndex,r_offset). DT_REL[A]COUNT requires us to
+ // place R_*_RELATIVE first. SymIndex is to improve locality, while r_offset
+ // is to make results easier to read.
+ if (sort)
+ llvm::stable_sort(
+ relocs, [&](const DynamicReloc &a, const DynamicReloc &b) {
+ return std::make_tuple(a.type != target->relativeRel,
+ a.getSymIndex(symTab), a.getOffset()) <
+ std::make_tuple(b.type != target->relativeRel,
+ b.getSymIndex(symTab), b.getOffset());
+ });
- for (const DynamicReloc &Rel : Relocs) {
- encodeDynamicReloc<ELFT>(reinterpret_cast<Elf_Rela *>(Buf), Rel);
- Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+ for (const DynamicReloc &rel : relocs) {
+ encodeDynamicReloc<ELFT>(symTab, reinterpret_cast<Elf_Rela *>(buf), rel);
+ buf += config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
}
-template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
- return this->Entsize * Relocs.size();
-}
-
template <class ELFT>
AndroidPackedRelocationSection<ELFT>::AndroidPackedRelocationSection(
- StringRef Name)
+ StringRef name)
: RelocationBaseSection(
- Name, Config->IsRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
- Config->IsRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
- Config->IsRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
- this->Entsize = 1;
+ name, config->isRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
+ config->isRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
+ config->isRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
+ this->entsize = 1;
}
template <class ELFT>
@@ -1619,32 +1630,32 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
// RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for
// this relocation.
- size_t OldSize = RelocData.size();
+ size_t oldSize = relocData.size();
- RelocData = {'A', 'P', 'S', '2'};
- raw_svector_ostream OS(RelocData);
- auto Add = [&](int64_t V) { encodeSLEB128(V, OS); };
+ relocData = {'A', 'P', 'S', '2'};
+ raw_svector_ostream os(relocData);
+ auto add = [&](int64_t v) { encodeSLEB128(v, os); };
// The format header includes the number of relocations and the initial
// offset (we set this to zero because the first relocation group will
// perform the initial adjustment).
- Add(Relocs.size());
- Add(0);
+ add(relocs.size());
+ add(0);
- std::vector<Elf_Rela> Relatives, NonRelatives;
+ std::vector<Elf_Rela> relatives, nonRelatives;
- for (const DynamicReloc &Rel : Relocs) {
- Elf_Rela R;
- encodeDynamicReloc<ELFT>(&R, Rel);
+ for (const DynamicReloc &rel : relocs) {
+ Elf_Rela r;
+ encodeDynamicReloc<ELFT>(getPartition().dynSymTab, &r, rel);
- if (R.getType(Config->IsMips64EL) == Target->RelativeRel)
- Relatives.push_back(R);
+ if (r.getType(config->isMips64EL) == target->relativeRel)
+ relatives.push_back(r);
else
- NonRelatives.push_back(R);
+ nonRelatives.push_back(r);
}
- llvm::sort(Relatives, [](const Elf_Rel &A, const Elf_Rel &B) {
- return A.r_offset < B.r_offset;
+ llvm::sort(relatives, [](const Elf_Rel &a, const Elf_Rel &b) {
+ return a.r_offset < b.r_offset;
});
// Try to find groups of relative relocations which are spaced one word
@@ -1653,108 +1664,108 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
// encoding, but each group will cost 7 bytes in addition to the offset from
// the previous group, so it is only profitable to do this for groups of
// size 8 or larger.
- std::vector<Elf_Rela> UngroupedRelatives;
- std::vector<std::vector<Elf_Rela>> RelativeGroups;
- for (auto I = Relatives.begin(), E = Relatives.end(); I != E;) {
- std::vector<Elf_Rela> Group;
+ std::vector<Elf_Rela> ungroupedRelatives;
+ std::vector<std::vector<Elf_Rela>> relativeGroups;
+ for (auto i = relatives.begin(), e = relatives.end(); i != e;) {
+ std::vector<Elf_Rela> group;
do {
- Group.push_back(*I++);
- } while (I != E && (I - 1)->r_offset + Config->Wordsize == I->r_offset);
+ group.push_back(*i++);
+ } while (i != e && (i - 1)->r_offset + config->wordsize == i->r_offset);
- if (Group.size() < 8)
- UngroupedRelatives.insert(UngroupedRelatives.end(), Group.begin(),
- Group.end());
+ if (group.size() < 8)
+ ungroupedRelatives.insert(ungroupedRelatives.end(), group.begin(),
+ group.end());
else
- RelativeGroups.emplace_back(std::move(Group));
+ relativeGroups.emplace_back(std::move(group));
}
- unsigned HasAddendIfRela =
- Config->IsRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
+ unsigned hasAddendIfRela =
+ config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
- uint64_t Offset = 0;
- uint64_t Addend = 0;
+ uint64_t offset = 0;
+ uint64_t addend = 0;
// Emit the run-length encoding for the groups of adjacent relative
// relocations. Each group is represented using two groups in the packed
// format. The first is used to set the current offset to the start of the
// group (and also encodes the first relocation), and the second encodes the
// remaining relocations.
- for (std::vector<Elf_Rela> &G : RelativeGroups) {
+ for (std::vector<Elf_Rela> &g : relativeGroups) {
// The first relocation in the group.
- Add(1);
- Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
- RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
- Add(G[0].r_offset - Offset);
- Add(Target->RelativeRel);
- if (Config->IsRela) {
- Add(G[0].r_addend - Addend);
- Addend = G[0].r_addend;
+ add(1);
+ add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
+ RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
+ add(g[0].r_offset - offset);
+ add(target->relativeRel);
+ if (config->isRela) {
+ add(g[0].r_addend - addend);
+ addend = g[0].r_addend;
}
// The remaining relocations.
- Add(G.size() - 1);
- Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
- RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
- Add(Config->Wordsize);
- Add(Target->RelativeRel);
- if (Config->IsRela) {
- for (auto I = G.begin() + 1, E = G.end(); I != E; ++I) {
- Add(I->r_addend - Addend);
- Addend = I->r_addend;
+ add(g.size() - 1);
+ add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
+ RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
+ add(config->wordsize);
+ add(target->relativeRel);
+ if (config->isRela) {
+ for (auto i = g.begin() + 1, e = g.end(); i != e; ++i) {
+ add(i->r_addend - addend);
+ addend = i->r_addend;
}
}
- Offset = G.back().r_offset;
+ offset = g.back().r_offset;
}
// Now the ungrouped relatives.
- if (!UngroupedRelatives.empty()) {
- Add(UngroupedRelatives.size());
- Add(RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
- Add(Target->RelativeRel);
- for (Elf_Rela &R : UngroupedRelatives) {
- Add(R.r_offset - Offset);
- Offset = R.r_offset;
- if (Config->IsRela) {
- Add(R.r_addend - Addend);
- Addend = R.r_addend;
+ if (!ungroupedRelatives.empty()) {
+ add(ungroupedRelatives.size());
+ add(RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela);
+ add(target->relativeRel);
+ for (Elf_Rela &r : ungroupedRelatives) {
+ add(r.r_offset - offset);
+ offset = r.r_offset;
+ if (config->isRela) {
+ add(r.r_addend - addend);
+ addend = r.r_addend;
}
}
}
// Finally the non-relative relocations.
- llvm::sort(NonRelatives, [](const Elf_Rela &A, const Elf_Rela &B) {
- return A.r_offset < B.r_offset;
+ llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
+ return a.r_offset < b.r_offset;
});
- if (!NonRelatives.empty()) {
- Add(NonRelatives.size());
- Add(HasAddendIfRela);
- for (Elf_Rela &R : NonRelatives) {
- Add(R.r_offset - Offset);
- Offset = R.r_offset;
- Add(R.r_info);
- if (Config->IsRela) {
- Add(R.r_addend - Addend);
- Addend = R.r_addend;
+ if (!nonRelatives.empty()) {
+ add(nonRelatives.size());
+ add(hasAddendIfRela);
+ for (Elf_Rela &r : nonRelatives) {
+ add(r.r_offset - offset);
+ offset = r.r_offset;
+ add(r.r_info);
+ if (config->isRela) {
+ add(r.r_addend - addend);
+ addend = r.r_addend;
}
}
}
// Don't allow the section to shrink; otherwise the size of the section can
// oscillate infinitely.
- if (RelocData.size() < OldSize)
- RelocData.append(OldSize - RelocData.size(), 0);
+ if (relocData.size() < oldSize)
+ relocData.append(oldSize - relocData.size(), 0);
// Returns whether the section size changed. We need to keep recomputing both
// section layout and the contents of this section until the size converges
// because changing this section's size can affect section layout, which in
// turn can affect the sizes of the LEB-encoded integers stored in this
// section.
- return RelocData.size() != OldSize;
+ return relocData.size() != oldSize;
}
template <class ELFT> RelrSection<ELFT>::RelrSection() {
- this->Entsize = Config->Wordsize;
+ this->entsize = config->wordsize;
}
template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
@@ -1788,90 +1799,90 @@ template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
// even means address, odd means bitmap.
// 2. Just a simple list of addresses is a valid encoding.
- size_t OldSize = RelrRelocs.size();
- RelrRelocs.clear();
+ size_t oldSize = relrRelocs.size();
+ relrRelocs.clear();
// Same as Config->Wordsize but faster because this is a compile-time
// constant.
- const size_t Wordsize = sizeof(typename ELFT::uint);
+ const size_t wordsize = sizeof(typename ELFT::uint);
// Number of bits to use for the relocation offsets bitmap.
// Must be either 63 or 31.
- const size_t NBits = Wordsize * 8 - 1;
+ const size_t nBits = wordsize * 8 - 1;
// Get offsets for all relative relocations and sort them.
- std::vector<uint64_t> Offsets;
- for (const RelativeReloc &Rel : Relocs)
- Offsets.push_back(Rel.getOffset());
- llvm::sort(Offsets.begin(), Offsets.end());
+ std::vector<uint64_t> offsets;
+ for (const RelativeReloc &rel : relocs)
+ offsets.push_back(rel.getOffset());
+ llvm::sort(offsets);
// For each leading relocation, find following ones that can be folded
// as a bitmap and fold them.
- for (size_t I = 0, E = Offsets.size(); I < E;) {
+ for (size_t i = 0, e = offsets.size(); i < e;) {
// Add a leading relocation.
- RelrRelocs.push_back(Elf_Relr(Offsets[I]));
- uint64_t Base = Offsets[I] + Wordsize;
- ++I;
+ relrRelocs.push_back(Elf_Relr(offsets[i]));
+ uint64_t base = offsets[i] + wordsize;
+ ++i;
// Find foldable relocations to construct bitmaps.
- while (I < E) {
- uint64_t Bitmap = 0;
+ while (i < e) {
+ uint64_t bitmap = 0;
- while (I < E) {
- uint64_t Delta = Offsets[I] - Base;
+ while (i < e) {
+ uint64_t delta = offsets[i] - base;
// If it is too far, it cannot be folded.
- if (Delta >= NBits * Wordsize)
+ if (delta >= nBits * wordsize)
break;
// If it is not a multiple of wordsize away, it cannot be folded.
- if (Delta % Wordsize)
+ if (delta % wordsize)
break;
// Fold it.
- Bitmap |= 1ULL << (Delta / Wordsize);
- ++I;
+ bitmap |= 1ULL << (delta / wordsize);
+ ++i;
}
- if (!Bitmap)
+ if (!bitmap)
break;
- RelrRelocs.push_back(Elf_Relr((Bitmap << 1) | 1));
- Base += NBits * Wordsize;
+ relrRelocs.push_back(Elf_Relr((bitmap << 1) | 1));
+ base += nBits * wordsize;
}
}
- return RelrRelocs.size() != OldSize;
+ return relrRelocs.size() != oldSize;
}
-SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec)
- : SyntheticSection(StrTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0,
- StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
- Config->Wordsize,
- StrTabSec.isDynamic() ? ".dynsym" : ".symtab"),
- StrTabSec(StrTabSec) {}
+SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &strTabSec)
+ : SyntheticSection(strTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0,
+ strTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
+ config->wordsize,
+ strTabSec.isDynamic() ? ".dynsym" : ".symtab"),
+ strTabSec(strTabSec) {}
// Orders symbols according to their positions in the GOT,
// in compliance with MIPS ABI rules.
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-static bool sortMipsSymbols(const SymbolTableEntry &L,
- const SymbolTableEntry &R) {
+static bool sortMipsSymbols(const SymbolTableEntry &l,
+ const SymbolTableEntry &r) {
// Sort entries related to non-local preemptible symbols by GOT indexes.
// All other entries go to the beginning of a dynsym in arbitrary order.
- if (L.Sym->isInGot() && R.Sym->isInGot())
- return L.Sym->GotIndex < R.Sym->GotIndex;
- if (!L.Sym->isInGot() && !R.Sym->isInGot())
+ if (l.sym->isInGot() && r.sym->isInGot())
+ return l.sym->gotIndex < r.sym->gotIndex;
+ if (!l.sym->isInGot() && !r.sym->isInGot())
return false;
- return !L.Sym->isInGot();
+ return !l.sym->isInGot();
}
void SymbolTableBaseSection::finalizeContents() {
- if (OutputSection *Sec = StrTabSec.getParent())
- getParent()->Link = Sec->SectionIndex;
+ if (OutputSection *sec = strTabSec.getParent())
+ getParent()->link = sec->sectionIndex;
- if (this->Type != SHT_DYNSYM) {
+ if (this->type != SHT_DYNSYM) {
sortSymTabSymbols();
return;
}
@@ -1881,18 +1892,22 @@ void SymbolTableBaseSection::finalizeContents() {
// Section's Info field has the index of the first non-local symbol.
// Because the first symbol entry is a null entry, 1 is the first.
- getParent()->Info = 1;
+ getParent()->info = 1;
- if (In.GnuHashTab) {
+ if (getPartition().gnuHashTab) {
// NB: It also sorts Symbols to meet the GNU hash table requirements.
- In.GnuHashTab->addSymbols(Symbols);
- } else if (Config->EMachine == EM_MIPS) {
- std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols);
+ getPartition().gnuHashTab->addSymbols(symbols);
+ } else if (config->emachine == EM_MIPS) {
+ llvm::stable_sort(symbols, sortMipsSymbols);
}
- size_t I = 0;
- for (const SymbolTableEntry &S : Symbols)
- S.Sym->DynsymIndex = ++I;
+ // Only the main partition's dynsym indexes are stored in the symbols
+ // themselves. All other partitions use a lookup table.
+ if (this == mainPart->dynSymTab) {
+ size_t i = 0;
+ for (const SymbolTableEntry &s : symbols)
+ s.sym->dynsymIndex = ++i;
+ }
}
// The ELF spec requires that all local symbols precede global symbols, so we
@@ -1904,193 +1919,210 @@ void SymbolTableBaseSection::finalizeContents() {
// coming from.
void SymbolTableBaseSection::sortSymTabSymbols() {
// Move all local symbols before global symbols.
- auto E = std::stable_partition(
- Symbols.begin(), Symbols.end(), [](const SymbolTableEntry &S) {
- return S.Sym->isLocal() || S.Sym->computeBinding() == STB_LOCAL;
+ auto e = std::stable_partition(
+ symbols.begin(), symbols.end(), [](const SymbolTableEntry &s) {
+ return s.sym->isLocal() || s.sym->computeBinding() == STB_LOCAL;
});
- size_t NumLocals = E - Symbols.begin();
- getParent()->Info = NumLocals + 1;
+ size_t numLocals = e - symbols.begin();
+ getParent()->info = numLocals + 1;
// We want to group the local symbols by file. For that we rebuild the local
// part of the symbols vector. We do not need to care about the STT_FILE
// symbols, they are already naturally placed first in each group. That
// happens because STT_FILE is always the first symbol in the object and hence
// precede all other local symbols we add for a file.
- MapVector<InputFile *, std::vector<SymbolTableEntry>> Arr;
- for (const SymbolTableEntry &S : llvm::make_range(Symbols.begin(), E))
- Arr[S.Sym->File].push_back(S);
+ MapVector<InputFile *, std::vector<SymbolTableEntry>> arr;
+ for (const SymbolTableEntry &s : llvm::make_range(symbols.begin(), e))
+ arr[s.sym->file].push_back(s);
- auto I = Symbols.begin();
- for (std::pair<InputFile *, std::vector<SymbolTableEntry>> &P : Arr)
- for (SymbolTableEntry &Entry : P.second)
- *I++ = Entry;
+ auto i = symbols.begin();
+ for (std::pair<InputFile *, std::vector<SymbolTableEntry>> &p : arr)
+ for (SymbolTableEntry &entry : p.second)
+ *i++ = entry;
}
-void SymbolTableBaseSection::addSymbol(Symbol *B) {
+void SymbolTableBaseSection::addSymbol(Symbol *b) {
// Adding a local symbol to a .dynsym is a bug.
- assert(this->Type != SHT_DYNSYM || !B->isLocal());
+ assert(this->type != SHT_DYNSYM || !b->isLocal());
- bool HashIt = B->isLocal();
- Symbols.push_back({B, StrTabSec.addString(B->getName(), HashIt)});
+ bool hashIt = b->isLocal();
+ symbols.push_back({b, strTabSec.addString(b->getName(), hashIt)});
}
-size_t SymbolTableBaseSection::getSymbolIndex(Symbol *Sym) {
- // Initializes symbol lookup tables lazily. This is used only
- // for -r or -emit-relocs.
- llvm::call_once(OnceFlag, [&] {
- SymbolIndexMap.reserve(Symbols.size());
- size_t I = 0;
- for (const SymbolTableEntry &E : Symbols) {
- if (E.Sym->Type == STT_SECTION)
- SectionIndexMap[E.Sym->getOutputSection()] = ++I;
+size_t SymbolTableBaseSection::getSymbolIndex(Symbol *sym) {
+ if (this == mainPart->dynSymTab)
+ return sym->dynsymIndex;
+
+ // Initializes symbol lookup tables lazily. This is used only for -r,
+ // -emit-relocs and dynsyms in partitions other than the main one.
+ llvm::call_once(onceFlag, [&] {
+ symbolIndexMap.reserve(symbols.size());
+ size_t i = 0;
+ for (const SymbolTableEntry &e : symbols) {
+ if (e.sym->type == STT_SECTION)
+ sectionIndexMap[e.sym->getOutputSection()] = ++i;
else
- SymbolIndexMap[E.Sym] = ++I;
+ symbolIndexMap[e.sym] = ++i;
}
});
// Section symbols are mapped based on their output sections
// to maintain their semantics.
- if (Sym->Type == STT_SECTION)
- return SectionIndexMap.lookup(Sym->getOutputSection());
- return SymbolIndexMap.lookup(Sym);
+ if (sym->type == STT_SECTION)
+ return sectionIndexMap.lookup(sym->getOutputSection());
+ return symbolIndexMap.lookup(sym);
}
template <class ELFT>
-SymbolTableSection<ELFT>::SymbolTableSection(StringTableSection &StrTabSec)
- : SymbolTableBaseSection(StrTabSec) {
- this->Entsize = sizeof(Elf_Sym);
+SymbolTableSection<ELFT>::SymbolTableSection(StringTableSection &strTabSec)
+ : SymbolTableBaseSection(strTabSec) {
+ this->entsize = sizeof(Elf_Sym);
}
-static BssSection *getCommonSec(Symbol *Sym) {
- if (!Config->DefineCommon)
- if (auto *D = dyn_cast<Defined>(Sym))
- return dyn_cast_or_null<BssSection>(D->Section);
+static BssSection *getCommonSec(Symbol *sym) {
+ if (!config->defineCommon)
+ if (auto *d = dyn_cast<Defined>(sym))
+ return dyn_cast_or_null<BssSection>(d->section);
return nullptr;
}
-static uint32_t getSymSectionIndex(Symbol *Sym) {
- if (getCommonSec(Sym))
+static uint32_t getSymSectionIndex(Symbol *sym) {
+ if (getCommonSec(sym))
return SHN_COMMON;
- if (!isa<Defined>(Sym) || Sym->NeedsPltAddr)
+ if (!isa<Defined>(sym) || sym->needsPltAddr)
return SHN_UNDEF;
- if (const OutputSection *OS = Sym->getOutputSection())
- return OS->SectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX
- : OS->SectionIndex;
+ if (const OutputSection *os = sym->getOutputSection())
+ return os->sectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX
+ : os->sectionIndex;
return SHN_ABS;
}
// Write the internal symbol table contents to the output symbol table.
-template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
+template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *buf) {
// The first entry is a null entry as per the ELF spec.
- memset(Buf, 0, sizeof(Elf_Sym));
- Buf += sizeof(Elf_Sym);
+ memset(buf, 0, sizeof(Elf_Sym));
+ buf += sizeof(Elf_Sym);
- auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+ auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
- for (SymbolTableEntry &Ent : Symbols) {
- Symbol *Sym = Ent.Sym;
+ for (SymbolTableEntry &ent : symbols) {
+ Symbol *sym = ent.sym;
+ bool isDefinedHere = type == SHT_SYMTAB || sym->partition == partition;
// Set st_info and st_other.
- ESym->st_other = 0;
- if (Sym->isLocal()) {
- ESym->setBindingAndType(STB_LOCAL, Sym->Type);
+ eSym->st_other = 0;
+ if (sym->isLocal()) {
+ eSym->setBindingAndType(STB_LOCAL, sym->type);
} else {
- ESym->setBindingAndType(Sym->computeBinding(), Sym->Type);
- ESym->setVisibility(Sym->Visibility);
+ eSym->setBindingAndType(sym->computeBinding(), sym->type);
+ eSym->setVisibility(sym->visibility);
}
- ESym->st_name = Ent.StrTabOffset;
- ESym->st_shndx = getSymSectionIndex(Ent.Sym);
+ // The 3 most significant bits of st_other are used by OpenPOWER ABI.
+ // See getPPC64GlobalEntryToLocalEntryOffset() for more details.
+ if (config->emachine == EM_PPC64)
+ eSym->st_other |= sym->stOther & 0xe0;
+
+ eSym->st_name = ent.strTabOffset;
+ if (isDefinedHere)
+ eSym->st_shndx = getSymSectionIndex(ent.sym);
+ else
+ eSym->st_shndx = 0;
// Copy symbol size if it is a defined symbol. st_size is not significant
// for undefined symbols, so whether copying it or not is up to us if that's
// the case. We'll leave it as zero because by not setting a value, we can
// get the exact same outputs for two sets of input files that differ only
// in undefined symbol size in DSOs.
- if (ESym->st_shndx == SHN_UNDEF)
- ESym->st_size = 0;
+ if (eSym->st_shndx == SHN_UNDEF || !isDefinedHere)
+ eSym->st_size = 0;
else
- ESym->st_size = Sym->getSize();
+ 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
// occur if -r is given).
- if (BssSection *CommonSec = getCommonSec(Ent.Sym))
- ESym->st_value = CommonSec->Alignment;
+ if (BssSection *commonSec = getCommonSec(ent.sym))
+ eSym->st_value = commonSec->alignment;
+ else if (isDefinedHere)
+ eSym->st_value = sym->getVA();
else
- ESym->st_value = Sym->getVA();
+ eSym->st_value = 0;
- ++ESym;
+ ++eSym;
}
// On MIPS we need to mark symbol which has a PLT entry and requires
// pointer equality by STO_MIPS_PLT flag. That is necessary to help
// dynamic linker distinguish such symbols and MIPS lazy-binding stubs.
// https://sourceware.org/ml/binutils/2008-07/txt00000.txt
- if (Config->EMachine == EM_MIPS) {
- auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+ if (config->emachine == EM_MIPS) {
+ auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
- for (SymbolTableEntry &Ent : Symbols) {
- Symbol *Sym = Ent.Sym;
- if (Sym->isInPlt() && Sym->NeedsPltAddr)
- ESym->st_other |= STO_MIPS_PLT;
+ for (SymbolTableEntry &ent : symbols) {
+ Symbol *sym = ent.sym;
+ if (sym->isInPlt() && sym->needsPltAddr)
+ eSym->st_other |= STO_MIPS_PLT;
if (isMicroMips()) {
- // Set STO_MIPS_MICROMIPS flag and less-significant bit for
- // a defined microMIPS symbol and symbol should point to its
- // PLT entry (in case of microMIPS, PLT entries always contain
- // microMIPS code).
- if (Sym->isDefined() &&
- ((Sym->StOther & STO_MIPS_MICROMIPS) || Sym->NeedsPltAddr)) {
- if (StrTabSec.isDynamic())
- ESym->st_value |= 1;
- ESym->st_other |= STO_MIPS_MICROMIPS;
+ // 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
+ // clear that bit for non-dynamic symbol table, so tools
+ // like `objdump` will be able to deal with a correct
+ // symbol position.
+ if (sym->isDefined() &&
+ ((sym->stOther & STO_MIPS_MICROMIPS) || sym->needsPltAddr)) {
+ if (!strTabSec.isDynamic())
+ eSym->st_value &= ~1;
+ eSym->st_other |= STO_MIPS_MICROMIPS;
}
}
- if (Config->Relocatable)
- if (auto *D = dyn_cast<Defined>(Sym))
- if (isMipsPIC<ELFT>(D))
- ESym->st_other |= STO_MIPS_PIC;
- ++ESym;
+ if (config->relocatable)
+ if (auto *d = dyn_cast<Defined>(sym))
+ if (isMipsPIC<ELFT>(d))
+ eSym->st_other |= STO_MIPS_PIC;
+ ++eSym;
}
}
}
SymtabShndxSection::SymtabShndxSection()
- : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndxr") {
- this->Entsize = 4;
+ : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndx") {
+ this->entsize = 4;
}
-void SymtabShndxSection::writeTo(uint8_t *Buf) {
+void SymtabShndxSection::writeTo(uint8_t *buf) {
// We write an array of 32 bit values, where each value has 1:1 association
// with an entry in .symtab. If the corresponding entry contains SHN_XINDEX,
// we need to write actual index, otherwise, we must write SHN_UNDEF(0).
- Buf += 4; // Ignore .symtab[0] entry.
- for (const SymbolTableEntry &Entry : In.SymTab->getSymbols()) {
- if (getSymSectionIndex(Entry.Sym) == SHN_XINDEX)
- write32(Buf, Entry.Sym->getOutputSection()->SectionIndex);
- Buf += 4;
+ buf += 4; // Ignore .symtab[0] entry.
+ for (const SymbolTableEntry &entry : in.symTab->getSymbols()) {
+ if (getSymSectionIndex(entry.sym) == SHN_XINDEX)
+ write32(buf, entry.sym->getOutputSection()->sectionIndex);
+ buf += 4;
}
}
-bool SymtabShndxSection::empty() const {
+bool SymtabShndxSection::isNeeded() const {
// SHT_SYMTAB can hold symbols with section indices values up to
// SHN_LORESERVE. If we need more, we want to use extension SHT_SYMTAB_SHNDX
// section. Problem is that we reveal the final section indices a bit too
// late, and we do not know them here. For simplicity, we just always create
- // a .symtab_shndxr section when the amount of output sections is huge.
- size_t Size = 0;
- for (BaseCommand *Base : Script->SectionCommands)
- if (isa<OutputSection>(Base))
- ++Size;
- return Size < SHN_LORESERVE;
+ // a .symtab_shndx section when the amount of output sections is huge.
+ size_t size = 0;
+ for (BaseCommand *base : script->sectionCommands)
+ if (isa<OutputSection>(base))
+ ++size;
+ return size >= SHN_LORESERVE;
}
void SymtabShndxSection::finalizeContents() {
- getParent()->Link = In.SymTab->getParent()->SectionIndex;
+ getParent()->link = in.symTab->getParent()->sectionIndex;
}
size_t SymtabShndxSection::getSize() const {
- return In.SymTab->getNumSymbols() * 4;
+ return in.symTab->getNumSymbols() * 4;
}
// .hash and .gnu.hash sections contain on-disk hash tables that map
@@ -2125,45 +2157,45 @@ size_t SymtabShndxSection::getSize() const {
// about .gnu.hash, you want to specify -hash-style=gnu. Otherwise, a
// safe bet is to specify -hash-style=both for backward compatibilty.
GnuHashTableSection::GnuHashTableSection()
- : SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, Config->Wordsize, ".gnu.hash") {
+ : SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, config->wordsize, ".gnu.hash") {
}
void GnuHashTableSection::finalizeContents() {
- if (OutputSection *Sec = In.DynSymTab->getParent())
- getParent()->Link = Sec->SectionIndex;
+ if (OutputSection *sec = getPartition().dynSymTab->getParent())
+ getParent()->link = sec->sectionIndex;
// Computes bloom filter size in word size. We want to allocate 12
// bits for each symbol. It must be a power of two.
- if (Symbols.empty()) {
- MaskWords = 1;
+ if (symbols.empty()) {
+ maskWords = 1;
} else {
- uint64_t NumBits = Symbols.size() * 12;
- MaskWords = NextPowerOf2(NumBits / (Config->Wordsize * 8));
+ uint64_t numBits = symbols.size() * 12;
+ maskWords = NextPowerOf2(numBits / (config->wordsize * 8));
}
- Size = 16; // Header
- Size += Config->Wordsize * MaskWords; // Bloom filter
- Size += NBuckets * 4; // Hash buckets
- Size += Symbols.size() * 4; // Hash values
+ size = 16; // Header
+ size += config->wordsize * maskWords; // Bloom filter
+ size += nBuckets * 4; // Hash buckets
+ size += symbols.size() * 4; // Hash values
}
-void GnuHashTableSection::writeTo(uint8_t *Buf) {
+void GnuHashTableSection::writeTo(uint8_t *buf) {
// The output buffer is not guaranteed to be zero-cleared because we pre-
// fill executable sections with trap instructions. This is a precaution
// for that case, which happens only when -no-rosegment is given.
- memset(Buf, 0, Size);
+ memset(buf, 0, size);
// Write a header.
- write32(Buf, NBuckets);
- write32(Buf + 4, In.DynSymTab->getNumSymbols() - Symbols.size());
- write32(Buf + 8, MaskWords);
- write32(Buf + 12, Shift2);
- Buf += 16;
+ write32(buf, nBuckets);
+ write32(buf + 4, getPartition().dynSymTab->getNumSymbols() - symbols.size());
+ write32(buf + 8, maskWords);
+ write32(buf + 12, Shift2);
+ buf += 16;
// Write a bloom filter and a hash table.
- writeBloomFilter(Buf);
- Buf += Config->Wordsize * MaskWords;
- writeHashTable(Buf);
+ writeBloomFilter(buf);
+ buf += config->wordsize * maskWords;
+ writeHashTable(buf);
}
// This function writes a 2-bit bloom filter. This bloom filter alone
@@ -2173,57 +2205,58 @@ void GnuHashTableSection::writeTo(uint8_t *Buf) {
//
// [1] Ulrich Drepper (2011), "How To Write Shared Libraries" (Ver. 4.1.2),
// p.9, https://www.akkadia.org/drepper/dsohowto.pdf
-void GnuHashTableSection::writeBloomFilter(uint8_t *Buf) {
- unsigned C = Config->Is64 ? 64 : 32;
- for (const Entry &Sym : Symbols) {
+void GnuHashTableSection::writeBloomFilter(uint8_t *buf) {
+ unsigned c = config->is64 ? 64 : 32;
+ for (const Entry &sym : symbols) {
// When C = 64, we choose a word with bits [6:...] and set 1 to two bits in
// the word using bits [0:5] and [26:31].
- size_t I = (Sym.Hash / C) & (MaskWords - 1);
- uint64_t Val = readUint(Buf + I * Config->Wordsize);
- Val |= uint64_t(1) << (Sym.Hash % C);
- Val |= uint64_t(1) << ((Sym.Hash >> Shift2) % C);
- writeUint(Buf + I * Config->Wordsize, Val);
+ size_t i = (sym.hash / c) & (maskWords - 1);
+ uint64_t val = readUint(buf + i * config->wordsize);
+ val |= uint64_t(1) << (sym.hash % c);
+ val |= uint64_t(1) << ((sym.hash >> Shift2) % c);
+ writeUint(buf + i * config->wordsize, val);
}
}
-void GnuHashTableSection::writeHashTable(uint8_t *Buf) {
- uint32_t *Buckets = reinterpret_cast<uint32_t *>(Buf);
- uint32_t OldBucket = -1;
- uint32_t *Values = Buckets + NBuckets;
- for (auto I = Symbols.begin(), E = Symbols.end(); I != E; ++I) {
+void GnuHashTableSection::writeHashTable(uint8_t *buf) {
+ uint32_t *buckets = reinterpret_cast<uint32_t *>(buf);
+ uint32_t oldBucket = -1;
+ uint32_t *values = buckets + nBuckets;
+ for (auto i = symbols.begin(), e = symbols.end(); i != e; ++i) {
// Write a hash value. It represents a sequence of chains that share the
// same hash modulo value. The last element of each chain is terminated by
// LSB 1.
- uint32_t Hash = I->Hash;
- bool IsLastInChain = (I + 1) == E || I->BucketIdx != (I + 1)->BucketIdx;
- Hash = IsLastInChain ? Hash | 1 : Hash & ~1;
- write32(Values++, Hash);
+ uint32_t hash = i->hash;
+ bool isLastInChain = (i + 1) == e || i->bucketIdx != (i + 1)->bucketIdx;
+ hash = isLastInChain ? hash | 1 : hash & ~1;
+ write32(values++, hash);
- if (I->BucketIdx == OldBucket)
+ if (i->bucketIdx == oldBucket)
continue;
// Write a hash bucket. Hash buckets contain indices in the following hash
// value table.
- write32(Buckets + I->BucketIdx, I->Sym->DynsymIndex);
- OldBucket = I->BucketIdx;
+ write32(buckets + i->bucketIdx,
+ getPartition().dynSymTab->getSymbolIndex(i->sym));
+ oldBucket = i->bucketIdx;
}
}
-static uint32_t hashGnu(StringRef Name) {
- uint32_t H = 5381;
- for (uint8_t C : Name)
- H = (H << 5) + H + C;
- return H;
+static uint32_t hashGnu(StringRef name) {
+ uint32_t h = 5381;
+ for (uint8_t c : name)
+ h = (h << 5) + h + c;
+ return h;
}
// Add symbols to this symbol hash table. Note that this function
// destructively sort a given vector -- which is needed because
// GNU-style hash table places some sorting requirements.
-void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
+void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &v) {
// We cannot use 'auto' for Mid because GCC 6.1 cannot deduce
// its type correctly.
- std::vector<SymbolTableEntry>::iterator Mid =
- std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) {
- return !S.Sym->isDefined();
+ std::vector<SymbolTableEntry>::iterator mid =
+ std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
+ return !s.sym->isDefined() || s.sym->partition != partition;
});
// We chose load factor 4 for the on-disk hash table. For each hash
@@ -2235,138 +2268,143 @@ void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
// Android loader as of 2018 doesn't like a .gnu.hash containing such
// table. If that's the case, we create a hash table with one unused
// dummy slot.
- NBuckets = std::max<size_t>((V.end() - Mid) / 4, 1);
+ nBuckets = std::max<size_t>((v.end() - mid) / 4, 1);
- if (Mid == V.end())
+ if (mid == v.end())
return;
- for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) {
- Symbol *B = Ent.Sym;
- uint32_t Hash = hashGnu(B->getName());
- uint32_t BucketIdx = Hash % NBuckets;
- Symbols.push_back({B, Ent.StrTabOffset, Hash, BucketIdx});
+ for (SymbolTableEntry &ent : llvm::make_range(mid, v.end())) {
+ Symbol *b = ent.sym;
+ uint32_t hash = hashGnu(b->getName());
+ uint32_t bucketIdx = hash % nBuckets;
+ symbols.push_back({b, ent.strTabOffset, hash, bucketIdx});
}
- std::stable_sort(
- Symbols.begin(), Symbols.end(),
- [](const Entry &L, const Entry &R) { return L.BucketIdx < R.BucketIdx; });
+ llvm::stable_sort(symbols, [](const Entry &l, const Entry &r) {
+ return l.bucketIdx < r.bucketIdx;
+ });
- V.erase(Mid, V.end());
- for (const Entry &Ent : Symbols)
- V.push_back({Ent.Sym, Ent.StrTabOffset});
+ v.erase(mid, v.end());
+ for (const Entry &ent : symbols)
+ v.push_back({ent.sym, ent.strTabOffset});
}
HashTableSection::HashTableSection()
: SyntheticSection(SHF_ALLOC, SHT_HASH, 4, ".hash") {
- this->Entsize = 4;
+ this->entsize = 4;
}
void HashTableSection::finalizeContents() {
- if (OutputSection *Sec = In.DynSymTab->getParent())
- getParent()->Link = Sec->SectionIndex;
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
- unsigned NumEntries = 2; // nbucket and nchain.
- NumEntries += In.DynSymTab->getNumSymbols(); // The chain entries.
+ if (OutputSection *sec = symTab->getParent())
+ getParent()->link = sec->sectionIndex;
+
+ unsigned numEntries = 2; // nbucket and nchain.
+ numEntries += symTab->getNumSymbols(); // The chain entries.
// Create as many buckets as there are symbols.
- NumEntries += In.DynSymTab->getNumSymbols();
- this->Size = NumEntries * 4;
+ numEntries += symTab->getNumSymbols();
+ this->size = numEntries * 4;
}
-void HashTableSection::writeTo(uint8_t *Buf) {
+void HashTableSection::writeTo(uint8_t *buf) {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab;
+
// See comment in GnuHashTableSection::writeTo.
- memset(Buf, 0, Size);
+ memset(buf, 0, size);
- unsigned NumSymbols = In.DynSymTab->getNumSymbols();
+ unsigned numSymbols = symTab->getNumSymbols();
- uint32_t *P = reinterpret_cast<uint32_t *>(Buf);
- write32(P++, NumSymbols); // nbucket
- write32(P++, NumSymbols); // nchain
+ uint32_t *p = reinterpret_cast<uint32_t *>(buf);
+ write32(p++, numSymbols); // nbucket
+ write32(p++, numSymbols); // nchain
- uint32_t *Buckets = P;
- uint32_t *Chains = P + NumSymbols;
+ uint32_t *buckets = p;
+ uint32_t *chains = p + numSymbols;
- for (const SymbolTableEntry &S : In.DynSymTab->getSymbols()) {
- Symbol *Sym = S.Sym;
- StringRef Name = Sym->getName();
- unsigned I = Sym->DynsymIndex;
- uint32_t Hash = hashSysV(Name) % NumSymbols;
- Chains[I] = Buckets[Hash];
- write32(Buckets + Hash, I);
+ for (const SymbolTableEntry &s : symTab->getSymbols()) {
+ Symbol *sym = s.sym;
+ StringRef name = sym->getName();
+ unsigned i = sym->dynsymIndex;
+ uint32_t hash = hashSysV(name) % numSymbols;
+ chains[i] = buckets[hash];
+ write32(buckets + hash, i);
}
}
// On PowerPC64 the lazy symbol resolvers go into the `global linkage table`
// in the .glink section, rather then the typical .plt section.
-PltSection::PltSection(bool IsIplt)
- : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
- Config->EMachine == EM_PPC64 ? ".glink" : ".plt"),
- HeaderSize(!IsIplt || Config->ZRetpolineplt ? Target->PltHeaderSize : 0),
- IsIplt(IsIplt) {
+PltSection::PltSection(bool isIplt)
+ : SyntheticSection(
+ SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
+ (config->emachine == EM_PPC || config->emachine == EM_PPC64)
+ ? ".glink"
+ : ".plt"),
+ headerSize(!isIplt || config->zRetpolineplt ? target->pltHeaderSize : 0),
+ isIplt(isIplt) {
// The PLT needs to be writable on SPARC as the dynamic linker will
// modify the instructions in the PLT entries.
- if (Config->EMachine == EM_SPARCV9)
- this->Flags |= SHF_WRITE;
+ if (config->emachine == EM_SPARCV9)
+ this->flags |= SHF_WRITE;
}
-void PltSection::writeTo(uint8_t *Buf) {
+void PltSection::writeTo(uint8_t *buf) {
+ if (config->emachine == EM_PPC) {
+ writePPC32GlinkSection(buf, entries.size());
+ return;
+ }
+
// At beginning of PLT or retpoline IPLT, we have code to call the dynamic
// linker to resolve dynsyms at runtime. Write such code.
- if (HeaderSize > 0)
- Target->writePltHeader(Buf);
- size_t Off = HeaderSize;
- // The IPlt is immediately after the Plt, account for this in RelOff
- unsigned PltOff = getPltRelocOff();
+ if (headerSize)
+ target->writePltHeader(buf);
+ size_t off = headerSize;
+
+ RelocationBaseSection *relSec = isIplt ? in.relaIplt : in.relaPlt;
- for (auto &I : Entries) {
- const Symbol *B = I.first;
- unsigned RelOff = I.second + PltOff;
- uint64_t Got = B->getGotPltVA();
- uint64_t Plt = this->getVA() + Off;
- Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff);
- Off += Target->PltEntrySize;
+ // The IPlt is immediately after the Plt, account for this in relOff
+ size_t pltOff = isIplt ? in.plt->getSize() : 0;
+
+ for (size_t i = 0, e = entries.size(); i != e; ++i) {
+ const Symbol *b = entries[i];
+ unsigned relOff = relSec->entsize * i + pltOff;
+ uint64_t got = b->getGotPltVA();
+ uint64_t plt = this->getVA() + off;
+ target->writePlt(buf + off, got, plt, b->pltIndex, relOff);
+ off += target->pltEntrySize;
}
}
-template <class ELFT> void PltSection::addEntry(Symbol &Sym) {
- Sym.PltIndex = Entries.size();
- RelocationBaseSection *PltRelocSection = In.RelaPlt;
- if (IsIplt) {
- PltRelocSection = In.RelaIplt;
- Sym.IsInIplt = true;
- }
- unsigned RelOff =
- static_cast<RelocationSection<ELFT> *>(PltRelocSection)->getRelocOffset();
- Entries.push_back(std::make_pair(&Sym, RelOff));
+template <class ELFT> void PltSection::addEntry(Symbol &sym) {
+ sym.pltIndex = entries.size();
+ entries.push_back(&sym);
}
size_t PltSection::getSize() const {
- return HeaderSize + Entries.size() * Target->PltEntrySize;
+ return headerSize + entries.size() * target->pltEntrySize;
}
// Some architectures such as additional symbols in the PLT section. For
// example ARM uses mapping symbols to aid disassembly
void PltSection::addSymbols() {
// The PLT may have symbols defined for the Header, the IPLT has no header
- if (!IsIplt)
- Target->addPltHeaderSymbols(*this);
- size_t Off = HeaderSize;
- for (size_t I = 0; I < Entries.size(); ++I) {
- Target->addPltSymbols(*this, Off);
- Off += Target->PltEntrySize;
- }
-}
+ if (!isIplt)
+ target->addPltHeaderSymbols(*this);
-unsigned PltSection::getPltRelocOff() const {
- return IsIplt ? In.Plt->getSize() : 0;
+ size_t off = headerSize;
+ for (size_t i = 0; i < entries.size(); ++i) {
+ target->addPltSymbols(*this, off);
+ off += target->pltEntrySize;
+ }
}
// The string hash function for .gdb_index.
-static uint32_t computeGdbHash(StringRef S) {
- uint32_t H = 0;
- for (uint8_t C : S)
- H = H * 67 + toLower(C) - 113;
- return H;
+static uint32_t computeGdbHash(StringRef s) {
+ uint32_t h = 0;
+ for (uint8_t c : s)
+ h = h * 67 + toLower(c) - 113;
+ return h;
}
GdbIndexSection::GdbIndexSection()
@@ -2375,367 +2413,384 @@ GdbIndexSection::GdbIndexSection()
// Returns the desired size of an on-disk hash table for a .gdb_index section.
// There's a tradeoff between size and collision rate. We aim 75% utilization.
size_t GdbIndexSection::computeSymtabSize() const {
- return std::max<size_t>(NextPowerOf2(Symbols.size() * 4 / 3), 1024);
+ return std::max<size_t>(NextPowerOf2(symbols.size() * 4 / 3), 1024);
}
// Compute the output section size.
void GdbIndexSection::initOutputSize() {
- Size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8;
+ size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8;
- for (GdbChunk &Chunk : Chunks)
- Size += Chunk.CompilationUnits.size() * 16 + Chunk.AddressAreas.size() * 20;
+ for (GdbChunk &chunk : chunks)
+ size += chunk.compilationUnits.size() * 16 + chunk.addressAreas.size() * 20;
// Add the constant pool size if exists.
- if (!Symbols.empty()) {
- GdbSymbol &Sym = Symbols.back();
- Size += Sym.NameOff + Sym.Name.size() + 1;
+ if (!symbols.empty()) {
+ GdbSymbol &sym = symbols.back();
+ size += sym.nameOff + sym.name.size() + 1;
}
}
static std::vector<InputSection *> getDebugInfoSections() {
- std::vector<InputSection *> Ret;
- for (InputSectionBase *S : InputSections)
- if (InputSection *IS = dyn_cast<InputSection>(S))
- if (IS->Name == ".debug_info")
- Ret.push_back(IS);
- return Ret;
+ std::vector<InputSection *> ret;
+ for (InputSectionBase *s : inputSections)
+ if (InputSection *isec = dyn_cast<InputSection>(s))
+ if (isec->name == ".debug_info")
+ ret.push_back(isec);
+ return ret;
}
-static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &Dwarf) {
- std::vector<GdbIndexSection::CuEntry> Ret;
- for (std::unique_ptr<DWARFUnit> &Cu : Dwarf.compile_units())
- Ret.push_back({Cu->getOffset(), Cu->getLength() + 4});
- return Ret;
+static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &dwarf) {
+ std::vector<GdbIndexSection::CuEntry> ret;
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units())
+ ret.push_back({cu->getOffset(), cu->getLength() + 4});
+ return ret;
}
static std::vector<GdbIndexSection::AddressEntry>
-readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) {
- std::vector<GdbIndexSection::AddressEntry> Ret;
-
- uint32_t CuIdx = 0;
- for (std::unique_ptr<DWARFUnit> &Cu : Dwarf.compile_units()) {
- Expected<DWARFAddressRangesVector> Ranges = Cu->collectAddressRanges();
- if (!Ranges) {
- error(toString(Sec) + ": " + toString(Ranges.takeError()));
+readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
+ std::vector<GdbIndexSection::AddressEntry> ret;
+
+ uint32_t cuIdx = 0;
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
+ Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
+ if (!ranges) {
+ error(toString(sec) + ": " + toString(ranges.takeError()));
return {};
}
- ArrayRef<InputSectionBase *> Sections = Sec->File->getSections();
- for (DWARFAddressRange &R : *Ranges) {
- InputSectionBase *S = Sections[R.SectionIndex];
- if (!S || S == &InputSection::Discarded || !S->Live)
+ ArrayRef<InputSectionBase *> sections = sec->file->getSections();
+ 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)
+ if (r.LowPC == r.HighPC)
continue;
- auto *IS = cast<InputSection>(S);
- uint64_t Offset = IS->getOffsetInFile();
- Ret.push_back({IS, R.LowPC - Offset, R.HighPC - Offset, CuIdx});
+ auto *isec = cast<InputSection>(s);
+ uint64_t offset = isec->getOffsetInFile();
+ ret.push_back({isec, r.LowPC - offset, r.HighPC - offset, cuIdx});
}
- ++CuIdx;
+ ++cuIdx;
}
- return Ret;
+ return ret;
}
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();
-
- std::vector<GdbIndexSection::NameAttrEntry> Ret;
- for (const DWARFSection *Pub : {&PubNames, &PubTypes}) {
- DWARFDebugPubTable Table(Obj, *Pub, Config->IsLE, true);
- for (const DWARFDebugPubTable::Set &Set : Table.getData()) {
- // The value written into the constant pool is Kind << 24 | CuIndex. As we
+readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
+ const std::vector<GdbIndexSection::CuEntry> &cUs) {
+ const DWARFSection &pubNames = obj.getGnuPubNamesSection();
+ const DWARFSection &pubTypes = obj.getGnuPubTypesSection();
+
+ std::vector<GdbIndexSection::NameAttrEntry> ret;
+ for (const DWARFSection *pub : {&pubNames, &pubTypes}) {
+ DWARFDebugPubTable table(obj, *pub, config->isLE, true);
+ 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
- // CuIndex, we compute (Kind << 24 | CuIndexInThisObject) instead, and add
+ // cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
// the number of preceding compilation units later.
- uint32_t I =
- lower_bound(CUs, Set.Offset,
- [](GdbIndexSection::CuEntry CU, uint32_t Offset) {
- return CU.CuOffset < Offset;
+ uint32_t i =
+ lower_bound(cUs, set.Offset,
+ [](GdbIndexSection::CuEntry cu, uint32_t offset) {
+ return cu.cuOffset < offset;
}) -
- CUs.begin();
- for (const DWARFDebugPubTable::Entry &Ent : Set.Entries)
- Ret.push_back({{Ent.Name, computeGdbHash(Ent.Name)},
- (Ent.Descriptor.toBits() << 24) | I});
+ cUs.begin();
+ for (const DWARFDebugPubTable::Entry &ent : set.Entries)
+ ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
+ (ent.Descriptor.toBits() << 24) | i});
}
}
- return Ret;
+ return ret;
}
// Create a list of symbols from a given list of symbol names and types
// by uniquifying them by name.
static std::vector<GdbIndexSection::GdbSymbol>
-createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> NameAttrs,
- const std::vector<GdbIndexSection::GdbChunk> &Chunks) {
- typedef GdbIndexSection::GdbSymbol GdbSymbol;
- typedef GdbIndexSection::NameAttrEntry NameAttrEntry;
+createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> nameAttrs,
+ const std::vector<GdbIndexSection::GdbChunk> &chunks) {
+ using GdbSymbol = GdbIndexSection::GdbSymbol;
+ using NameAttrEntry = GdbIndexSection::NameAttrEntry;
// For each chunk, compute the number of compilation units preceding it.
- uint32_t CuIdx = 0;
- std::vector<uint32_t> CuIdxs(Chunks.size());
- for (uint32_t I = 0, E = Chunks.size(); I != E; ++I) {
- CuIdxs[I] = CuIdx;
- CuIdx += Chunks[I].CompilationUnits.size();
+ uint32_t cuIdx = 0;
+ std::vector<uint32_t> cuIdxs(chunks.size());
+ for (uint32_t i = 0, e = chunks.size(); i != e; ++i) {
+ cuIdxs[i] = cuIdx;
+ cuIdx += chunks[i].compilationUnits.size();
}
// 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);
+ size_t numShards = 32;
+ size_t concurrency = 1;
+ if (threadsEnabled)
+ concurrency =
+ std::min<size_t>(PowerOf2Floor(hardware_concurrency()), numShards);
// A sharded map to uniquify symbols by name.
- std::vector<DenseMap<CachedHashStringRef, size_t>> Map(NumShards);
- size_t Shift = 32 - countTrailingZeros(NumShards);
+ std::vector<DenseMap<CachedHashStringRef, size_t>> map(numShards);
+ size_t shift = 32 - countTrailingZeros(numShards);
// Instantiate GdbSymbols while uniqufying them by name.
- std::vector<std::vector<GdbSymbol>> Symbols(NumShards);
- parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
- uint32_t I = 0;
- for (ArrayRef<NameAttrEntry> Entries : NameAttrs) {
- for (const NameAttrEntry &Ent : Entries) {
- size_t ShardId = Ent.Name.hash() >> Shift;
- if ((ShardId & (Concurrency - 1)) != ThreadId)
+ std::vector<std::vector<GdbSymbol>> symbols(numShards);
+ parallelForEachN(0, concurrency, [&](size_t threadId) {
+ uint32_t i = 0;
+ for (ArrayRef<NameAttrEntry> entries : nameAttrs) {
+ for (const NameAttrEntry &ent : entries) {
+ size_t shardId = ent.name.hash() >> shift;
+ if ((shardId & (concurrency - 1)) != threadId)
continue;
- uint32_t V = Ent.CuIndexAndAttrs + CuIdxs[I];
- size_t &Idx = Map[ShardId][Ent.Name];
- if (Idx) {
- Symbols[ShardId][Idx - 1].CuVector.push_back(V);
+ uint32_t v = ent.cuIndexAndAttrs + cuIdxs[i];
+ size_t &idx = map[shardId][ent.name];
+ if (idx) {
+ symbols[shardId][idx - 1].cuVector.push_back(v);
continue;
}
- Idx = Symbols[ShardId].size() + 1;
- Symbols[ShardId].push_back({Ent.Name, {V}, 0, 0});
+ idx = symbols[shardId].size() + 1;
+ symbols[shardId].push_back({ent.name, {v}, 0, 0});
}
- ++I;
+ ++i;
}
});
- size_t NumSymbols = 0;
- for (ArrayRef<GdbSymbol> V : Symbols)
- NumSymbols += V.size();
+ size_t numSymbols = 0;
+ for (ArrayRef<GdbSymbol> v : symbols)
+ numSymbols += v.size();
// The return type is a flattened vector, so we'll copy each vector
// contents to Ret.
- std::vector<GdbSymbol> Ret;
- Ret.reserve(NumSymbols);
- for (std::vector<GdbSymbol> &Vec : Symbols)
- for (GdbSymbol &Sym : Vec)
- Ret.push_back(std::move(Sym));
+ std::vector<GdbSymbol> ret;
+ ret.reserve(numSymbols);
+ for (std::vector<GdbSymbol> &vec : symbols)
+ for (GdbSymbol &sym : vec)
+ ret.push_back(std::move(sym));
// CU vectors and symbol names are adjacent in the output file.
// We can compute their offsets in the output file now.
- size_t Off = 0;
- for (GdbSymbol &Sym : Ret) {
- Sym.CuVectorOff = Off;
- Off += (Sym.CuVector.size() + 1) * 4;
+ size_t off = 0;
+ for (GdbSymbol &sym : ret) {
+ sym.cuVectorOff = off;
+ off += (sym.cuVector.size() + 1) * 4;
}
- for (GdbSymbol &Sym : Ret) {
- Sym.NameOff = Off;
- Off += Sym.Name.size() + 1;
+ for (GdbSymbol &sym : ret) {
+ sym.nameOff = off;
+ off += sym.name.size() + 1;
}
- return Ret;
+ return ret;
}
// Returns a newly-created .gdb_index section.
template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
- std::vector<InputSection *> Sections = getDebugInfoSections();
+ std::vector<InputSection *> sections = getDebugInfoSections();
// .debug_gnu_pub{names,types} are useless in executables.
// They are present in input object files solely for creating
// a .gdb_index. So we can remove them from the output.
- for (InputSectionBase *S : InputSections)
- if (S->Name == ".debug_gnu_pubnames" || S->Name == ".debug_gnu_pubtypes")
- S->Live = false;
-
- std::vector<GdbChunk> Chunks(Sections.size());
- std::vector<std::vector<NameAttrEntry>> NameAttrs(Sections.size());
-
- parallelForEachN(0, Sections.size(), [&](size_t I) {
- ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>();
- DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File));
-
- Chunks[I].Sec = Sections[I];
- Chunks[I].CompilationUnits = readCuList(Dwarf);
- Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]);
- NameAttrs[I] = readPubNamesAndTypes<ELFT>(
- static_cast<const LLDDwarfObj<ELFT> &>(Dwarf.getDWARFObj()),
- Chunks[I].CompilationUnits);
+ for (InputSectionBase *s : inputSections)
+ if (s->name == ".debug_gnu_pubnames" || s->name == ".debug_gnu_pubtypes")
+ s->markDead();
+
+ std::vector<GdbChunk> chunks(sections.size());
+ std::vector<std::vector<NameAttrEntry>> nameAttrs(sections.size());
+
+ parallelForEachN(0, sections.size(), [&](size_t i) {
+ ObjFile<ELFT> *file = sections[i]->getFile<ELFT>();
+ DWARFContext dwarf(make_unique<LLDDwarfObj<ELFT>>(file));
+
+ chunks[i].sec = sections[i];
+ chunks[i].compilationUnits = readCuList(dwarf);
+ chunks[i].addressAreas = readAddressAreas(dwarf, sections[i]);
+ nameAttrs[i] = readPubNamesAndTypes<ELFT>(
+ static_cast<const LLDDwarfObj<ELFT> &>(dwarf.getDWARFObj()),
+ chunks[i].compilationUnits);
});
- auto *Ret = make<GdbIndexSection>();
- Ret->Chunks = std::move(Chunks);
- Ret->Symbols = createSymbols(NameAttrs, Ret->Chunks);
- Ret->initOutputSize();
- return Ret;
+ auto *ret = make<GdbIndexSection>();
+ ret->chunks = std::move(chunks);
+ ret->symbols = createSymbols(nameAttrs, ret->chunks);
+ ret->initOutputSize();
+ return ret;
}
-void GdbIndexSection::writeTo(uint8_t *Buf) {
+void GdbIndexSection::writeTo(uint8_t *buf) {
// Write the header.
- auto *Hdr = reinterpret_cast<GdbIndexHeader *>(Buf);
- uint8_t *Start = Buf;
- Hdr->Version = 7;
- Buf += sizeof(*Hdr);
+ auto *hdr = reinterpret_cast<GdbIndexHeader *>(buf);
+ uint8_t *start = buf;
+ hdr->version = 7;
+ buf += sizeof(*hdr);
// Write the CU list.
- Hdr->CuListOff = Buf - Start;
- for (GdbChunk &Chunk : Chunks) {
- for (CuEntry &Cu : Chunk.CompilationUnits) {
- write64le(Buf, Chunk.Sec->OutSecOff + Cu.CuOffset);
- write64le(Buf + 8, Cu.CuLength);
- Buf += 16;
+ hdr->cuListOff = buf - start;
+ for (GdbChunk &chunk : chunks) {
+ for (CuEntry &cu : chunk.compilationUnits) {
+ write64le(buf, chunk.sec->outSecOff + cu.cuOffset);
+ write64le(buf + 8, cu.cuLength);
+ buf += 16;
}
}
// Write the address area.
- Hdr->CuTypesOff = Buf - Start;
- Hdr->AddressAreaOff = Buf - Start;
- uint32_t CuOff = 0;
- for (GdbChunk &Chunk : Chunks) {
- for (AddressEntry &E : Chunk.AddressAreas) {
- uint64_t BaseAddr = E.Section->getVA(0);
- write64le(Buf, BaseAddr + E.LowAddress);
- write64le(Buf + 8, BaseAddr + E.HighAddress);
- write32le(Buf + 16, E.CuIndex + CuOff);
- Buf += 20;
+ hdr->cuTypesOff = buf - start;
+ hdr->addressAreaOff = buf - start;
+ uint32_t cuOff = 0;
+ for (GdbChunk &chunk : chunks) {
+ for (AddressEntry &e : chunk.addressAreas) {
+ uint64_t baseAddr = e.section->getVA(0);
+ write64le(buf, baseAddr + e.lowAddress);
+ write64le(buf + 8, baseAddr + e.highAddress);
+ write32le(buf + 16, e.cuIndex + cuOff);
+ buf += 20;
}
- CuOff += Chunk.CompilationUnits.size();
+ cuOff += chunk.compilationUnits.size();
}
// Write the on-disk open-addressing hash table containing symbols.
- Hdr->SymtabOff = Buf - Start;
- size_t SymtabSize = computeSymtabSize();
- uint32_t Mask = SymtabSize - 1;
+ hdr->symtabOff = buf - start;
+ size_t symtabSize = computeSymtabSize();
+ uint32_t mask = symtabSize - 1;
- for (GdbSymbol &Sym : Symbols) {
- uint32_t H = Sym.Name.hash();
- uint32_t I = H & Mask;
- uint32_t Step = ((H * 17) & Mask) | 1;
+ for (GdbSymbol &sym : symbols) {
+ uint32_t h = sym.name.hash();
+ uint32_t i = h & mask;
+ uint32_t step = ((h * 17) & mask) | 1;
- while (read32le(Buf + I * 8))
- I = (I + Step) & Mask;
+ while (read32le(buf + i * 8))
+ i = (i + step) & mask;
- write32le(Buf + I * 8, Sym.NameOff);
- write32le(Buf + I * 8 + 4, Sym.CuVectorOff);
+ write32le(buf + i * 8, sym.nameOff);
+ write32le(buf + i * 8 + 4, sym.cuVectorOff);
}
- Buf += SymtabSize * 8;
+ buf += symtabSize * 8;
// Write the string pool.
- Hdr->ConstantPoolOff = Buf - Start;
- parallelForEach(Symbols, [&](GdbSymbol &Sym) {
- memcpy(Buf + Sym.NameOff, Sym.Name.data(), Sym.Name.size());
+ hdr->constantPoolOff = buf - start;
+ parallelForEach(symbols, [&](GdbSymbol &sym) {
+ memcpy(buf + sym.nameOff, sym.name.data(), sym.name.size());
});
// Write the CU vectors.
- for (GdbSymbol &Sym : Symbols) {
- write32le(Buf, Sym.CuVector.size());
- Buf += 4;
- for (uint32_t Val : Sym.CuVector) {
- write32le(Buf, Val);
- Buf += 4;
+ for (GdbSymbol &sym : symbols) {
+ write32le(buf, sym.cuVector.size());
+ buf += 4;
+ for (uint32_t val : sym.cuVector) {
+ write32le(buf, val);
+ buf += 4;
}
}
}
-bool GdbIndexSection::empty() const { return Chunks.empty(); }
+bool GdbIndexSection::isNeeded() const { return !chunks.empty(); }
EhFrameHeader::EhFrameHeader()
: SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {}
+void EhFrameHeader::writeTo(uint8_t *buf) {
+ // Unlike most sections, the EhFrameHeader section is written while writing
+ // another section, namely EhFrameSection, which calls the write() function
+ // below from its writeTo() function. This is necessary because the contents
+ // of EhFrameHeader depend on the relocated contents of EhFrameSection and we
+ // don't know which order the sections will be written in.
+}
+
// .eh_frame_hdr contains a binary search table of pointers to FDEs.
// Each entry of the search table consists of two values,
// the starting PC from where FDEs covers, and the FDE's address.
// It is sorted by PC.
-void EhFrameHeader::writeTo(uint8_t *Buf) {
- typedef EhFrameSection::FdeData FdeData;
+void EhFrameHeader::write() {
+ uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff;
+ using FdeData = EhFrameSection::FdeData;
- std::vector<FdeData> Fdes = In.EhFrame->getFdeData();
+ std::vector<FdeData> fdes = getPartition().ehFrame->getFdeData();
- Buf[0] = 1;
- Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
- Buf[2] = DW_EH_PE_udata4;
- Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
- write32(Buf + 4, In.EhFrame->getParent()->Addr - this->getVA() - 4);
- write32(Buf + 8, Fdes.size());
- Buf += 12;
+ buf[0] = 1;
+ buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+ buf[2] = DW_EH_PE_udata4;
+ buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+ write32(buf + 4,
+ getPartition().ehFrame->getParent()->addr - this->getVA() - 4);
+ write32(buf + 8, fdes.size());
+ buf += 12;
- for (FdeData &Fde : Fdes) {
- write32(Buf, Fde.PcRel);
- write32(Buf + 4, Fde.FdeVARel);
- Buf += 8;
+ for (FdeData &fde : fdes) {
+ write32(buf, fde.pcRel);
+ write32(buf + 4, fde.fdeVARel);
+ buf += 8;
}
}
size_t EhFrameHeader::getSize() const {
// .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
- return 12 + In.EhFrame->NumFdes * 8;
+ return 12 + getPartition().ehFrame->numFdes * 8;
}
-bool EhFrameHeader::empty() const { return In.EhFrame->empty(); }
+bool EhFrameHeader::isNeeded() const {
+ return isLive() && getPartition().ehFrame->isNeeded();
+}
VersionDefinitionSection::VersionDefinitionSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_verdef, sizeof(uint32_t),
".gnu.version_d") {}
-static StringRef getFileDefName() {
- if (!Config->SoName.empty())
- return Config->SoName;
- return Config->OutputFile;
+StringRef VersionDefinitionSection::getFileDefName() {
+ if (!getPartition().name.empty())
+ return getPartition().name;
+ if (!config->soName.empty())
+ return config->soName;
+ return config->outputFile;
}
void VersionDefinitionSection::finalizeContents() {
- FileDefNameOff = In.DynStrTab->addString(getFileDefName());
- for (VersionDefinition &V : Config->VersionDefinitions)
- V.NameOff = In.DynStrTab->addString(V.Name);
+ fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName());
+ for (VersionDefinition &v : config->versionDefinitions)
+ verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name));
- if (OutputSection *Sec = In.DynStrTab->getParent())
- getParent()->Link = Sec->SectionIndex;
+ if (OutputSection *sec = getPartition().dynStrTab->getParent())
+ getParent()->link = sec->sectionIndex;
// sh_info should be set to the number of definitions. This fact is missed in
// documentation, but confirmed by binutils community:
// https://sourceware.org/ml/binutils/2014-11/msg00355.html
- getParent()->Info = getVerDefNum();
+ getParent()->info = getVerDefNum();
}
-void VersionDefinitionSection::writeOne(uint8_t *Buf, uint32_t Index,
- StringRef Name, size_t NameOff) {
- uint16_t Flags = Index == 1 ? VER_FLG_BASE : 0;
+void VersionDefinitionSection::writeOne(uint8_t *buf, uint32_t index,
+ StringRef name, size_t nameOff) {
+ uint16_t flags = index == 1 ? VER_FLG_BASE : 0;
// Write a verdef.
- write16(Buf, 1); // vd_version
- write16(Buf + 2, Flags); // vd_flags
- write16(Buf + 4, Index); // vd_ndx
- write16(Buf + 6, 1); // vd_cnt
- write32(Buf + 8, hashSysV(Name)); // vd_hash
- write32(Buf + 12, 20); // vd_aux
- write32(Buf + 16, 28); // vd_next
+ write16(buf, 1); // vd_version
+ write16(buf + 2, flags); // vd_flags
+ write16(buf + 4, index); // vd_ndx
+ write16(buf + 6, 1); // vd_cnt
+ write32(buf + 8, hashSysV(name)); // vd_hash
+ write32(buf + 12, 20); // vd_aux
+ write32(buf + 16, 28); // vd_next
// Write a veraux.
- write32(Buf + 20, NameOff); // vda_name
- write32(Buf + 24, 0); // vda_next
+ write32(buf + 20, nameOff); // vda_name
+ write32(buf + 24, 0); // vda_next
}
-void VersionDefinitionSection::writeTo(uint8_t *Buf) {
- writeOne(Buf, 1, getFileDefName(), FileDefNameOff);
+void VersionDefinitionSection::writeTo(uint8_t *buf) {
+ writeOne(buf, 1, getFileDefName(), fileDefNameOff);
- for (VersionDefinition &V : Config->VersionDefinitions) {
- Buf += EntrySize;
- writeOne(Buf, V.Id, V.Name, V.NameOff);
+ auto nameOffIt = verDefNameOffs.begin();
+ for (VersionDefinition &v : config->versionDefinitions) {
+ buf += EntrySize;
+ writeOne(buf, v.id, v.name, *nameOffIt++);
}
// Need to terminate the last version definition.
- write32(Buf + 16, 0); // vd_next
+ write32(buf + 16, 0); // vd_next
}
size_t VersionDefinitionSection::getSize() const {
@@ -2743,160 +2798,161 @@ size_t VersionDefinitionSection::getSize() const {
}
// .gnu.version is a table where each entry is 2 byte long.
-template <class ELFT>
-VersionTableSection<ELFT>::VersionTableSection()
+VersionTableSection::VersionTableSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_versym, sizeof(uint16_t),
".gnu.version") {
- this->Entsize = 2;
+ this->entsize = 2;
}
-template <class ELFT> void VersionTableSection<ELFT>::finalizeContents() {
+void VersionTableSection::finalizeContents() {
// At the moment of june 2016 GNU docs does not mention that sh_link field
// should be set, but Sun docs do. Also readelf relies on this field.
- getParent()->Link = In.DynSymTab->getParent()->SectionIndex;
+ getParent()->link = getPartition().dynSymTab->getParent()->sectionIndex;
}
-template <class ELFT> size_t VersionTableSection<ELFT>::getSize() const {
- return (In.DynSymTab->getSymbols().size() + 1) * 2;
+size_t VersionTableSection::getSize() const {
+ return (getPartition().dynSymTab->getSymbols().size() + 1) * 2;
}
-template <class ELFT> void VersionTableSection<ELFT>::writeTo(uint8_t *Buf) {
- Buf += 2;
- for (const SymbolTableEntry &S : In.DynSymTab->getSymbols()) {
- write16(Buf, S.Sym->VersionId);
- Buf += 2;
+void VersionTableSection::writeTo(uint8_t *buf) {
+ buf += 2;
+ for (const SymbolTableEntry &s : getPartition().dynSymTab->getSymbols()) {
+ write16(buf, s.sym->versionId);
+ buf += 2;
}
}
-template <class ELFT> bool VersionTableSection<ELFT>::empty() const {
- return !In.VerDef && InX<ELFT>::VerNeed->empty();
+bool VersionTableSection::isNeeded() const {
+ return getPartition().verDef || getPartition().verNeed->isNeeded();
+}
+
+void elf::addVerneed(Symbol *ss) {
+ auto &file = cast<SharedFile>(*ss->file);
+ if (ss->verdefIndex == VER_NDX_GLOBAL) {
+ ss->versionId = VER_NDX_GLOBAL;
+ return;
+ }
+
+ if (file.vernauxs.empty())
+ file.vernauxs.resize(file.verdefs.size());
+
+ // Select a version identifier for the vernaux data structure, if we haven't
+ // already allocated one. The verdef identifiers cover the range
+ // [1..getVerDefNum()]; this causes the vernaux identifiers to start from
+ // getVerDefNum()+1.
+ if (file.vernauxs[ss->verdefIndex] == 0)
+ file.vernauxs[ss->verdefIndex] = ++SharedFile::vernauxNum + getVerDefNum();
+
+ ss->versionId = file.vernauxs[ss->verdefIndex];
}
template <class ELFT>
VersionNeedSection<ELFT>::VersionNeedSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_verneed, sizeof(uint32_t),
- ".gnu.version_r") {
- // Identifiers in verneed section start at 2 because 0 and 1 are reserved
- // for VER_NDX_LOCAL and VER_NDX_GLOBAL.
- // First identifiers are reserved by verdef section if it exist.
- NextIndex = getVerDefNum() + 1;
-}
+ ".gnu.version_r") {}
-template <class ELFT> void VersionNeedSection<ELFT>::addSymbol(Symbol *SS) {
- auto &File = cast<SharedFile<ELFT>>(*SS->File);
- if (SS->VerdefIndex == VER_NDX_GLOBAL) {
- SS->VersionId = VER_NDX_GLOBAL;
- return;
+template <class ELFT> void VersionNeedSection<ELFT>::finalizeContents() {
+ for (SharedFile *f : sharedFiles) {
+ if (f->vernauxs.empty())
+ continue;
+ verneeds.emplace_back();
+ Verneed &vn = verneeds.back();
+ vn.nameStrTab = getPartition().dynStrTab->addString(f->soName);
+ for (unsigned i = 0; i != f->vernauxs.size(); ++i) {
+ if (f->vernauxs[i] == 0)
+ continue;
+ auto *verdef =
+ reinterpret_cast<const typename ELFT::Verdef *>(f->verdefs[i]);
+ vn.vernauxs.push_back(
+ {verdef->vd_hash, f->vernauxs[i],
+ getPartition().dynStrTab->addString(f->getStringTable().data() +
+ verdef->getAux()->vda_name)});
+ }
}
- // If we don't already know that we need an Elf_Verneed for this DSO, prepare
- // to create one by adding it to our needed list and creating a dynstr entry
- // for the soname.
- if (File.VerdefMap.empty())
- Needed.push_back({&File, In.DynStrTab->addString(File.SoName)});
- const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex];
- typename SharedFile<ELFT>::NeededVer &NV = File.VerdefMap[Ver];
-
- // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef,
- // prepare to create one by allocating a version identifier and creating a
- // dynstr entry for the version name.
- if (NV.Index == 0) {
- NV.StrTab = In.DynStrTab->addString(File.getStringTable().data() +
- Ver->getAux()->vda_name);
- NV.Index = NextIndex++;
- }
- SS->VersionId = NV.Index;
+ if (OutputSection *sec = getPartition().dynStrTab->getParent())
+ getParent()->link = sec->sectionIndex;
+ getParent()->info = verneeds.size();
}
-template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *Buf) {
+template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *buf) {
// The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs.
- auto *Verneed = reinterpret_cast<Elf_Verneed *>(Buf);
- auto *Vernaux = reinterpret_cast<Elf_Vernaux *>(Verneed + Needed.size());
+ auto *verneed = reinterpret_cast<Elf_Verneed *>(buf);
+ auto *vernaux = reinterpret_cast<Elf_Vernaux *>(verneed + verneeds.size());
- for (std::pair<SharedFile<ELFT> *, size_t> &P : Needed) {
+ for (auto &vn : verneeds) {
// Create an Elf_Verneed for this DSO.
- Verneed->vn_version = 1;
- Verneed->vn_cnt = P.first->VerdefMap.size();
- Verneed->vn_file = P.second;
- Verneed->vn_aux =
- reinterpret_cast<char *>(Vernaux) - reinterpret_cast<char *>(Verneed);
- Verneed->vn_next = sizeof(Elf_Verneed);
- ++Verneed;
-
- // Create the Elf_Vernauxs for this Elf_Verneed. The loop iterates over
- // VerdefMap, which will only contain references to needed version
- // definitions. Each Elf_Vernaux is based on the information contained in
- // the Elf_Verdef in the source DSO. This loop iterates over a std::map of
- // pointers, but is deterministic because the pointers refer to Elf_Verdef
- // data structures within a single input file.
- for (auto &NV : P.first->VerdefMap) {
- Vernaux->vna_hash = NV.first->vd_hash;
- Vernaux->vna_flags = 0;
- Vernaux->vna_other = NV.second.Index;
- Vernaux->vna_name = NV.second.StrTab;
- Vernaux->vna_next = sizeof(Elf_Vernaux);
- ++Vernaux;
+ verneed->vn_version = 1;
+ verneed->vn_cnt = vn.vernauxs.size();
+ verneed->vn_file = vn.nameStrTab;
+ verneed->vn_aux =
+ reinterpret_cast<char *>(vernaux) - reinterpret_cast<char *>(verneed);
+ verneed->vn_next = sizeof(Elf_Verneed);
+ ++verneed;
+
+ // Create the Elf_Vernauxs for this Elf_Verneed.
+ for (auto &vna : vn.vernauxs) {
+ vernaux->vna_hash = vna.hash;
+ vernaux->vna_flags = 0;
+ vernaux->vna_other = vna.verneedIndex;
+ vernaux->vna_name = vna.nameStrTab;
+ vernaux->vna_next = sizeof(Elf_Vernaux);
+ ++vernaux;
}
- Vernaux[-1].vna_next = 0;
+ vernaux[-1].vna_next = 0;
}
- Verneed[-1].vn_next = 0;
-}
-
-template <class ELFT> void VersionNeedSection<ELFT>::finalizeContents() {
- if (OutputSection *Sec = In.DynStrTab->getParent())
- getParent()->Link = Sec->SectionIndex;
- getParent()->Info = Needed.size();
+ verneed[-1].vn_next = 0;
}
template <class ELFT> size_t VersionNeedSection<ELFT>::getSize() const {
- unsigned Size = Needed.size() * sizeof(Elf_Verneed);
- for (const std::pair<SharedFile<ELFT> *, size_t> &P : Needed)
- Size += P.first->VerdefMap.size() * sizeof(Elf_Vernaux);
- return Size;
+ return verneeds.size() * sizeof(Elf_Verneed) +
+ SharedFile::vernauxNum * sizeof(Elf_Vernaux);
}
-template <class ELFT> bool VersionNeedSection<ELFT>::empty() const {
- return getNeedNum() == 0;
+template <class ELFT> bool VersionNeedSection<ELFT>::isNeeded() const {
+ return SharedFile::vernauxNum != 0;
}
-void MergeSyntheticSection::addSection(MergeInputSection *MS) {
- MS->Parent = this;
- Sections.push_back(MS);
+void MergeSyntheticSection::addSection(MergeInputSection *ms) {
+ ms->parent = this;
+ sections.push_back(ms);
+ assert(alignment == ms->alignment || !(ms->flags & SHF_STRINGS));
+ alignment = std::max(alignment, ms->alignment);
}
-MergeTailSection::MergeTailSection(StringRef Name, uint32_t Type,
- uint64_t Flags, uint32_t Alignment)
- : MergeSyntheticSection(Name, Type, Flags, Alignment),
- Builder(StringTableBuilder::RAW, Alignment) {}
+MergeTailSection::MergeTailSection(StringRef name, uint32_t type,
+ uint64_t flags, uint32_t alignment)
+ : MergeSyntheticSection(name, type, flags, alignment),
+ builder(StringTableBuilder::RAW, alignment) {}
-size_t MergeTailSection::getSize() const { return Builder.getSize(); }
+size_t MergeTailSection::getSize() const { return builder.getSize(); }
-void MergeTailSection::writeTo(uint8_t *Buf) { Builder.write(Buf); }
+void MergeTailSection::writeTo(uint8_t *buf) { builder.write(buf); }
void MergeTailSection::finalizeContents() {
// Add all string pieces to the string table builder to create section
// contents.
- for (MergeInputSection *Sec : Sections)
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
- if (Sec->Pieces[I].Live)
- Builder.add(Sec->getData(I));
+ for (MergeInputSection *sec : sections)
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
+ if (sec->pieces[i].live)
+ builder.add(sec->getData(i));
// Fix the string table content. After this, the contents will never change.
- Builder.finalize();
+ builder.finalize();
// finalize() fixed tail-optimized strings, so we can now get
// offsets of strings. Get an offset for each string and save it
- // to a corresponding StringPiece for easy access.
- for (MergeInputSection *Sec : Sections)
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
- if (Sec->Pieces[I].Live)
- Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I));
+ // to a corresponding SectionPiece for easy access.
+ for (MergeInputSection *sec : sections)
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
+ if (sec->pieces[i].live)
+ sec->pieces[i].outputOff = builder.getOffset(sec->getData(i));
}
-void MergeNoTailSection::writeTo(uint8_t *Buf) {
- for (size_t I = 0; I < NumShards; ++I)
- Shards[I].write(Buf + ShardOffsets[I]);
+void MergeNoTailSection::writeTo(uint8_t *buf) {
+ for (size_t i = 0; i < numShards; ++i)
+ shards[i].write(buf + shardOffsets[i]);
}
// This function is very hot (i.e. it can take several seconds to finish)
@@ -2909,66 +2965,68 @@ void MergeNoTailSection::writeTo(uint8_t *Buf) {
// We do it in parallel.
void MergeNoTailSection::finalizeContents() {
// Initializes string table builders.
- for (size_t I = 0; I < NumShards; ++I)
- Shards.emplace_back(StringTableBuilder::RAW, Alignment);
+ for (size_t i = 0; i < numShards; ++i)
+ shards.emplace_back(StringTableBuilder::RAW, alignment);
// 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 = 1;
+ if (threadsEnabled)
+ concurrency =
+ std::min<size_t>(PowerOf2Floor(hardware_concurrency()), numShards);
// Add section pieces to the builders.
- parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
- for (MergeInputSection *Sec : Sections) {
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) {
- size_t ShardId = getShardId(Sec->Pieces[I].Hash);
- if ((ShardId & (Concurrency - 1)) == ThreadId && Sec->Pieces[I].Live)
- Sec->Pieces[I].OutputOff = Shards[ShardId].add(Sec->getData(I));
+ parallelForEachN(0, concurrency, [&](size_t threadId) {
+ for (MergeInputSection *sec : sections) {
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) {
+ if (!sec->pieces[i].live)
+ continue;
+ size_t shardId = getShardId(sec->pieces[i].hash);
+ if ((shardId & (concurrency - 1)) == threadId)
+ sec->pieces[i].outputOff = shards[shardId].add(sec->getData(i));
}
}
});
// Compute an in-section offset for each shard.
- size_t Off = 0;
- for (size_t I = 0; I < NumShards; ++I) {
- Shards[I].finalizeInOrder();
- if (Shards[I].getSize() > 0)
- Off = alignTo(Off, Alignment);
- ShardOffsets[I] = Off;
- Off += Shards[I].getSize();
+ size_t off = 0;
+ for (size_t i = 0; i < numShards; ++i) {
+ shards[i].finalizeInOrder();
+ if (shards[i].getSize() > 0)
+ off = alignTo(off, alignment);
+ shardOffsets[i] = off;
+ off += shards[i].getSize();
}
- Size = Off;
+ size = off;
// So far, section pieces have offsets from beginning of shards, but
// we want offsets from beginning of the whole section. Fix them.
- parallelForEach(Sections, [&](MergeInputSection *Sec) {
- for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
- if (Sec->Pieces[I].Live)
- Sec->Pieces[I].OutputOff +=
- ShardOffsets[getShardId(Sec->Pieces[I].Hash)];
+ parallelForEach(sections, [&](MergeInputSection *sec) {
+ for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
+ if (sec->pieces[i].live)
+ sec->pieces[i].outputOff +=
+ shardOffsets[getShardId(sec->pieces[i].hash)];
});
}
-static MergeSyntheticSection *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);
+static MergeSyntheticSection *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 elf::splitSections() {
// splitIntoPieces needs to be called on each MergeInputSection
// before calling finalizeContents().
- parallelForEach(InputSections, [](InputSectionBase *Sec) {
- if (auto *S = dyn_cast<MergeInputSection>(Sec))
- S->splitIntoPieces();
- else if (auto *Eh = dyn_cast<EhInputSection>(Sec))
- Eh->split<ELFT>();
+ parallelForEach(inputSections, [](InputSectionBase *sec) {
+ if (auto *s = dyn_cast<MergeInputSection>(sec))
+ s->splitIntoPieces();
+ else if (auto *eh = dyn_cast<EhInputSection>(sec))
+ eh->split<ELFT>();
});
}
@@ -2980,23 +3038,22 @@ template <class ELFT> void elf::splitSections() {
// that it replaces. It then finalizes each synthetic section in order
// to compute an output offset for each piece of each input section.
void elf::mergeSections() {
- std::vector<MergeSyntheticSection *> MergeSections;
- for (InputSectionBase *&S : InputSections) {
- MergeInputSection *MS = dyn_cast<MergeInputSection>(S);
- if (!MS)
+ std::vector<MergeSyntheticSection *> mergeSections;
+ for (InputSectionBase *&s : inputSections) {
+ MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
+ if (!ms)
continue;
// We do not want to handle sections that are not alive, so just remove
// them instead of trying to merge.
- if (!MS->Live) {
- S = nullptr;
+ if (!ms->isLive()) {
+ s = nullptr;
continue;
}
- StringRef OutsecName = getOutputSectionName(MS);
- uint32_t Alignment = std::max<uint32_t>(MS->Alignment, MS->Entsize);
+ StringRef outsecName = getOutputSectionName(ms);
- auto I = llvm::find_if(MergeSections, [=](MergeSyntheticSection *Sec) {
+ auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
// While we could create a single synthetic section for two different
// values of Entsize, it is better to take Entsize into consideration.
//
@@ -3005,98 +3062,285 @@ void elf::mergeSections() {
//
// Using Entsize in here also allows us to propagate it to the synthetic
// section.
- return Sec->Name == OutsecName && Sec->Flags == MS->Flags &&
- Sec->Entsize == MS->Entsize && Sec->Alignment == Alignment;
+ //
+ // SHF_STRINGS section with different alignments should not be merged.
+ return sec->name == outsecName && sec->flags == ms->flags &&
+ sec->entsize == ms->entsize &&
+ (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
});
- if (I == MergeSections.end()) {
- MergeSyntheticSection *Syn =
- createMergeSynthetic(OutsecName, MS->Type, MS->Flags, Alignment);
- MergeSections.push_back(Syn);
- I = std::prev(MergeSections.end());
- S = Syn;
- Syn->Entsize = MS->Entsize;
+ if (i == mergeSections.end()) {
+ MergeSyntheticSection *syn =
+ createMergeSynthetic(outsecName, ms->type, ms->flags, ms->alignment);
+ mergeSections.push_back(syn);
+ i = std::prev(mergeSections.end());
+ s = syn;
+ syn->entsize = ms->entsize;
} else {
- S = nullptr;
+ s = nullptr;
}
- (*I)->addSection(MS);
+ (*i)->addSection(ms);
}
- for (auto *MS : MergeSections)
- MS->finalizeContents();
+ for (auto *ms : mergeSections)
+ ms->finalizeContents();
- std::vector<InputSectionBase *> &V = InputSections;
- V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
+ std::vector<InputSectionBase *> &v = inputSections;
+ v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
}
MipsRldMapSection::MipsRldMapSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Config->Wordsize,
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
".rld_map") {}
-ARMExidxSentinelSection::ARMExidxSentinelSection()
+ARMExidxSyntheticSection::ARMExidxSyntheticSection()
: SyntheticSection(SHF_ALLOC | SHF_LINK_ORDER, SHT_ARM_EXIDX,
- Config->Wordsize, ".ARM.exidx") {}
-
-// Write a terminating sentinel entry to the end of the .ARM.exidx table.
-// This section will have been sorted last in the .ARM.exidx table.
-// This table entry will have the form:
-// | PREL31 upper bound of code that has exception tables | EXIDX_CANTUNWIND |
-// The sentinel must have the PREL31 value of an address higher than any
-// address described by any other table entry.
-void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
- assert(Highest);
- uint64_t S = Highest->getVA(Highest->getSize());
- uint64_t P = getVA();
- Target->relocateOne(Buf, R_ARM_PREL31, S - P);
- write32le(Buf + 4, 1);
-}
-
-// The sentinel has to be removed if there are no other .ARM.exidx entries.
-bool ARMExidxSentinelSection::empty() const {
- for (InputSection *IS : getInputSections(getParent()))
- if (!isa<ARMExidxSentinelSection>(IS))
+ config->wordsize, ".ARM.exidx") {}
+
+static InputSection *findExidxSection(InputSection *isec) {
+ for (InputSection *d : isec->dependentSections)
+ if (d->type == SHT_ARM_EXIDX)
+ return d;
+ return nullptr;
+}
+
+bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
+ if (isec->type == SHT_ARM_EXIDX) {
+ exidxSections.push_back(isec);
+ return true;
+ }
+
+ if ((isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
+ isec->getSize() > 0) {
+ executableSections.push_back(isec);
+ if (empty && findExidxSection(isec))
+ empty = false;
+ return false;
+ }
+
+ // FIXME: we do not output a relocation section when --emit-relocs is used
+ // as we do not have relocation sections for linker generated table entries
+ // and we would have to erase at a late stage relocations from merged entries.
+ // Given that exception tables are already position independent and a binary
+ // analyzer could derive the relocations we choose to erase the relocations.
+ if (config->emitRelocs && isec->type == SHT_REL)
+ if (InputSectionBase *ex = isec->getRelocatedSection())
+ if (isa<InputSection>(ex) && ex->type == SHT_ARM_EXIDX)
+ return true;
+
+ return false;
+}
+
+// References to .ARM.Extab Sections have bit 31 clear and are not the
+// special EXIDX_CANTUNWIND bit-pattern.
+static bool isExtabRef(uint32_t unwind) {
+ return (unwind & 0x80000000) == 0 && unwind != 0x1;
+}
+
+// Return true if the .ARM.exidx section Cur can be merged into the .ARM.exidx
+// section Prev, where Cur follows Prev in the table. This can be done if the
+// unwinding instructions in Cur are identical to Prev. Linker generated
+// EXIDX_CANTUNWIND entries are represented by nullptr as they do not have an
+// InputSection.
+static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) {
+
+ struct ExidxEntry {
+ ulittle32_t fn;
+ ulittle32_t unwind;
+ };
+ // Get the last table Entry from the previous .ARM.exidx section. If Prev is
+ // nullptr then it will be a synthesized EXIDX_CANTUNWIND entry.
+ ExidxEntry prevEntry = {ulittle32_t(0), ulittle32_t(1)};
+ if (prev)
+ prevEntry = prev->getDataAs<ExidxEntry>().back();
+ if (isExtabRef(prevEntry.unwind))
+ return false;
+
+ // We consider the unwind instructions of an .ARM.exidx table entry
+ // a duplicate if the previous unwind instructions if:
+ // - Both are the special EXIDX_CANTUNWIND.
+ // - Both are the same inline unwind instructions.
+ // We do not attempt to follow and check links into .ARM.extab tables as
+ // consecutive identical entries are rare and the effort to check that they
+ // are identical is high.
+
+ // If Cur is nullptr then this is synthesized EXIDX_CANTUNWIND entry.
+ if (cur == nullptr)
+ return prevEntry.unwind == 1;
+
+ for (const ExidxEntry entry : cur->getDataAs<ExidxEntry>())
+ if (isExtabRef(entry.unwind) || entry.unwind != prevEntry.unwind)
return false;
+
+ // All table entries in this .ARM.exidx Section can be merged into the
+ // previous Section.
return true;
}
-bool ARMExidxSentinelSection::classof(const SectionBase *D) {
- return D->kind() == InputSectionBase::Synthetic && D->Type == SHT_ARM_EXIDX;
+// The .ARM.exidx table must be sorted in ascending order of the address of the
+// functions the table describes. Optionally duplicate adjacent table entries
+// can be removed. At the end of the function the ExecutableSections must be
+// sorted in ascending order of address, Sentinel is set to the InputSection
+// with the highest address and any InputSections that have mergeable
+// .ARM.exidx table entries are removed from it.
+void ARMExidxSyntheticSection::finalizeContents() {
+ // 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.
+ 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 a->outSecOff < b->outSecOff;
+ };
+ llvm::stable_sort(executableSections, compareByFilePosition);
+ sentinel = executableSections.back();
+ // Optionally merge adjacent duplicate entries.
+ if (config->mergeArmExidx) {
+ std::vector<InputSection *> selectedSections;
+ selectedSections.reserve(executableSections.size());
+ selectedSections.push_back(executableSections[0]);
+ size_t prev = 0;
+ for (size_t i = 1; i < executableSections.size(); ++i) {
+ InputSection *ex1 = findExidxSection(executableSections[prev]);
+ InputSection *ex2 = findExidxSection(executableSections[i]);
+ if (!isDuplicateArmExidxSec(ex1, ex2)) {
+ selectedSections.push_back(executableSections[i]);
+ prev = i;
+ }
+ }
+ executableSections = std::move(selectedSections);
+ }
+
+ size_t offset = 0;
+ size = 0;
+ for (InputSection *isec : executableSections) {
+ if (InputSection *d = findExidxSection(isec)) {
+ d->outSecOff = offset;
+ d->parent = getParent();
+ offset += d->getSize();
+ } else {
+ offset += 8;
+ }
+ }
+ // Size includes Sentinel.
+ size = offset + 8;
+}
+
+InputSection *ARMExidxSyntheticSection::getLinkOrderDep() const {
+ return executableSections.front();
}
-ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off)
+// To write the .ARM.exidx table from the ExecutableSections we have three cases
+// 1.) The InputSection has a .ARM.exidx InputSection in its dependent sections.
+// We write the .ARM.exidx section contents and apply its relocations.
+// 2.) The InputSection does not have a dependent .ARM.exidx InputSection. We
+// must write the contents of an EXIDX_CANTUNWIND directly. We use the
+// start of the InputSection as the purpose of the linker generated
+// section is to terminate the address range of the previous entry.
+// 3.) A trailing EXIDX_CANTUNWIND sentinel section is required at the end of
+// the table to terminate the address range of the final entry.
+void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
+
+ const uint8_t cantUnwindData[8] = {0, 0, 0, 0, // PREL31 to target
+ 1, 0, 0, 0}; // EXIDX_CANTUNWIND
+
+ uint64_t offset = 0;
+ for (InputSection *isec : executableSections) {
+ assert(isec->getParent() != nullptr);
+ if (InputSection *d = findExidxSection(isec)) {
+ memcpy(buf + offset, d->data().data(), d->data().size());
+ d->relocateAlloc(buf, buf + d->getSize());
+ offset += d->getSize();
+ } else {
+ // A Linker generated CANTUNWIND section.
+ 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);
+ offset += 8;
+ }
+ }
+ // Write Sentinel.
+ 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);
+ assert(size == offset + 8);
+}
+
+bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
+ return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
+}
+
+ThunkSection::ThunkSection(OutputSection *os, uint64_t off)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS,
- Config->Wordsize, ".text.thunk") {
- this->Parent = OS;
- this->OutSecOff = Off;
+ config->wordsize, ".text.thunk") {
+ this->parent = os;
+ this->outSecOff = off;
}
-void ThunkSection::addThunk(Thunk *T) {
- Thunks.push_back(T);
- T->addSymbols(*this);
+void ThunkSection::addThunk(Thunk *t) {
+ thunks.push_back(t);
+ t->addSymbols(*this);
}
-void ThunkSection::writeTo(uint8_t *Buf) {
- for (Thunk *T : Thunks)
- T->writeTo(Buf + T->Offset);
+void ThunkSection::writeTo(uint8_t *buf) {
+ for (Thunk *t : thunks)
+ t->writeTo(buf + t->offset);
}
InputSection *ThunkSection::getTargetInputSection() const {
- if (Thunks.empty())
+ if (thunks.empty())
return nullptr;
- const Thunk *T = Thunks.front();
- return T->getTargetInputSection();
+ const Thunk *t = thunks.front();
+ return t->getTargetInputSection();
}
bool ThunkSection::assignOffsets() {
- uint64_t Off = 0;
- for (Thunk *T : Thunks) {
- Off = alignTo(Off, T->Alignment);
- T->setOffset(Off);
- uint32_t Size = T->size();
- T->getThunkTargetSym()->Size = Size;
- Off += Size;
- }
- bool Changed = Off != Size;
- Size = Off;
- return Changed;
+ uint64_t off = 0;
+ for (Thunk *t : thunks) {
+ off = alignTo(off, t->alignment);
+ t->setOffset(off);
+ uint32_t size = t->size();
+ t->getThunkTargetSym()->size = size;
+ off += size;
+ }
+ bool changed = off != size;
+ size = off;
+ return changed;
+}
+
+PPC32Got2Section::PPC32Got2Section()
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 4, ".got2") {}
+
+bool PPC32Got2Section::isNeeded() const {
+ // See the comment below. This is not needed if there is no other
+ // InputSection.
+ for (BaseCommand *base : getParent()->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ for (InputSection *isec : isd->sections)
+ if (isec != this)
+ return true;
+ return false;
+}
+
+void PPC32Got2Section::finalizeContents() {
+ // PPC32 may create multiple GOT sections for -fPIC/-fPIE, one per file in
+ // .got2 . This function computes outSecOff of each .got2 to be used in
+ // PPC32PltCallStub::writeTo(). The purpose of this empty synthetic section is
+ // to collect input sections named ".got2".
+ uint32_t offset = 0;
+ for (BaseCommand *base : getParent()->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (InputSection *isec : isd->sections) {
+ if (isec == this)
+ continue;
+ isec->file->ppc32Got2OutSecOff = offset;
+ offset += (uint32_t)isec->getSize();
+ }
+ }
}
// If linking position-dependent code then the table will store the addresses
@@ -3105,48 +3349,188 @@ bool ThunkSection::assignOffsets() {
// allocated and filled in by the dynamic linker.
PPC64LongBranchTargetSection::PPC64LongBranchTargetSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE,
- Config->Pic ? SHT_NOBITS : SHT_PROGBITS, 8,
+ config->isPic ? SHT_NOBITS : SHT_PROGBITS, 8,
".branch_lt") {}
-void PPC64LongBranchTargetSection::addEntry(Symbol &Sym) {
- assert(Sym.PPC64BranchltIndex == 0xffff);
- Sym.PPC64BranchltIndex = Entries.size();
- Entries.push_back(&Sym);
+void PPC64LongBranchTargetSection::addEntry(Symbol &sym) {
+ assert(sym.ppc64BranchltIndex == 0xffff);
+ sym.ppc64BranchltIndex = entries.size();
+ entries.push_back(&sym);
}
size_t PPC64LongBranchTargetSection::getSize() const {
- return Entries.size() * 8;
+ return entries.size() * 8;
}
-void PPC64LongBranchTargetSection::writeTo(uint8_t *Buf) {
- assert(Target->GotPltEntrySize == 8);
+void PPC64LongBranchTargetSection::writeTo(uint8_t *buf) {
// If linking non-pic we have the final addresses of the targets and they get
// written to the table directly. For pic the dynamic linker will allocate
// the section and fill it it.
- if (Config->Pic)
+ if (config->isPic)
return;
- for (const Symbol *Sym : Entries) {
- assert(Sym->getVA());
+ for (const Symbol *sym : entries) {
+ assert(sym->getVA());
// Need calls to branch to the local entry-point since a long-branch
// must be a local-call.
- write64(Buf,
- Sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(Sym->StOther));
- Buf += Target->GotPltEntrySize;
+ write64(buf,
+ sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(sym->stOther));
+ buf += 8;
}
}
-bool PPC64LongBranchTargetSection::empty() const {
+bool PPC64LongBranchTargetSection::isNeeded() const {
// `removeUnusedSyntheticSections()` is called before thunk allocation which
// is too early to determine if this section will be empty or not. We need
// Finalized to keep the section alive until after thunk creation. Finalized
// only gets set to true once `finalizeSections()` is called after thunk
// creation. Becuase of this, if we don't create any long-branch thunks we end
// up with an empty .branch_lt section in the binary.
- return Finalized && Entries.empty();
+ return !finalized || !entries.empty();
}
-InStruct elf::In;
+RISCVSdataSection::RISCVSdataSection()
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 1, ".sdata") {}
+
+bool RISCVSdataSection::isNeeded() const {
+ if (!ElfSym::riscvGlobalPointer)
+ return false;
+
+ // __global_pointer$ is defined relative to .sdata . If the section does not
+ // exist, create a dummy one.
+ for (BaseCommand *base : getParent()->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ for (InputSection *isec : isd->sections)
+ if (isec != this)
+ return false;
+ return true;
+}
+
+static uint8_t getAbiVersion() {
+ // MIPS non-PIC executable gets ABI version 1.
+ if (config->emachine == EM_MIPS) {
+ if (!config->isPic && !config->relocatable &&
+ (config->eflags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
+ return 1;
+ return 0;
+ }
+
+ if (config->emachine == EM_AMDGPU) {
+ uint8_t ver = objectFiles[0]->abiVersion;
+ for (InputFile *file : makeArrayRef(objectFiles).slice(1))
+ if (file->abiVersion != ver)
+ error("incompatible ABI version: " + toString(file));
+ return ver;
+ }
+
+ return 0;
+}
+
+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.
+ memset(buf, 0, sizeof(typename ELFT::Ehdr));
+ memcpy(buf, "\177ELF", 4);
+
+ auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
+ eHdr->e_ident[EI_CLASS] = config->is64 ? ELFCLASS64 : ELFCLASS32;
+ eHdr->e_ident[EI_DATA] = config->isLE ? ELFDATA2LSB : ELFDATA2MSB;
+ eHdr->e_ident[EI_VERSION] = EV_CURRENT;
+ eHdr->e_ident[EI_OSABI] = config->osabi;
+ eHdr->e_ident[EI_ABIVERSION] = getAbiVersion();
+ eHdr->e_machine = config->emachine;
+ eHdr->e_version = EV_CURRENT;
+ eHdr->e_flags = config->eflags;
+ eHdr->e_ehsize = sizeof(typename ELFT::Ehdr);
+ eHdr->e_phnum = part.phdrs.size();
+ eHdr->e_shentsize = sizeof(typename ELFT::Shdr);
+
+ if (!config->relocatable) {
+ eHdr->e_phoff = sizeof(typename ELFT::Ehdr);
+ eHdr->e_phentsize = sizeof(typename ELFT::Phdr);
+ }
+}
+
+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) {
+ hBuf->p_type = p->p_type;
+ hBuf->p_flags = p->p_flags;
+ hBuf->p_offset = p->p_offset;
+ hBuf->p_vaddr = p->p_vaddr;
+ hBuf->p_paddr = p->p_paddr;
+ hBuf->p_filesz = p->p_filesz;
+ hBuf->p_memsz = p->p_memsz;
+ hBuf->p_align = p->p_align;
+ ++hBuf;
+ }
+}
+
+template <typename ELFT>
+PartitionElfHeaderSection<ELFT>::PartitionElfHeaderSection()
+ : SyntheticSection(SHF_ALLOC, SHT_LLVM_PART_EHDR, 1, "") {}
+
+template <typename ELFT>
+size_t PartitionElfHeaderSection<ELFT>::getSize() const {
+ return sizeof(typename ELFT::Ehdr);
+}
+
+template <typename ELFT>
+void PartitionElfHeaderSection<ELFT>::writeTo(uint8_t *buf) {
+ writeEhdr<ELFT>(buf, getPartition());
+
+ // Loadable partitions are always ET_DYN.
+ auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
+ eHdr->e_type = ET_DYN;
+}
+
+template <typename ELFT>
+PartitionProgramHeadersSection<ELFT>::PartitionProgramHeadersSection()
+ : SyntheticSection(SHF_ALLOC, SHT_LLVM_PART_PHDR, 1, ".phdrs") {}
+
+template <typename ELFT>
+size_t PartitionProgramHeadersSection<ELFT>::getSize() const {
+ return sizeof(typename ELFT::Phdr) * getPartition().phdrs.size();
+}
+
+template <typename ELFT>
+void PartitionProgramHeadersSection<ELFT>::writeTo(uint8_t *buf) {
+ writePhdrs<ELFT>(buf, getPartition());
+}
+
+PartitionIndexSection::PartitionIndexSection()
+ : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".rodata") {}
+
+size_t PartitionIndexSection::getSize() const {
+ return 12 * (partitions.size() - 1);
+}
+
+void PartitionIndexSection::finalizeContents() {
+ for (size_t i = 1; i != partitions.size(); ++i)
+ partitions[i].nameStrTab = mainPart->dynStrTab->addString(partitions[i].name);
+}
+
+void PartitionIndexSection::writeTo(uint8_t *buf) {
+ uint64_t va = getVA();
+ for (size_t i = 1; i != partitions.size(); ++i) {
+ write32(buf, mainPart->dynStrTab->getVA() + partitions[i].nameStrTab - va);
+ write32(buf + 4, partitions[i].elfHeader->getVA() - (va + 4));
+
+ SyntheticSection *next =
+ i == partitions.size() - 1 ? in.partEnd : partitions[i + 1].elfHeader;
+ write32(buf + 8, next->getVA() - partitions[i].elfHeader->getVA());
+
+ va += 12;
+ buf += 12;
+ }
+}
+
+InStruct elf::in;
+
+std::vector<Partition> elf::partitions;
+Partition *elf::mainPart;
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
@@ -3168,11 +3552,6 @@ template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
-template void MipsGotSection::build<ELF32LE>();
-template void MipsGotSection::build<ELF32BE>();
-template void MipsGotSection::build<ELF64LE>();
-template void MipsGotSection::build<ELF64BE>();
-
template class elf::MipsAbiFlagsSection<ELF32LE>;
template class elf::MipsAbiFlagsSection<ELF32BE>;
template class elf::MipsAbiFlagsSection<ELF64LE>;
@@ -3213,12 +3592,27 @@ template class elf::SymbolTableSection<ELF32BE>;
template class elf::SymbolTableSection<ELF64LE>;
template class elf::SymbolTableSection<ELF64BE>;
-template class elf::VersionTableSection<ELF32LE>;
-template class elf::VersionTableSection<ELF32BE>;
-template class elf::VersionTableSection<ELF64LE>;
-template class elf::VersionTableSection<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/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
index 6fc40d355d5e..1c4dd06e0277 100644
--- a/ELF/SyntheticSections.h
+++ b/ELF/SyntheticSections.h
@@ -1,9 +1,8 @@
//===- SyntheticSection.h ---------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -18,8 +17,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_ELF_SYNTHETIC_SECTION_H
-#define LLD_ELF_SYNTHETIC_SECTION_H
+#ifndef LLD_ELF_SYNTHETIC_SECTIONS_H
+#define LLD_ELF_SYNTHETIC_SECTIONS_H
#include "DWARF.h"
#include "EhFrame.h"
@@ -32,107 +31,113 @@
namespace lld {
namespace elf {
class Defined;
-class SharedSymbol;
+struct PhdrEntry;
+class SymbolTableBaseSection;
+class VersionNeedBaseSection;
class SyntheticSection : public InputSection {
public:
- SyntheticSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
- StringRef Name)
- : InputSection(nullptr, Flags, Type, Alignment, {}, Name,
+ SyntheticSection(uint64_t flags, uint32_t type, uint32_t alignment,
+ StringRef name)
+ : InputSection(nullptr, flags, type, alignment, {}, name,
InputSectionBase::Synthetic) {
- this->Live = true;
+ markLive();
}
virtual ~SyntheticSection() = default;
- virtual void writeTo(uint8_t *Buf) = 0;
+ virtual void writeTo(uint8_t *buf) = 0;
virtual size_t getSize() const = 0;
virtual void finalizeContents() {}
// If the section has the SHF_ALLOC flag and the size may be changed if
// thunks are added, update the section size.
virtual bool updateAllocSize() { return false; }
- virtual bool empty() const { return false; }
+ virtual bool isNeeded() const { return true; }
- static bool classof(const SectionBase *D) {
- return D->kind() == InputSectionBase::Synthetic;
+ static bool classof(const SectionBase *d) {
+ return d->kind() == InputSectionBase::Synthetic;
}
};
struct CieRecord {
- EhSectionPiece *Cie = nullptr;
- std::vector<EhSectionPiece *> Fdes;
+ EhSectionPiece *cie = nullptr;
+ std::vector<EhSectionPiece *> fdes;
};
// Section for .eh_frame.
class EhFrameSection final : public SyntheticSection {
public:
EhFrameSection();
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
void finalizeContents() override;
- bool empty() const override { return Sections.empty(); }
- size_t getSize() const override { return Size; }
+ bool isNeeded() const override { return !sections.empty(); }
+ size_t getSize() const override { return size; }
+
+ static bool classof(const SectionBase *d) {
+ return SyntheticSection::classof(d) && d->name == ".eh_frame";
+ }
- template <class ELFT> void addSection(InputSectionBase *S);
+ template <class ELFT> void addSection(InputSectionBase *s);
- std::vector<EhInputSection *> Sections;
- size_t NumFdes = 0;
+ std::vector<EhInputSection *> sections;
+ size_t numFdes = 0;
struct FdeData {
- uint32_t PcRel;
- uint32_t FdeVARel;
+ uint32_t pcRel;
+ uint32_t fdeVARel;
};
std::vector<FdeData> getFdeData() const;
- ArrayRef<CieRecord *> getCieRecords() const { return CieRecords; }
+ ArrayRef<CieRecord *> getCieRecords() const { return cieRecords; }
private:
// This is used only when parsing EhInputSection. We keep it here to avoid
// allocating one for each EhInputSection.
- llvm::DenseMap<size_t, CieRecord *> OffsetToCie;
+ llvm::DenseMap<size_t, CieRecord *> offsetToCie;
- uint64_t Size = 0;
+ uint64_t size = 0;
template <class ELFT, class RelTy>
- void addSectionAux(EhInputSection *S, llvm::ArrayRef<RelTy> Rels);
+ void addSectionAux(EhInputSection *s, llvm::ArrayRef<RelTy> rels);
template <class ELFT, class RelTy>
- CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
+ CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
template <class ELFT, class RelTy>
- bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
+ bool isFdeLive(EhSectionPiece &piece, ArrayRef<RelTy> rels);
- uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc) const;
+ uint64_t getFdePc(uint8_t *buf, size_t off, uint8_t enc) const;
- std::vector<CieRecord *> CieRecords;
+ std::vector<CieRecord *> cieRecords;
// CIE records are uniquified by their contents and personality functions.
- llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> CieMap;
+ llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> cieMap;
};
class GotSection : public SyntheticSection {
public:
GotSection();
- size_t getSize() const override { return Size; }
+ size_t getSize() const override { return size; }
void finalizeContents() override;
- bool empty() const override;
- void writeTo(uint8_t *Buf) override;
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) override;
- void addEntry(Symbol &Sym);
- bool addDynTlsEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
+ bool addDynTlsEntry(Symbol &sym);
bool addTlsIndex();
- uint64_t getGlobalDynAddr(const Symbol &B) const;
- uint64_t getGlobalDynOffset(const Symbol &B) const;
+ uint64_t getGlobalDynAddr(const Symbol &b) const;
+ uint64_t getGlobalDynOffset(const Symbol &b) const;
- uint64_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
- uint32_t getTlsIndexOff() const { return TlsIndexOff; }
+ uint64_t getTlsIndexVA() { return this->getVA() + tlsIndexOff; }
+ uint32_t getTlsIndexOff() const { return tlsIndexOff; }
// Flag to force GOT to be in output if we have relocations
// that relies on its address.
- bool HasGotOffRel = false;
+ bool hasGotOffRel = false;
protected:
- size_t NumEntries = 0;
- uint32_t TlsIndexOff = -1;
- uint64_t Size = 0;
+ size_t numEntries = 0;
+ uint32_t tlsIndexOff = -1;
+ uint64_t size = 0;
};
// .note.GNU-stack section.
@@ -140,27 +145,31 @@ class GnuStackSection : public SyntheticSection {
public:
GnuStackSection()
: SyntheticSection(0, llvm::ELF::SHT_PROGBITS, 1, ".note.GNU-stack") {}
- void writeTo(uint8_t *Buf) override {}
+ void writeTo(uint8_t *buf) override {}
size_t getSize() const override { return 0; }
};
+class GnuPropertySection : public SyntheticSection {
+public:
+ GnuPropertySection();
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override;
+};
+
// .note.gnu.build-id section.
class BuildIdSection : public SyntheticSection {
// First 16 bytes are a header.
- static const unsigned HeaderSize = 16;
+ static const unsigned headerSize = 16;
public:
+ const size_t hashSize;
BuildIdSection();
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return HeaderSize + HashSize; }
- void writeBuildId(llvm::ArrayRef<uint8_t> Buf);
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return headerSize + hashSize; }
+ void writeBuildId(llvm::ArrayRef<uint8_t> buf);
private:
- void computeHash(llvm::ArrayRef<uint8_t> Buf,
- std::function<void(uint8_t *, ArrayRef<uint8_t>)> Hash);
-
- size_t HashSize;
- uint8_t *HashBuf;
+ uint8_t *hashBuf;
};
// BssSection is used to reserve space for copy relocations and common symbols.
@@ -169,40 +178,40 @@ private:
// respectively.
class BssSection final : public SyntheticSection {
public:
- BssSection(StringRef Name, uint64_t Size, uint32_t Alignment);
+ BssSection(StringRef name, uint64_t size, uint32_t alignment);
void writeTo(uint8_t *) override {
llvm_unreachable("unexpected writeTo() call for SHT_NOBITS section");
}
- bool empty() const override { return getSize() == 0; }
- size_t getSize() const override { return Size; }
+ bool isNeeded() const override { return size != 0; }
+ size_t getSize() const override { return size; }
- static bool classof(const SectionBase *S) { return S->Bss; }
- uint64_t Size;
+ static bool classof(const SectionBase *s) { return s->bss; }
+ uint64_t size;
};
class MipsGotSection final : public SyntheticSection {
public:
MipsGotSection();
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
bool updateAllocSize() override;
void finalizeContents() override;
- bool empty() const override;
+ bool isNeeded() const override;
// Join separate GOTs built for each input file to generate
// primary and optional multiple secondary GOTs.
- template <class ELFT> void build();
+ void build();
- void addEntry(InputFile &File, Symbol &Sym, int64_t Addend, RelExpr Expr);
- void addDynTlsEntry(InputFile &File, Symbol &Sym);
- void addTlsIndex(InputFile &File);
+ void addEntry(InputFile &file, Symbol &sym, int64_t addend, RelExpr expr);
+ void addDynTlsEntry(InputFile &file, Symbol &sym);
+ void addTlsIndex(InputFile &file);
- uint64_t getPageEntryOffset(const InputFile *F, const Symbol &S,
- int64_t Addend) const;
- uint64_t getSymEntryOffset(const InputFile *F, const Symbol &S,
- int64_t Addend) const;
- uint64_t getGlobalDynOffset(const InputFile *F, const Symbol &S) const;
- uint64_t getTlsIndexOffset(const InputFile *F) const;
+ uint64_t getPageEntryOffset(const InputFile *f, const Symbol &s,
+ int64_t addend) const;
+ uint64_t getSymEntryOffset(const InputFile *f, const Symbol &s,
+ int64_t addend) const;
+ uint64_t getGlobalDynOffset(const InputFile *f, const Symbol &s) const;
+ uint64_t getTlsIndexOffset(const InputFile *f) const;
// Returns the symbol which corresponds to the first entry of the global part
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
@@ -215,7 +224,7 @@ public:
unsigned getLocalEntriesNum() const;
// Return _gp value for primary GOT (nullptr) or particular input file.
- uint64_t getGp(const InputFile *F = nullptr) const;
+ uint64_t getGp(const InputFile *f = nullptr) const;
private:
// MIPS GOT consists of three parts: local, global and tls. Each part
@@ -305,34 +314,35 @@ private:
// https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT
// Number of "Header" entries.
- static const unsigned HeaderEntriesNum = 2;
+ static const unsigned headerEntriesNum = 2;
- uint64_t Size = 0;
+ uint64_t size = 0;
// Symbol and addend.
- typedef std::pair<Symbol *, int64_t> GotEntry;
+ using GotEntry = std::pair<Symbol *, int64_t>;
struct FileGot {
- InputFile *File = nullptr;
- size_t StartIndex = 0;
+ InputFile *file = nullptr;
+ size_t startIndex = 0;
struct PageBlock {
- size_t FirstIndex = 0;
- size_t Count = 0;
+ size_t firstIndex;
+ size_t count;
+ PageBlock() : firstIndex(0), count(0) {}
};
// Map output sections referenced by MIPS GOT relocations
// to the description (index/count) "page" entries allocated
// for this section.
- llvm::SmallMapVector<const OutputSection *, PageBlock, 16> PagesMap;
+ llvm::SmallMapVector<const OutputSection *, PageBlock, 16> pagesMap;
// Maps from Symbol+Addend pair or just Symbol to the GOT entry index.
- llvm::MapVector<GotEntry, size_t> Local16;
- llvm::MapVector<GotEntry, size_t> Local32;
- llvm::MapVector<Symbol *, size_t> Global;
- llvm::MapVector<Symbol *, size_t> Relocs;
- llvm::MapVector<Symbol *, size_t> Tls;
+ llvm::MapVector<GotEntry, size_t> local16;
+ llvm::MapVector<GotEntry, size_t> local32;
+ llvm::MapVector<Symbol *, size_t> global;
+ llvm::MapVector<Symbol *, size_t> relocs;
+ llvm::MapVector<Symbol *, size_t> tls;
// Set of symbols referenced by dynamic TLS relocations.
- llvm::MapVector<Symbol *, size_t> DynTlsSymbols;
+ llvm::MapVector<Symbol *, size_t> dynTlsSymbols;
// Total number of all entries.
size_t getEntriesNum() const;
@@ -345,27 +355,31 @@ private:
// Container of GOT created for each input file.
// After building a final series of GOTs this container
// holds primary and secondary GOT's.
- std::vector<FileGot> Gots;
+ std::vector<FileGot> gots;
// Return (and create if necessary) `FileGot`.
- FileGot &getGot(InputFile &F);
+ FileGot &getGot(InputFile &f);
// 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.
- bool tryMergeGots(FileGot & Dst, FileGot & Src, bool IsPrimary);
+ bool tryMergeGots(FileGot & dst, FileGot & src, bool isPrimary);
};
class GotPltSection final : public SyntheticSection {
public:
GotPltSection();
- void addEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override;
+
+ // Flag to force GotPlt to be in output if we have relocations
+ // that relies on its address.
+ bool hasGotPltOffRel = false;
private:
- std::vector<const Symbol *> Entries;
+ std::vector<const Symbol *> entries;
};
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
@@ -375,167 +389,164 @@ private:
class IgotPltSection final : public SyntheticSection {
public:
IgotPltSection();
- void addEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override { return Entries.empty(); }
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override { return !entries.empty(); }
private:
- std::vector<const Symbol *> Entries;
+ std::vector<const Symbol *> entries;
};
class StringTableSection final : public SyntheticSection {
public:
- StringTableSection(StringRef Name, bool Dynamic);
- unsigned addString(StringRef S, bool HashIt = true);
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
- bool isDynamic() const { return Dynamic; }
+ StringTableSection(StringRef name, bool dynamic);
+ unsigned addString(StringRef s, bool hashIt = true);
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
+ bool isDynamic() const { return dynamic; }
private:
- const bool Dynamic;
+ const bool dynamic;
- uint64_t Size = 0;
+ uint64_t size = 0;
- llvm::DenseMap<StringRef, unsigned> StringMap;
- std::vector<StringRef> Strings;
+ llvm::DenseMap<StringRef, unsigned> stringMap;
+ std::vector<StringRef> strings;
};
class DynamicReloc {
public:
- DynamicReloc(RelType Type, const InputSectionBase *InputSec,
- uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, int64_t Addend)
- : Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
- UseSymVA(UseSymVA), Addend(Addend), OutputSec(nullptr) {}
+ DynamicReloc(RelType type, const InputSectionBase *inputSec,
+ uint64_t offsetInSec, bool useSymVA, Symbol *sym, int64_t addend)
+ : type(type), sym(sym), inputSec(inputSec), offsetInSec(offsetInSec),
+ useSymVA(useSymVA), addend(addend), outputSec(nullptr) {}
// This constructor records dynamic relocation settings used by MIPS
// multi-GOT implementation. It's to relocate addresses of 64kb pages
// lie inside the output section.
- DynamicReloc(RelType Type, const InputSectionBase *InputSec,
- uint64_t OffsetInSec, const OutputSection *OutputSec,
- int64_t Addend)
- : Type(Type), Sym(nullptr), InputSec(InputSec), OffsetInSec(OffsetInSec),
- UseSymVA(false), Addend(Addend), OutputSec(OutputSec) {}
+ DynamicReloc(RelType type, const InputSectionBase *inputSec,
+ uint64_t offsetInSec, const OutputSection *outputSec,
+ int64_t addend)
+ : type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec),
+ useSymVA(false), addend(addend), outputSec(outputSec) {}
uint64_t getOffset() const;
- uint32_t getSymIndex() const;
- const InputSectionBase *getInputSec() const { return InputSec; }
+ uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
// Computes the addend of the dynamic relocation. Note that this is not the
- // same as the Addend member variable as it also includes the symbol address
- // if UseSymVA is true.
+ // same as the addend member variable as it also includes the symbol address
+ // if useSymVA is true.
int64_t computeAddend() const;
- RelType Type;
+ RelType type;
-private:
- Symbol *Sym;
- const InputSectionBase *InputSec = nullptr;
- uint64_t OffsetInSec;
+ Symbol *sym;
+ const InputSectionBase *inputSec = nullptr;
+ uint64_t offsetInSec;
// If this member is true, the dynamic relocation will not be against the
// symbol but will instead be a relative relocation that simply adds the
// load address. This means we need to write the symbol virtual address
// plus the original addend as the final relocation addend.
- bool UseSymVA;
- int64_t Addend;
- const OutputSection *OutputSec;
+ bool useSymVA;
+ int64_t addend;
+ const OutputSection *outputSec;
};
template <class ELFT> class DynamicSection final : public SyntheticSection {
- typedef typename ELFT::Dyn Elf_Dyn;
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
- typedef typename ELFT::Relr Elf_Relr;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
+ using Elf_Dyn = typename ELFT::Dyn;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
+ using Elf_Relr = typename ELFT::Relr;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
// finalizeContents() fills this vector with the section contents.
- std::vector<std::pair<int32_t, std::function<uint64_t()>>> Entries;
+ std::vector<std::pair<int32_t, std::function<uint64_t()>>> entries;
public:
DynamicSection();
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
private:
- void add(int32_t Tag, std::function<uint64_t()> Fn);
- void addInt(int32_t Tag, uint64_t Val);
- void addInSec(int32_t Tag, InputSection *Sec);
- void addInSecRelative(int32_t Tag, InputSection *Sec);
- void addOutSec(int32_t Tag, OutputSection *Sec);
- void addSize(int32_t Tag, OutputSection *Sec);
- void addSym(int32_t Tag, Symbol *Sym);
-
- uint64_t Size = 0;
+ void add(int32_t tag, std::function<uint64_t()> fn);
+ void addInt(int32_t tag, uint64_t val);
+ void addInSec(int32_t tag, InputSection *sec);
+ void addInSecRelative(int32_t tag, InputSection *sec);
+ void addOutSec(int32_t tag, OutputSection *sec);
+ void addSize(int32_t tag, OutputSection *sec);
+ void addSym(int32_t tag, Symbol *sym);
+
+ uint64_t size = 0;
};
class RelocationBaseSection : public SyntheticSection {
public:
- RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag,
- int32_t SizeDynamicTag);
- void addReloc(RelType DynType, InputSectionBase *IS, uint64_t OffsetInSec,
- Symbol *Sym);
+ RelocationBaseSection(StringRef name, uint32_t type, int32_t dynamicTag,
+ int32_t sizeDynamicTag);
+ void addReloc(RelType dynType, InputSectionBase *isec, uint64_t offsetInSec,
+ Symbol *sym);
// Add a dynamic relocation that might need an addend. This takes care of
// writing the addend to the output section if needed.
- void addReloc(RelType DynType, InputSectionBase *InputSec,
- uint64_t OffsetInSec, Symbol *Sym, int64_t Addend, RelExpr Expr,
- RelType Type);
- void addReloc(const DynamicReloc &Reloc);
- bool empty() const override { return Relocs.empty(); }
- size_t getSize() const override { return Relocs.size() * this->Entsize; }
- size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
+ void addReloc(RelType dynType, InputSectionBase *inputSec,
+ uint64_t offsetInSec, Symbol *sym, int64_t addend, RelExpr expr,
+ RelType type);
+ void addReloc(const DynamicReloc &reloc);
+ bool isNeeded() const override { return !relocs.empty(); }
+ size_t getSize() const override { return relocs.size() * this->entsize; }
+ size_t getRelativeRelocCount() const { return numRelativeRelocs; }
void finalizeContents() override;
- int32_t DynamicTag, SizeDynamicTag;
+ int32_t dynamicTag, sizeDynamicTag;
+ std::vector<DynamicReloc> relocs;
protected:
- std::vector<DynamicReloc> Relocs;
- size_t NumRelativeRelocs = 0;
+ size_t numRelativeRelocs = 0;
};
template <class ELFT>
class RelocationSection final : public RelocationBaseSection {
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
public:
- RelocationSection(StringRef Name, bool Sort);
- unsigned getRelocOffset();
- void writeTo(uint8_t *Buf) override;
+ RelocationSection(StringRef name, bool sort);
+ void writeTo(uint8_t *buf) override;
private:
- bool Sort;
+ bool sort;
};
template <class ELFT>
class AndroidPackedRelocationSection final : public RelocationBaseSection {
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
public:
- AndroidPackedRelocationSection(StringRef Name);
+ AndroidPackedRelocationSection(StringRef name);
bool updateAllocSize() override;
- size_t getSize() const override { return RelocData.size(); }
- void writeTo(uint8_t *Buf) override {
- memcpy(Buf, RelocData.data(), RelocData.size());
+ size_t getSize() const override { return relocData.size(); }
+ void writeTo(uint8_t *buf) override {
+ memcpy(buf, relocData.data(), relocData.size());
}
private:
- SmallVector<char, 0> RelocData;
+ SmallVector<char, 0> relocData;
};
struct RelativeReloc {
- uint64_t getOffset() const { return InputSec->getVA(OffsetInSec); }
+ uint64_t getOffset() const { return inputSec->getVA(offsetInSec); }
- const InputSectionBase *InputSec;
- uint64_t OffsetInSec;
+ const InputSectionBase *inputSec;
+ uint64_t offsetInSec;
};
class RelrBaseSection : public SyntheticSection {
public:
RelrBaseSection();
- bool empty() const override { return Relocs.empty(); }
- std::vector<RelativeReloc> Relocs;
+ bool isNeeded() const override { return !relocs.empty(); }
+ std::vector<RelativeReloc> relocs;
};
// RelrSection is used to encode offsets for relative relocations.
@@ -543,65 +554,65 @@ public:
// https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
// For more details, see the comment in RelrSection::updateAllocSize().
template <class ELFT> class RelrSection final : public RelrBaseSection {
- typedef typename ELFT::Relr Elf_Relr;
+ using Elf_Relr = typename ELFT::Relr;
public:
RelrSection();
bool updateAllocSize() override;
- size_t getSize() const override { return RelrRelocs.size() * this->Entsize; }
- void writeTo(uint8_t *Buf) override {
- memcpy(Buf, RelrRelocs.data(), getSize());
+ size_t getSize() const override { return relrRelocs.size() * this->entsize; }
+ void writeTo(uint8_t *buf) override {
+ memcpy(buf, relrRelocs.data(), getSize());
}
private:
- std::vector<Elf_Relr> RelrRelocs;
+ std::vector<Elf_Relr> relrRelocs;
};
struct SymbolTableEntry {
- Symbol *Sym;
- size_t StrTabOffset;
+ Symbol *sym;
+ size_t strTabOffset;
};
class SymbolTableBaseSection : public SyntheticSection {
public:
- SymbolTableBaseSection(StringTableSection &StrTabSec);
+ SymbolTableBaseSection(StringTableSection &strTabSec);
void finalizeContents() override;
- size_t getSize() const override { return getNumSymbols() * Entsize; }
- void addSymbol(Symbol *Sym);
- unsigned getNumSymbols() const { return Symbols.size() + 1; }
- size_t getSymbolIndex(Symbol *Sym);
- ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
+ size_t getSize() const override { return getNumSymbols() * entsize; }
+ void addSymbol(Symbol *sym);
+ unsigned getNumSymbols() const { return symbols.size() + 1; }
+ size_t getSymbolIndex(Symbol *sym);
+ ArrayRef<SymbolTableEntry> getSymbols() const { return symbols; }
protected:
void sortSymTabSymbols();
// A vector of symbols and their string table offsets.
- std::vector<SymbolTableEntry> Symbols;
+ std::vector<SymbolTableEntry> symbols;
- StringTableSection &StrTabSec;
+ StringTableSection &strTabSec;
- llvm::once_flag OnceFlag;
- llvm::DenseMap<Symbol *, size_t> SymbolIndexMap;
- llvm::DenseMap<OutputSection *, size_t> SectionIndexMap;
+ llvm::once_flag onceFlag;
+ llvm::DenseMap<Symbol *, size_t> symbolIndexMap;
+ llvm::DenseMap<OutputSection *, size_t> sectionIndexMap;
};
template <class ELFT>
class SymbolTableSection final : public SymbolTableBaseSection {
- typedef typename ELFT::Sym Elf_Sym;
+ using Elf_Sym = typename ELFT::Sym;
public:
- SymbolTableSection(StringTableSection &StrTabSec);
- void writeTo(uint8_t *Buf) override;
+ SymbolTableSection(StringTableSection &strTabSec);
+ void writeTo(uint8_t *buf) override;
};
class SymtabShndxSection final : public SyntheticSection {
public:
SymtabShndxSection();
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- bool empty() const override;
+ bool isNeeded() const override;
void finalizeContents() override;
};
@@ -611,42 +622,42 @@ class GnuHashTableSection final : public SyntheticSection {
public:
GnuHashTableSection();
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
// Adds symbols to the hash table.
// Sorts the input to satisfy GNU hash section requirements.
- void addSymbols(std::vector<SymbolTableEntry> &Symbols);
+ void addSymbols(std::vector<SymbolTableEntry> &symbols);
private:
// See the comment in writeBloomFilter.
enum { Shift2 = 26 };
- void writeBloomFilter(uint8_t *Buf);
- void writeHashTable(uint8_t *Buf);
+ void writeBloomFilter(uint8_t *buf);
+ void writeHashTable(uint8_t *buf);
struct Entry {
- Symbol *Sym;
- size_t StrTabOffset;
- uint32_t Hash;
- uint32_t BucketIdx;
+ Symbol *sym;
+ size_t strTabOffset;
+ uint32_t hash;
+ uint32_t bucketIdx;
};
- std::vector<Entry> Symbols;
- size_t MaskWords;
- size_t NBuckets = 0;
- size_t Size = 0;
+ std::vector<Entry> symbols;
+ size_t maskWords;
+ size_t nBuckets = 0;
+ size_t size = 0;
};
class HashTableSection final : public SyntheticSection {
public:
HashTableSection();
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
private:
- size_t Size = 0;
+ size_t size = 0;
};
// The PltSection is used for both the Plt and Iplt. The former usually has a
@@ -655,67 +666,66 @@ private:
// Target->IRelativeRel.
class PltSection : public SyntheticSection {
public:
- PltSection(bool IsIplt);
- void writeTo(uint8_t *Buf) override;
+ PltSection(bool isIplt);
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- bool empty() const override { return Entries.empty(); }
+ bool isNeeded() const override { return !entries.empty(); }
void addSymbols();
- template <class ELFT> void addEntry(Symbol &Sym);
+ template <class ELFT> void addEntry(Symbol &sym);
- size_t HeaderSize;
+ size_t headerSize;
private:
- unsigned getPltRelocOff() const;
- std::vector<std::pair<const Symbol *, unsigned>> Entries;
- bool IsIplt;
+ std::vector<const Symbol *> entries;
+ bool isIplt;
};
class GdbIndexSection final : public SyntheticSection {
public:
struct AddressEntry {
- InputSection *Section;
- uint64_t LowAddress;
- uint64_t HighAddress;
- uint32_t CuIndex;
+ InputSection *section;
+ uint64_t lowAddress;
+ uint64_t highAddress;
+ uint32_t cuIndex;
};
struct CuEntry {
- uint64_t CuOffset;
- uint64_t CuLength;
+ uint64_t cuOffset;
+ uint64_t cuLength;
};
struct NameAttrEntry {
- llvm::CachedHashStringRef Name;
- uint32_t CuIndexAndAttrs;
+ llvm::CachedHashStringRef name;
+ uint32_t cuIndexAndAttrs;
};
struct GdbChunk {
- InputSection *Sec;
- std::vector<AddressEntry> AddressAreas;
- std::vector<CuEntry> CompilationUnits;
+ InputSection *sec;
+ std::vector<AddressEntry> addressAreas;
+ std::vector<CuEntry> compilationUnits;
};
struct GdbSymbol {
- llvm::CachedHashStringRef Name;
- std::vector<uint32_t> CuVector;
- uint32_t NameOff;
- uint32_t CuVectorOff;
+ llvm::CachedHashStringRef name;
+ std::vector<uint32_t> cuVector;
+ uint32_t nameOff;
+ uint32_t cuVectorOff;
};
GdbIndexSection();
template <typename ELFT> static GdbIndexSection *create();
- void writeTo(uint8_t *Buf) override;
- size_t getSize() const override { return Size; }
- bool empty() const override;
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override { return size; }
+ bool isNeeded() const override;
private:
struct GdbIndexHeader {
- llvm::support::ulittle32_t Version;
- llvm::support::ulittle32_t CuListOff;
- llvm::support::ulittle32_t CuTypesOff;
- llvm::support::ulittle32_t AddressAreaOff;
- llvm::support::ulittle32_t SymtabOff;
- llvm::support::ulittle32_t ConstantPoolOff;
+ llvm::support::ulittle32_t version;
+ llvm::support::ulittle32_t cuListOff;
+ llvm::support::ulittle32_t cuTypesOff;
+ llvm::support::ulittle32_t addressAreaOff;
+ llvm::support::ulittle32_t symtabOff;
+ llvm::support::ulittle32_t constantPoolOff;
};
void initOutputSize();
@@ -723,12 +733,12 @@ private:
// Each chunk contains information gathered from debug sections of a
// single object file.
- std::vector<GdbChunk> Chunks;
+ std::vector<GdbChunk> chunks;
// A symbol table for this .gdb_index section.
- std::vector<GdbSymbol> Symbols;
+ std::vector<GdbSymbol> symbols;
- size_t Size;
+ size_t size;
};
// --eh-frame-hdr option tells linker to construct a header for all the
@@ -743,9 +753,10 @@ private:
class EhFrameHeader final : public SyntheticSection {
public:
EhFrameHeader();
- void writeTo(uint8_t *Buf) override;
+ void write();
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- bool empty() const override;
+ bool isNeeded() const override;
};
// For more information about .gnu.version and .gnu.version_r see:
@@ -761,13 +772,15 @@ public:
VersionDefinitionSection();
void finalizeContents() override;
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
private:
enum { EntrySize = 28 };
- void writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff);
+ void writeOne(uint8_t *buf, uint32_t index, StringRef name, size_t nameOff);
+ StringRef getFileDefName();
- unsigned FileDefNameOff;
+ unsigned fileDefNameOff;
+ std::vector<unsigned> verDefNameOffs;
};
// The .gnu.version section specifies the required version of each symbol in the
@@ -776,14 +789,13 @@ private:
// identifier defined in the either .gnu.version_r or .gnu.version_d section.
// The values 0 and 1 are reserved. All other values are used for versions in
// the own object or in any of the dependencies.
-template <class ELFT>
class VersionTableSection final : public SyntheticSection {
public:
VersionTableSection();
void finalizeContents() override;
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override;
};
// The .gnu.version_r section defines the version identifiers used by
@@ -791,25 +803,30 @@ public:
// Elf_Verneed specifies the version requirements for a single DSO, and contains
// a reference to a linked list of Elf_Vernaux data structures which define the
// mapping from version identifiers to version names.
-template <class ELFT> class VersionNeedSection final : public SyntheticSection {
- typedef typename ELFT::Verneed Elf_Verneed;
- typedef typename ELFT::Vernaux Elf_Vernaux;
+template <class ELFT>
+class VersionNeedSection final : public SyntheticSection {
+ using Elf_Verneed = typename ELFT::Verneed;
+ using Elf_Vernaux = typename ELFT::Vernaux;
+
+ struct Vernaux {
+ uint64_t hash;
+ uint32_t verneedIndex;
+ uint64_t nameStrTab;
+ };
- // A vector of shared files that need Elf_Verneed data structures and the
- // string table offsets of their sonames.
- std::vector<std::pair<SharedFile<ELFT> *, size_t>> Needed;
+ struct Verneed {
+ uint64_t nameStrTab;
+ std::vector<Vernaux> vernauxs;
+ };
- // The next available version identifier.
- unsigned NextIndex;
+ std::vector<Verneed> verneeds;
public:
VersionNeedSection();
- void addSymbol(Symbol *Sym);
void finalizeContents() override;
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- size_t getNeedNum() const { return Needed.size(); }
- bool empty() const override;
+ bool isNeeded() const override;
};
// MergeSyntheticSection is a class that allows us to put mergeable sections
@@ -818,36 +835,36 @@ public:
// attached to regular output sections.
class MergeSyntheticSection : public SyntheticSection {
public:
- void addSection(MergeInputSection *MS);
- std::vector<MergeInputSection *> Sections;
+ void addSection(MergeInputSection *ms);
+ std::vector<MergeInputSection *> sections;
protected:
- MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment)
- : SyntheticSection(Flags, Type, Alignment, Name) {}
+ MergeSyntheticSection(StringRef name, uint32_t type, uint64_t flags,
+ uint32_t alignment)
+ : SyntheticSection(flags, type, alignment, name) {}
};
class MergeTailSection final : public MergeSyntheticSection {
public:
- MergeTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment);
+ MergeTailSection(StringRef name, uint32_t type, uint64_t flags,
+ uint32_t alignment);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
void finalizeContents() override;
private:
- llvm::StringTableBuilder Builder;
+ llvm::StringTableBuilder builder;
};
class MergeNoTailSection final : public MergeSyntheticSection {
public:
- MergeNoTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment)
- : MergeSyntheticSection(Name, Type, Flags, Alignment) {}
+ MergeNoTailSection(StringRef name, uint32_t type, uint64_t flags,
+ uint32_t alignment)
+ : MergeSyntheticSection(name, type, flags, alignment) {}
- size_t getSize() const override { return Size; }
- void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
void finalizeContents() override;
private:
@@ -856,67 +873,68 @@ private:
// because DenseMap also uses lower bits to determine a bucket ID.
// If we use lower bits, it significantly increases the probability of
// hash collisons.
- size_t getShardId(uint32_t Hash) {
- return Hash >> (32 - llvm::countTrailingZeros(NumShards));
+ size_t getShardId(uint32_t hash) {
+ assert((hash >> 31) == 0);
+ return hash >> (31 - llvm::countTrailingZeros(numShards));
}
// Section size
- size_t Size;
+ size_t size;
// String table contents
- constexpr static size_t NumShards = 32;
- std::vector<llvm::StringTableBuilder> Shards;
- size_t ShardOffsets[NumShards];
+ constexpr static size_t numShards = 32;
+ std::vector<llvm::StringTableBuilder> shards;
+ size_t shardOffsets[numShards];
};
// .MIPS.abiflags section.
template <class ELFT>
class MipsAbiFlagsSection final : public SyntheticSection {
- typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags;
+ using Elf_Mips_ABIFlags = llvm::object::Elf_Mips_ABIFlags<ELFT>;
public:
static MipsAbiFlagsSection *create();
- MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags);
+ MipsAbiFlagsSection(Elf_Mips_ABIFlags flags);
size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
private:
- Elf_Mips_ABIFlags Flags;
+ Elf_Mips_ABIFlags flags;
};
// .MIPS.options section.
template <class ELFT> class MipsOptionsSection final : public SyntheticSection {
- typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options;
- typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
+ using Elf_Mips_Options = llvm::object::Elf_Mips_Options<ELFT>;
+ using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
public:
static MipsOptionsSection *create();
- MipsOptionsSection(Elf_Mips_RegInfo Reginfo);
- void writeTo(uint8_t *Buf) override;
+ MipsOptionsSection(Elf_Mips_RegInfo reginfo);
+ void writeTo(uint8_t *buf) override;
size_t getSize() const override {
return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
}
private:
- Elf_Mips_RegInfo Reginfo;
+ Elf_Mips_RegInfo reginfo;
};
// MIPS .reginfo section.
template <class ELFT> class MipsReginfoSection final : public SyntheticSection {
- typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
+ using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
public:
static MipsReginfoSection *create();
- MipsReginfoSection(Elf_Mips_RegInfo Reginfo);
+ MipsReginfoSection(Elf_Mips_RegInfo reginfo);
size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
- void writeTo(uint8_t *Buf) override;
+ void writeTo(uint8_t *buf) override;
private:
- Elf_Mips_RegInfo Reginfo;
+ Elf_Mips_RegInfo reginfo;
};
// This is a MIPS specific section to hold a space within the data segment
@@ -926,45 +944,115 @@ private:
class MipsRldMapSection : public SyntheticSection {
public:
MipsRldMapSection();
- size_t getSize() const override { return Config->Wordsize; }
- void writeTo(uint8_t *Buf) override {}
+ size_t getSize() const override { return config->wordsize; }
+ void writeTo(uint8_t *buf) override {}
};
-class ARMExidxSentinelSection : public SyntheticSection {
+// Representation of the combined .ARM.Exidx input sections. We process these
+// as a SyntheticSection like .eh_frame as we need to merge duplicate entries
+// and add terminating sentinel entries.
+//
+// The .ARM.exidx input sections after SHF_LINK_ORDER processing is done form
+// a table that the unwinder can derive (Addresses are encoded as offsets from
+// table):
+// | Address of function | Unwind instructions for function |
+// where the unwind instructions are either a small number of unwind or the
+// special EXIDX_CANTUNWIND entry representing no unwinding information.
+// When an exception is thrown from an address A, the unwinder searches the
+// table for the closest table entry with Address of function <= A. This means
+// that for two consecutive table entries:
+// | A1 | U1 |
+// | A2 | U2 |
+// The range of addresses described by U1 is [A1, A2)
+//
+// There are two cases where we need a linker generated table entry to fixup
+// the address ranges in the table
+// Case 1:
+// - A sentinel entry added with an address higher than all
+// executable sections. This was needed to work around libunwind bug pr31091.
+// - After address assignment we need to find the highest addressed executable
+// section and use the limit of that section so that the unwinder never
+// matches it.
+// Case 2:
+// - InputSections without a .ARM.exidx section (usually from Assembly)
+// need a table entry so that they terminate the range of the previously
+// function. This is pr40277.
+//
+// Instead of storing pointers to the .ARM.exidx InputSections from
+// InputObjects, we store pointers to the executable sections that need
+// .ARM.exidx sections. We can then use the dependentSections of these to
+// either find the .ARM.exidx section or know that we need to generate one.
+class ARMExidxSyntheticSection : public SyntheticSection {
public:
- ARMExidxSentinelSection();
- size_t getSize() const override { return 8; }
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
+ ARMExidxSyntheticSection();
+
+ // Add an input section to the ARMExidxSyntheticSection. Returns whether the
+ // section needs to be removed from the main input section list.
+ bool addSection(InputSection *isec);
- static bool classof(const SectionBase *D);
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override { return !empty; }
+ // Sort and remove duplicate entries.
+ void finalizeContents() override;
+ InputSection *getLinkOrderDep() const;
+
+ static bool classof(const SectionBase *d);
+
+ // Links to the ARMExidxSections so we can transfer the relocations once the
+ // layout is known.
+ std::vector<InputSection *> exidxSections;
- // The last section referenced by a regular .ARM.exidx section.
- // It is found and filled in Writer<ELFT>::resolveShfLinkOrder().
- // The sentinel points at the end of that section.
- InputSection *Highest = nullptr;
+private:
+ size_t size;
+
+ // Empty if ExecutableSections contains no dependent .ARM.exidx sections.
+ bool empty = true;
+
+ // Instead of storing pointers to the .ARM.exidx InputSections from
+ // InputObjects, we store pointers to the executable sections that need
+ // .ARM.exidx sections. We can then use the dependentSections of these to
+ // either find the .ARM.exidx section or know that we need to generate one.
+ std::vector<InputSection *> executableSections;
+
+ // The executable InputSection with the highest address to use for the
+ // sentinel. We store separately from ExecutableSections as merging of
+ // duplicate entries may mean this InputSection is removed from
+ // ExecutableSections.
+ InputSection *sentinel = nullptr;
};
// A container for one or more linker generated thunks. Instances of these
// thunks including ARM interworking and Mips LA25 PI to non-PI thunks.
class ThunkSection : public SyntheticSection {
public:
- // ThunkSection in OS, with desired OutSecOff of Off
- ThunkSection(OutputSection *OS, uint64_t Off);
+ // ThunkSection in OS, with desired outSecOff of Off
+ ThunkSection(OutputSection *os, uint64_t off);
// Add a newly created Thunk to this container:
// Thunk is given offset from start of this InputSection
// Thunk defines a symbol in this InputSection that can be used as target
// of a relocation
- void addThunk(Thunk *T);
- size_t getSize() const override { return Size; }
- void writeTo(uint8_t *Buf) override;
+ void addThunk(Thunk *t);
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
InputSection *getTargetInputSection() const;
bool assignOffsets();
private:
- std::vector<Thunk *> Thunks;
- size_t Size = 0;
+ std::vector<Thunk *> thunks;
+ size_t size = 0;
+};
+
+// Used to compute outSecOff of .got2 in each object file. This is needed to
+// synthesize PLT entries for PPC32 Secure PLT ABI.
+class PPC32Got2Section final : public SyntheticSection {
+public:
+ PPC32Got2Section();
+ size_t getSize() const override { return 0; }
+ bool isNeeded() const override;
+ void finalizeContents() override;
+ void writeTo(uint8_t *buf) override {}
};
// This section is used to store the addresses of functions that are called
@@ -975,15 +1063,48 @@ private:
class PPC64LongBranchTargetSection final : public SyntheticSection {
public:
PPC64LongBranchTargetSection();
- void addEntry(Symbol &Sym);
+ void addEntry(Symbol &sym);
size_t getSize() const override;
- void writeTo(uint8_t *Buf) override;
- bool empty() const override;
- void finalizeContents() override { Finalized = true; }
+ void writeTo(uint8_t *buf) override;
+ bool isNeeded() const override;
+ void finalizeContents() override { finalized = true; }
private:
- std::vector<const Symbol *> Entries;
- bool Finalized = false;
+ std::vector<const Symbol *> entries;
+ bool finalized = false;
+};
+
+template <typename ELFT>
+class PartitionElfHeaderSection : public SyntheticSection {
+public:
+ PartitionElfHeaderSection();
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) override;
+};
+
+template <typename ELFT>
+class PartitionProgramHeadersSection : public SyntheticSection {
+public:
+ PartitionProgramHeadersSection();
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) override;
+};
+
+class PartitionIndexSection : public SyntheticSection {
+public:
+ PartitionIndexSection();
+ size_t getSize() const override;
+ void finalizeContents() override;
+ void writeTo(uint8_t *buf) override;
+};
+
+// Create a dummy .sdata for __global_pointer$ if .sdata does not exist.
+class RISCVSdataSection final : public SyntheticSection {
+public:
+ RISCVSdataSection();
+ size_t getSize() const override { return 0; }
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) override {}
};
InputSection *createInterpSection();
@@ -991,52 +1112,76 @@ MergeInputSection *createCommentSection();
template <class ELFT> void splitSections();
void mergeSections();
-Defined *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section);
-
-// Linker generated sections which can be used as inputs.
-struct InStruct {
- InputSection *ARMAttributes;
- BssSection *Bss;
- BssSection *BssRelRo;
- BuildIdSection *BuildId;
- EhFrameHeader *EhFrameHdr;
- EhFrameSection *EhFrame;
- SyntheticSection *Dynamic;
- StringTableSection *DynStrTab;
- SymbolTableBaseSection *DynSymTab;
- GnuHashTableSection *GnuHashTab;
- HashTableSection *HashTab;
- InputSection *Interp;
- GdbIndexSection *GdbIndex;
- GotSection *Got;
- GotPltSection *GotPlt;
- IgotPltSection *IgotPlt;
- PPC64LongBranchTargetSection *PPC64LongBranchTarget;
- MipsGotSection *MipsGot;
- MipsRldMapSection *MipsRldMap;
- PltSection *Plt;
- PltSection *Iplt;
- RelocationBaseSection *RelaDyn;
- RelrBaseSection *RelrDyn;
- RelocationBaseSection *RelaPlt;
- RelocationBaseSection *RelaIplt;
- StringTableSection *ShStrTab;
- StringTableSection *StrTab;
- SymbolTableBaseSection *SymTab;
- SymtabShndxSection *SymTabShndx;
- VersionDefinitionSection *VerDef;
+template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
+template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
+
+Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
+ uint64_t size, InputSectionBase &section);
+
+void addVerneed(Symbol *ss);
+
+// Linker generated per-partition sections.
+struct Partition {
+ StringRef name;
+ uint64_t nameStrTab;
+
+ SyntheticSection *elfHeader;
+ SyntheticSection *programHeaders;
+ std::vector<PhdrEntry *> phdrs;
+
+ ARMExidxSyntheticSection *armExidx;
+ BuildIdSection *buildId;
+ SyntheticSection *dynamic;
+ StringTableSection *dynStrTab;
+ SymbolTableBaseSection *dynSymTab;
+ EhFrameHeader *ehFrameHdr;
+ EhFrameSection *ehFrame;
+ GnuHashTableSection *gnuHashTab;
+ HashTableSection *hashTab;
+ RelocationBaseSection *relaDyn;
+ RelrBaseSection *relrDyn;
+ VersionDefinitionSection *verDef;
+ SyntheticSection *verNeed;
+ VersionTableSection *verSym;
+
+ unsigned getNumber() const { return this - &partitions[0] + 1; }
};
-extern InStruct In;
+extern Partition *mainPart;
-template <class ELFT> struct InX {
- static VersionTableSection<ELFT> *VerSym;
- static VersionNeedSection<ELFT> *VerNeed;
+inline Partition &SectionBase::getPartition() const {
+ assert(isLive());
+ return partitions[partition - 1];
+}
+
+// Linker generated sections which can be used as inputs and are not specific to
+// a partition.
+struct InStruct {
+ InputSection *armAttributes;
+ BssSection *bss;
+ BssSection *bssRelRo;
+ GotSection *got;
+ GotPltSection *gotPlt;
+ IgotPltSection *igotPlt;
+ PPC64LongBranchTargetSection *ppc64LongBranchTarget;
+ MipsGotSection *mipsGot;
+ MipsRldMapSection *mipsRldMap;
+ SyntheticSection *partEnd;
+ SyntheticSection *partIndex;
+ PltSection *plt;
+ PltSection *iplt;
+ PPC32Got2Section *ppc32Got2;
+ RISCVSdataSection *riscvSdata;
+ RelocationBaseSection *relaPlt;
+ RelocationBaseSection *relaIplt;
+ StringTableSection *shStrTab;
+ StringTableSection *strTab;
+ SymbolTableBaseSection *symTab;
+ SymtabShndxSection *symTabShndx;
};
-template <class ELFT> VersionTableSection<ELFT> *InX<ELFT>::VerSym;
-template <class ELFT> VersionNeedSection<ELFT> *InX<ELFT>::VerNeed;
+extern InStruct in;
+
} // namespace elf
} // namespace lld
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index 01073a62cfd6..d07478a5178c 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -1,9 +1,8 @@
//===- Target.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -38,17 +37,17 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-TargetInfo *elf::Target;
+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;
+std::string lld::toString(RelType type) {
+ StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
+ if (s == "Unknown")
+ return ("Unknown (" + Twine(type) + ")").str();
+ return s;
}
TargetInfo *elf::getTarget() {
- switch (Config->EMachine) {
+ switch (config->emachine) {
case EM_386:
case EM_IAMCU:
return getX86TargetInfo();
@@ -63,7 +62,7 @@ TargetInfo *elf::getTarget() {
case EM_HEXAGON:
return getHexagonTargetInfo();
case EM_MIPS:
- switch (Config->EKind) {
+ switch (config->ekind) {
case ELF32LEKind:
return getMipsTargetInfo<ELF32LE>();
case ELF32BEKind:
@@ -86,36 +85,34 @@ TargetInfo *elf::getTarget() {
case EM_SPARCV9:
return getSPARCV9TargetInfo();
case EM_X86_64:
- if (Config->EKind == ELF32LEKind)
- return getX32TargetInfo();
return getX86_64TargetInfo();
}
llvm_unreachable("unknown target machine");
}
-template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) {
- for (InputSectionBase *D : InputSections) {
- auto *IS = cast<InputSection>(D);
- if (!IS->getParent())
+template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
+ for (InputSectionBase *d : inputSections) {
+ auto *isec = cast<InputSection>(d);
+ if (!isec->getParent())
continue;
- uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff;
- if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
- return {IS, IS->template getLocation<ELFT>(Loc - ISLoc) + ": "};
+ uint8_t *isecLoc = Out::bufferStart + isec->getParent()->offset + isec->outSecOff;
+ if (isecLoc <= loc && loc < isecLoc + isec->getSize())
+ return {isec, isec->template getLocation<ELFT>(loc - isecLoc) + ": "};
}
return {};
}
-ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
- switch (Config->EKind) {
+ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
+ switch (config->ekind) {
case ELF32LEKind:
- return getErrPlace<ELF32LE>(Loc);
+ return getErrPlace<ELF32LE>(loc);
case ELF32BEKind:
- return getErrPlace<ELF32BE>(Loc);
+ return getErrPlace<ELF32BE>(loc);
case ELF64LEKind:
- return getErrPlace<ELF64LE>(Loc);
+ return getErrPlace<ELF64LE>(loc);
case ELF64BEKind:
- return getErrPlace<ELF64BE>(Loc);
+ return getErrPlace<ELF64BE>(loc);
default:
llvm_unreachable("unknown ELF type");
}
@@ -123,62 +120,62 @@ ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
TargetInfo::~TargetInfo() {}
-int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
+int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const {
return 0;
}
-bool TargetInfo::usesOnlyLowPageBits(RelType Type) const { return false; }
+bool TargetInfo::usesOnlyLowPageBits(RelType type) const { return false; }
-bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
- uint64_t BranchAddr, const Symbol &S) const {
+bool TargetInfo::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s) const {
return false;
}
-bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const {
+bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const {
llvm_unreachable("Target doesn't support split stacks.");
}
-bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+bool TargetInfo::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
return true;
}
-void TargetInfo::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
- writeGotPlt(Buf, S);
+void TargetInfo::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
+ writeGotPlt(buf, s);
}
-RelExpr TargetInfo::adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const {
- return Expr;
+RelExpr TargetInfo::adjustRelaxExpr(RelType type, const uint8_t *data,
+ RelExpr expr) const {
+ return expr;
}
-void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const {
+void TargetInfo::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsGdToLe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsGdToIe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsIeToLe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
- uint64_t Val) const {
+void TargetInfo::relaxTlsLdToLe(uint8_t *loc, RelType type,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-uint64_t TargetInfo::getImageBase() {
+uint64_t TargetInfo::getImageBase() const {
// Use -image-base if set. Fall back to the target default if not.
- if (Config->ImageBase)
- return *Config->ImageBase;
- return Config->Pic ? 0 : DefaultImageBase;
+ if (config->imageBase)
+ return *config->imageBase;
+ return config->isPic ? 0 : defaultImageBase;
}
diff --git a/ELF/Target.h b/ELF/Target.h
index 685ad05ecd66..effa6001f6d9 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -1,9 +1,8 @@
//===- Target.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -17,7 +16,7 @@
#include <array>
namespace lld {
-std::string toString(elf::RelType Type);
+std::string toString(elf::RelType type);
namespace elf {
class Defined;
@@ -27,36 +26,39 @@ class Symbol;
class TargetInfo {
public:
virtual uint32_t calcEFlags() const { return 0; }
- virtual RelType getDynRel(RelType Type) const { return Type; }
- virtual void writeGotPltHeader(uint8_t *Buf) const {}
- virtual void writeGotHeader(uint8_t *Buf) const {}
- virtual void writeGotPlt(uint8_t *Buf, const Symbol &S) const {};
- virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) const;
- virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const;
+ virtual RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const = 0;
+ virtual RelType getDynRel(RelType type) const { return 0; }
+ virtual void writeGotPltHeader(uint8_t *buf) const {}
+ virtual void writeGotHeader(uint8_t *buf) const {}
+ virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {};
+ virtual void writeIgotPlt(uint8_t *buf, const Symbol &s) const;
+ virtual int64_t getImplicitAddend(const uint8_t *buf, RelType type) const;
+ virtual int getTlsGdRelaxSkip(RelType type) const { return 1; }
// If lazy binding is supported, the first entry of the PLT has code
// to call the dynamic linker to resolve PLT entries the first time
// they are called. This function writes that code.
- virtual void writePltHeader(uint8_t *Buf) const {}
+ virtual void writePltHeader(uint8_t *buf) const {}
- virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {}
- virtual void addPltHeaderSymbols(InputSection &IS) const {}
- virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {}
+ virtual void writePlt(uint8_t *buf, uint64_t gotEntryAddr,
+ uint64_t pltEntryAddr, int32_t index,
+ unsigned relOff) const {}
+ virtual void addPltHeaderSymbols(InputSection &isec) const {}
+ virtual void addPltSymbols(InputSection &isec, uint64_t off) const {}
// Returns true if a relocation only uses the low bits of a value such that
// all those bits are in the same page. For example, if the relocation
// only uses the low 12 bits in a system with 4k pages. If this is true, the
// bits will always have the same value at runtime and we don't have to emit
// a dynamic relocation.
- virtual bool usesOnlyLowPageBits(RelType Type) const;
+ virtual bool usesOnlyLowPageBits(RelType type) const;
// Decide whether a Thunk is needed for the relocation from File
// targeting S.
- virtual bool needsThunk(RelExpr Expr, RelType RelocType,
- const InputFile *File, uint64_t BranchAddr,
- const Symbol &S) const;
+ virtual bool needsThunk(RelExpr expr, RelType relocType,
+ const InputFile *file, uint64_t branchAddr,
+ const Symbol &s) const;
// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.
@@ -68,77 +70,71 @@ public:
// to do the right thing. See https://gcc.gnu.org/wiki/SplitStacks.
// The symbols st_other flags are needed on PowerPC64 for determining the
// offset to the split-stack prologue.
- virtual bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
- uint8_t StOther) const;
+ virtual bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const;
- // Return true if we can reach Dst from Src with Relocation RelocType
- virtual bool inBranchRange(RelType Type, uint64_t Src,
- uint64_t Dst) const;
- virtual RelExpr getRelExpr(RelType Type, const Symbol &S,
- const uint8_t *Loc) const = 0;
+ // Return true if we can reach dst from src with RelType type.
+ 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 relocateOne(uint8_t *loc, RelType type, uint64_t val) const = 0;
virtual ~TargetInfo();
- unsigned TlsGdRelaxSkip = 1;
- unsigned PageSize = 4096;
- unsigned DefaultMaxPageSize = 4096;
+ unsigned defaultCommonPageSize = 4096;
+ unsigned defaultMaxPageSize = 4096;
- uint64_t getImageBase();
+ uint64_t getImageBase() const;
- // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got or .got.plt section.
- uint64_t GotBaseSymOff = 0;
// True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
- bool GotBaseSymInGotPlt = true;
-
- RelType CopyRel;
- RelType GotRel;
- RelType NoneRel;
- RelType PltRel;
- RelType RelativeRel;
- RelType IRelativeRel;
- RelType TlsDescRel;
- RelType TlsGotRel;
- RelType TlsModuleIndexRel;
- RelType TlsOffsetRel;
- unsigned GotEntrySize = 0;
- unsigned GotPltEntrySize = 0;
- unsigned PltEntrySize;
- unsigned PltHeaderSize;
+ bool gotBaseSymInGotPlt = true;
+
+ RelType copyRel;
+ RelType gotRel;
+ RelType noneRel;
+ RelType pltRel;
+ RelType relativeRel;
+ RelType iRelativeRel;
+ RelType symbolicRel;
+ RelType tlsDescRel;
+ RelType tlsGotRel;
+ RelType tlsModuleIndexRel;
+ RelType tlsOffsetRel;
+ unsigned pltEntrySize;
+ unsigned pltHeaderSize;
// At least on x86_64 positions 1 and 2 are used by the first plt entry
// to support lazy loading.
- unsigned GotPltHeaderEntriesNum = 3;
+ unsigned gotPltHeaderEntriesNum = 3;
// On PPC ELF V2 abi, the first entry in the .got is the .TOC.
- unsigned GotHeaderEntriesNum = 0;
+ unsigned gotHeaderEntriesNum = 0;
- bool NeedsThunks = false;
+ bool needsThunks = false;
// A 4-byte field corresponding to one or more trap instructions, used to pad
// executable OutputSections.
- std::array<uint8_t, 4> TrapInstr;
+ std::array<uint8_t, 4> trapInstr;
// 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.
- bool NeedsMoreStackNonSplit = true;
+ bool needsMoreStackNonSplit = true;
- virtual RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
- RelExpr Expr) const;
- virtual void relaxGot(uint8_t *Loc, 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 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;
protected:
// On FreeBSD x86_64 the first page cannot be mmaped.
// On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
// installs that is 65536, so the first 15 pages cannot be used.
// Given that, the smallest value that can be used in here is 0x10000.
- uint64_t DefaultImageBase = 0x10000;
+ uint64_t defaultImageBase = 0x10000;
};
TargetInfo *getAArch64TargetInfo();
@@ -151,108 +147,113 @@ TargetInfo *getPPC64TargetInfo();
TargetInfo *getPPCTargetInfo();
TargetInfo *getRISCVTargetInfo();
TargetInfo *getSPARCV9TargetInfo();
-TargetInfo *getX32TargetInfo();
TargetInfo *getX86TargetInfo();
TargetInfo *getX86_64TargetInfo();
template <class ELFT> TargetInfo *getMipsTargetInfo();
struct ErrorPlace {
- InputSectionBase *IS;
- std::string Loc;
+ InputSectionBase *isec;
+ std::string loc;
};
// Returns input section and corresponding source string for the given location.
-ErrorPlace getErrorPlace(const uint8_t *Loc);
+ErrorPlace getErrorPlace(const uint8_t *loc);
-static inline std::string getErrorLocation(const uint8_t *Loc) {
- return getErrorPlace(Loc).Loc;
+static inline std::string getErrorLocation(const uint8_t *loc) {
+ return getErrorPlace(loc).loc;
}
-// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first is
-// a global entry point (GEP) which typically is used to intiailzie the TOC
+void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
+
+bool tryRelaxPPC64TocIndirection(RelType type, 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
+// is a global entry point (GEP) which typically is used to initialize the TOC
// pointer in general purpose register 2. The second is a local entry
// point (LEP) which bypasses the TOC pointer initialization code. The
// offset between GEP and LEP is encoded in a function's st_other flags.
// This function will return the offset (in bytes) from the global entry-point
// to the local entry-point.
-unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther);
+unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther);
+
+// Returns true if a relocation is a small code model relocation that accesses
+// the .toc section.
+bool isPPC64SmallCodeModelTocReloc(RelType type);
uint64_t getPPC64TocBase();
-uint64_t getAArch64Page(uint64_t Expr);
+uint64_t getAArch64Page(uint64_t expr);
-extern TargetInfo *Target;
+extern const TargetInfo *target;
TargetInfo *getTarget();
-template <class ELFT> bool isMipsPIC(const Defined *Sym);
+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.IS && ErrPlace.IS->Name.startswith(".debug"))
- Hint = "; consider recompiling with -fdebug-types-section to reduce size "
+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);
-}
-
-inline unsigned getPltEntryOffset(unsigned Idx) {
- return Target->PltHeaderSize + Target->PltEntrySize * Idx;
+ errorOrWarn(errPlace.loc + "relocation " + lld::toString(type) +
+ " out of range: " + v.str() + " is not in [" + Twine(min).str() +
+ ", " + Twine(max).str() + "]" + hint);
}
// 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) {
- if (V != llvm::SignExtend64(V, N))
- reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
+inline void checkInt(uint8_t *loc, int64_t v, int n, RelType type) {
+ if (v != llvm::SignExtend64(v, n))
+ reportRangeError(loc, type, 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) {
- if ((V >> N) != 0)
- reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N));
+inline void checkUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
+ if ((v >> n) != 0)
+ reportRangeError(loc, type, 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, RelType type) {
// 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),
- llvm::maxIntN(N));
+ if (v != (uint64_t)llvm::SignExtend64(v, n) && (v >> n) != 0)
+ reportRangeError(loc, type, Twine((int64_t)v), llvm::minIntN(n),
+ llvm::maxUIntN(n));
}
-inline void checkAlignment(uint8_t *Loc, uint64_t V, int N, RelType Type) {
- if ((V & (N - 1)) != 0)
- error(getErrorLocation(Loc) + "improper alignment for relocation " +
- lld::toString(Type) + ": 0x" + llvm::utohexstr(V) +
- " is not aligned to " + Twine(N) + " bytes");
+inline void checkAlignment(uint8_t *loc, uint64_t v, int n, RelType type) {
+ if ((v & (n - 1)) != 0)
+ error(getErrorLocation(loc) + "improper alignment for relocation " +
+ lld::toString(type) + ": 0x" + llvm::utohexstr(v) +
+ " is not aligned to " + Twine(n) + " bytes");
}
// Endianness-aware read/write.
-inline uint16_t read16(const void *P) {
- return llvm::support::endian::read16(P, Config->Endianness);
+inline uint16_t read16(const void *p) {
+ return llvm::support::endian::read16(p, config->endianness);
}
-inline uint32_t read32(const void *P) {
- return llvm::support::endian::read32(P, Config->Endianness);
+inline uint32_t read32(const void *p) {
+ return llvm::support::endian::read32(p, config->endianness);
}
-inline uint64_t read64(const void *P) {
- return llvm::support::endian::read64(P, Config->Endianness);
+inline uint64_t read64(const void *p) {
+ return llvm::support::endian::read64(p, config->endianness);
}
-inline void write16(void *P, uint16_t V) {
- llvm::support::endian::write16(P, V, Config->Endianness);
+inline void write16(void *p, uint16_t v) {
+ llvm::support::endian::write16(p, v, config->endianness);
}
-inline void write32(void *P, uint32_t V) {
- llvm::support::endian::write32(P, V, Config->Endianness);
+inline void write32(void *p, uint32_t v) {
+ llvm::support::endian::write32(p, v, config->endianness);
}
-inline void write64(void *P, uint64_t V) {
- llvm::support::endian::write64(P, V, Config->Endianness);
+inline void write64(void *p, uint64_t v) {
+ llvm::support::endian::write64(p, v, config->endianness);
}
} // namespace elf
} // namespace lld
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
index 95b57dc0db42..73208f932031 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -1,9 +1,8 @@
//===- Thunks.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===---------------------------------------------------------------------===//
//
@@ -50,18 +49,18 @@ namespace {
// AArch64 long range Thunks
class AArch64ABSLongThunk final : public Thunk {
public:
- AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
+ AArch64ABSLongThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 16; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class AArch64ADRPThunk final : public Thunk {
public:
- AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
+ AArch64ADRPThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 12; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// Base class for ARM thunks.
@@ -74,18 +73,19 @@ public:
// if the target is in range, otherwise it creates a long thunk.
class ARMThunk : public Thunk {
public:
- ARMThunk(Symbol &Dest) : Thunk(Dest) {}
+ ARMThunk(Symbol &dest) : Thunk(dest) {}
- bool mayUseShortThunk();
- uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
- void writeTo(uint8_t *Buf) override;
- bool isCompatibleWith(RelType Type) const override;
+ bool getMayUseShortThunk();
+ uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
+ void writeTo(uint8_t *buf) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
// Returns the size of a long thunk.
virtual uint32_t sizeLong() = 0;
// Writes a long thunk to Buf.
- virtual void writeLong(uint8_t *Buf) = 0;
+ virtual void writeLong(uint8_t *buf) = 0;
private:
// This field tracks whether all previously considered layouts would allow
@@ -94,7 +94,7 @@ private:
// distance to the target. We do this because transitioning from long to short
// can create layout oscillations in certain corner cases which would prevent
// the layout from converging.
- bool MayUseShortThunk = true;
+ bool mayUseShortThunk = true;
};
// Base class for Thumb-2 thunks.
@@ -103,60 +103,61 @@ private:
// which has a range of 16MB.
class ThumbThunk : public Thunk {
public:
- ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbThunk(Symbol &dest) : Thunk(dest) { alignment = 2; }
- bool mayUseShortThunk();
- uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
- void writeTo(uint8_t *Buf) override;
- bool isCompatibleWith(RelType Type) const override;
+ bool getMayUseShortThunk();
+ uint32_t size() override { return getMayUseShortThunk() ? 4 : sizeLong(); }
+ void writeTo(uint8_t *buf) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
// Returns the size of a long thunk.
virtual uint32_t sizeLong() = 0;
// Writes a long thunk to Buf.
- virtual void writeLong(uint8_t *Buf) = 0;
+ virtual void writeLong(uint8_t *buf) = 0;
private:
// See comment in ARMThunk above.
- bool MayUseShortThunk = true;
+ bool mayUseShortThunk = true;
};
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
class ARMV7ABSLongThunk final : public ARMThunk {
public:
- ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV7ABSLongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 12; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ARMV7PILongThunk final : public ARMThunk {
public:
- ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV7PILongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 16; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ThumbV7ABSLongThunk final : public ThumbThunk {
public:
- ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV7ABSLongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 10; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ThumbV7PILongThunk final : public ThumbThunk {
public:
- ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV7PILongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 12; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// Implementations of Thunks for older Arm architectures that do not support
@@ -166,76 +167,96 @@ public:
// can result in a thunk
class ARMV5ABSLongThunk final : public ARMThunk {
public:
- ARMV5ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV5ABSLongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 8; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(uint32_t RelocType) const override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
};
class ARMV5PILongThunk final : public ARMThunk {
public:
- ARMV5PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
+ ARMV5PILongThunk(Symbol &dest) : ARMThunk(dest) {}
uint32_t sizeLong() override { return 16; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(uint32_t RelocType) const override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+ bool isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const override;
};
// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
class ThumbV6MABSLongThunk final : public ThumbThunk {
public:
- ThumbV6MABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV6MABSLongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 12; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
class ThumbV6MPILongThunk final : public ThumbThunk {
public:
- ThumbV6MPILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
+ ThumbV6MPILongThunk(Symbol &dest) : ThumbThunk(dest) {}
uint32_t sizeLong() override { return 16; }
- void writeLong(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeLong(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
};
// MIPS LA25 thunk
class MipsThunk final : public Thunk {
public:
- MipsThunk(Symbol &Dest) : Thunk(Dest) {}
+ MipsThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 16; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
InputSection *getTargetInputSection() const override;
};
// microMIPS R2-R5 LA25 thunk
class MicroMipsThunk final : public Thunk {
public:
- MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
+ MicroMipsThunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 14; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
InputSection *getTargetInputSection() const override;
};
// microMIPS R6 LA25 thunk
class MicroMipsR6Thunk final : public Thunk {
public:
- MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
+ MicroMipsR6Thunk(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 12; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
InputSection *getTargetInputSection() const override;
};
+class PPC32PltCallStub final : public Thunk {
+public:
+ PPC32PltCallStub(const InputSection &isec, const Relocation &rel, Symbol &dest)
+ : Thunk(dest), addend(rel.type == R_PPC_PLTREL24 ? rel.addend : 0),
+ file(isec.file) {}
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+ bool isCompatibleWith(const InputSection &isec, const Relocation &rel) const override;
+
+private:
+ // For R_PPC_PLTREL24, this records the addend, which will be used to decide
+ // the offsets in the call stub.
+ uint32_t addend;
+
+ // Records the call site of the call stub.
+ const InputFile *file;
+};
// PPC64 Plt call stubs.
// Any call site that needs to call through a plt entry needs a call stub in
@@ -247,10 +268,10 @@ public:
// 3) Transfering control to the target function through an indirect branch.
class PPC64PltCallStub final : public Thunk {
public:
- PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {}
+ PPC64PltCallStub(Symbol &dest) : Thunk(dest) {}
uint32_t size() override { return 20; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ 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
@@ -264,74 +285,75 @@ public:
class PPC64LongBranchThunk : public Thunk {
public:
uint32_t size() override { return 16; }
- void writeTo(uint8_t *Buf) override;
- void addSymbols(ThunkSection &IS) override;
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
protected:
- PPC64LongBranchThunk(Symbol &Dest) : Thunk(Dest) {}
+ PPC64LongBranchThunk(Symbol &dest) : Thunk(dest) {}
};
class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
public:
- PPC64PILongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
- assert(!Dest.IsPreemptible);
- if (Dest.isInPPC64Branchlt())
+ PPC64PILongBranchThunk(Symbol &dest) : PPC64LongBranchThunk(dest) {
+ assert(!dest.isPreemptible);
+ if (dest.isInPPC64Branchlt())
return;
- In.PPC64LongBranchTarget->addEntry(Dest);
- In.RelaDyn->addReloc({Target->RelativeRel, In.PPC64LongBranchTarget,
- Dest.getPPC64LongBranchOffset(), true, &Dest,
- getPPC64GlobalEntryToLocalEntryOffset(Dest.StOther)});
+ in.ppc64LongBranchTarget->addEntry(dest);
+ mainPart->relaDyn->addReloc(
+ {target->relativeRel, in.ppc64LongBranchTarget,
+ dest.getPPC64LongBranchOffset(), true, &dest,
+ getPPC64GlobalEntryToLocalEntryOffset(dest.stOther)});
}
};
class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
public:
- PPC64PDLongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
- if (!Dest.isInPPC64Branchlt())
- In.PPC64LongBranchTarget->addEntry(Dest);
+ PPC64PDLongBranchThunk(Symbol &dest) : PPC64LongBranchThunk(dest) {
+ if (!dest.isInPPC64Branchlt())
+ in.ppc64LongBranchTarget->addEntry(dest);
}
};
} // end anonymous namespace
-Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
- InputSectionBase &Section) {
- Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
- Syms.push_back(D);
- return D;
+Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value,
+ InputSectionBase &section) {
+ Defined *d = addSyntheticLocal(name, type, value, /*size=*/0, section);
+ syms.push_back(d);
+ return d;
}
-void Thunk::setOffset(uint64_t NewOffset) {
- for (Defined *D : Syms)
- D->Value = D->Value - Offset + NewOffset;
- Offset = NewOffset;
+void Thunk::setOffset(uint64_t newOffset) {
+ for (Defined *d : syms)
+ d->value = d->value - offset + newOffset;
+ offset = newOffset;
}
// AArch64 long range Thunks
-static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
- uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
- return V;
+static uint64_t getAArch64ThunkDestVA(const Symbol &s) {
+ uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
+ return v;
}
-void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
- const uint8_t Data[] = {
+void AArch64ABSLongThunk::writeTo(uint8_t *buf) {
+ const uint8_t data[] = {
0x50, 0x00, 0x00, 0x58, // ldr x16, L0
0x00, 0x02, 0x1f, 0xd6, // br x16
0x00, 0x00, 0x00, 0x00, // L0: .xword S
0x00, 0x00, 0x00, 0x00,
};
- uint64_t S = getAArch64ThunkDestVA(Destination);
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
+ uint64_t s = getAArch64ThunkDestVA(destination);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 8, R_AARCH64_ABS64, s);
}
-void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
- STT_FUNC, 0, IS);
- addSymbol("$x", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 8, IS);
+void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ addSymbol("$x", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 8, isec);
}
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
@@ -339,259 +361,263 @@ void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
// clang and gcc do not support the large code model for position independent
// code so it is safe to use this for position independent thunks without
// worrying about the destination being more than 4Gb away.
-void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
- const uint8_t Data[] = {
+void AArch64ADRPThunk::writeTo(uint8_t *buf) {
+ const uint8_t data[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
0x00, 0x02, 0x1f, 0xd6, // br x16
};
- uint64_t S = getAArch64ThunkDestVA(Destination);
- 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);
+ uint64_t s = getAArch64ThunkDestVA(destination);
+ 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);
}
-void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
- 0, IS);
- addSymbol("$x", STT_NOTYPE, 0, IS);
+void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ addSymbol("$x", STT_NOTYPE, 0, isec);
}
// ARM Target Thunks
-static uint64_t getARMThunkDestVA(const Symbol &S) {
- uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
- return SignExtend64<32>(V);
+static uint64_t getARMThunkDestVA(const Symbol &s) {
+ uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA();
+ return SignExtend64<32>(v);
}
// This function returns true if the target is not Thumb and is within 2^26, and
-// it has not previously returned false (see comment for MayUseShortThunk).
-bool ARMThunk::mayUseShortThunk() {
- if (!MayUseShortThunk)
+// it has not previously returned false (see comment for mayUseShortThunk).
+bool ARMThunk::getMayUseShortThunk() {
+ if (!mayUseShortThunk)
return false;
- uint64_t S = getARMThunkDestVA(Destination);
- if (S & 1) {
- MayUseShortThunk = false;
+ uint64_t s = getARMThunkDestVA(destination);
+ if (s & 1) {
+ mayUseShortThunk = false;
return false;
}
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 8;
- MayUseShortThunk = llvm::isInt<26>(Offset);
- return MayUseShortThunk;
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 8;
+ mayUseShortThunk = llvm::isInt<26>(offset);
+ return mayUseShortThunk;
}
-void ARMThunk::writeTo(uint8_t *Buf) {
- if (!mayUseShortThunk()) {
- writeLong(Buf);
+void ARMThunk::writeTo(uint8_t *buf) {
+ if (!getMayUseShortThunk()) {
+ writeLong(buf);
return;
}
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 8;
- const uint8_t Data[] = {
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 8;
+ const uint8_t data[] = {
0x00, 0x00, 0x00, 0xea, // b S
};
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_JUMP24, offset);
}
-bool ARMThunk::isCompatibleWith(RelType Type) const {
+bool ARMThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// Thumb branch relocations can't use BLX
- return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
+ return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
}
// This function returns true if the target is Thumb and is within 2^25, and
-// it has not previously returned false (see comment for MayUseShortThunk).
-bool ThumbThunk::mayUseShortThunk() {
- if (!MayUseShortThunk)
+// it has not previously returned false (see comment for mayUseShortThunk).
+bool ThumbThunk::getMayUseShortThunk() {
+ if (!mayUseShortThunk)
return false;
- uint64_t S = getARMThunkDestVA(Destination);
- if ((S & 1) == 0) {
- MayUseShortThunk = false;
+ uint64_t s = getARMThunkDestVA(destination);
+ if ((s & 1) == 0) {
+ mayUseShortThunk = false;
return false;
}
- uint64_t P = getThunkTargetSym()->getVA() & ~1;
- int64_t Offset = S - P - 4;
- MayUseShortThunk = llvm::isInt<25>(Offset);
- return MayUseShortThunk;
+ uint64_t p = getThunkTargetSym()->getVA() & ~1;
+ int64_t offset = s - p - 4;
+ mayUseShortThunk = llvm::isInt<25>(offset);
+ return mayUseShortThunk;
}
-void ThumbThunk::writeTo(uint8_t *Buf) {
- if (!mayUseShortThunk()) {
- writeLong(Buf);
+void ThumbThunk::writeTo(uint8_t *buf) {
+ if (!getMayUseShortThunk()) {
+ writeLong(buf);
return;
}
- uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = getThunkTargetSym()->getVA();
- int64_t Offset = S - P - 4;
- const uint8_t Data[] = {
+ uint64_t s = getARMThunkDestVA(destination);
+ uint64_t p = getThunkTargetSym()->getVA();
+ int64_t offset = s - p - 4;
+ const uint8_t data[] = {
0x00, 0xf0, 0x00, 0xb0, // b.w S
};
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf, R_ARM_THM_JUMP24, offset);
}
-bool ThumbThunk::isCompatibleWith(RelType Type) const {
+bool ThumbThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// ARM branch relocations can't use BLX
- return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+ return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32;
}
-void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
0x1c, 0xff, 0x2f, 0xe1, // bx ip
};
- 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);
+ 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);
}
-void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
- STT_FUNC, 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
+void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
}
-void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
0x60, 0x47, // bx ip
};
- 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);
+ 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);
}
-void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
+void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
}
-void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV7PILongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
0x1c, 0xff, 0x2f, 0xe1, // bx ip
};
- uint64_t S = getARMThunkDestVA(Destination);
- 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);
+ uint64_t s = getARMThunkDestVA(destination);
+ 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);
}
-void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
- 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
+void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
}
-void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
0xfc, 0x44, // L1: add ip, pc
0x60, 0x47, // bx ip
};
- uint64_t S = getARMThunkDestVA(Destination);
- 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);
+ uint64_t s = getARMThunkDestVA(destination);
+ 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);
}
-void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
+void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
}
-void ARMV5ABSLongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV5ABSLongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1
0x00, 0x00, 0x00, 0x00, // L1: .word S
};
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 4, R_ARM_ABS32, getARMThunkDestVA(Destination));
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
}
-void ARMV5ABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMv5ABSLongThunk_" + Destination.getName()),
- STT_FUNC, 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 4, IS);
+void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 4, isec);
}
-bool ARMV5ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// Thumb branch relocations can't use BLX
- return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
+ return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
}
-void ARMV5PILongThunk::writeLong(uint8_t *Buf) {
- const uint8_t Data[] = {
+void ARMV5PILongThunk::writeLong(uint8_t *buf) {
+ const uint8_t data[] = {
0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2
0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
0x1c, 0xff, 0x2f, 0xe1, // bx ip
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
};
- 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);
+ 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);
}
-void ARMV5PILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__ARMV5PILongThunk_" + Destination.getName()), STT_FUNC,
- 0, IS);
- addSymbol("$a", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 12, IS);
+void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ addSymbol("$a", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 12, isec);
}
-bool ARMV5PILongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
// Thumb branch relocations can't use BLX
- return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
+ return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
}
-void ThumbV6MABSLongThunk::writeLong(uint8_t *Buf) {
+void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
// Most Thumb instructions cannot access the high registers r8 - r15. As the
// only register we can corrupt is r12 we must instead spill a low register
// to the stack to use as a scratch register. We push r1 even though we
// don't need to get some space to use for the return address.
- const uint8_t Data[] = {
+ const uint8_t data[] = {
0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers
0x01, 0x48, // ldr r0, [pc, #4] ; L1
0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S
0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest
0x00, 0x00, 0x00, 0x00 // L1: .word S
};
- uint64_t S = getARMThunkDestVA(Destination);
- memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf + 8, R_ARM_ABS32, S);
+ uint64_t s = getARMThunkDestVA(destination);
+ memcpy(buf, data, sizeof(data));
+ target->relocateOne(buf + 8, R_ARM_ABS32, s);
}
-void ThumbV6MABSLongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__Thumbv6MABSLongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 8, IS);
+void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 8, isec);
}
-void ThumbV6MPILongThunk::writeLong(uint8_t *Buf) {
+void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
// Most Thumb instructions cannot access the high registers r8 - r15. As the
// only register we can corrupt is ip (r12) we must instead spill a low
// register to the stack to use as a scratch register.
- const uint8_t Data[] = {
+ const uint8_t data[] = {
0x01, 0xb4, // P: push {r0} ; Obtain scratch register
0x02, 0x48, // ldr r0, [pc, #8] ; L2
0x84, 0x46, // mov ip, r0 ; high to low register
@@ -600,131 +626,185 @@ void ThumbV6MPILongThunk::writeLong(uint8_t *Buf) {
0xc0, 0x46, // nop ; pad to 4-byte boundary
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
};
- 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);
+ 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);
}
-void ThumbV6MPILongThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__Thumbv6MPILongThunk_" + Destination.getName()),
- STT_FUNC, 1, IS);
- addSymbol("$t", STT_NOTYPE, 0, IS);
- addSymbol("$d", STT_NOTYPE, 12, IS);
+void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()),
+ STT_FUNC, 1, isec);
+ addSymbol("$t", STT_NOTYPE, 0, isec);
+ addSymbol("$d", STT_NOTYPE, 12, isec);
}
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
-void MipsThunk::writeTo(uint8_t *Buf) {
- uint64_t S = Destination.getVA();
- write32(Buf, 0x3c190000); // lui $25, %hi(func)
- 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);
+void MipsThunk::writeTo(uint8_t *buf) {
+ uint64_t s = destination.getVA();
+ write32(buf, 0x3c190000); // lui $25, %hi(func)
+ 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);
}
-void MipsThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
- IS);
+void MipsThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
+ isec);
}
InputSection *MipsThunk::getTargetInputSection() const {
- auto &DR = cast<Defined>(Destination);
- return dyn_cast<InputSection>(DR.Section);
+ auto &dr = cast<Defined>(destination);
+ return dyn_cast<InputSection>(dr.section);
}
// Write microMIPS R2-R5 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsThunk::writeTo(uint8_t *Buf) {
- uint64_t S = Destination.getVA() | 1;
- write16(Buf, 0x41b9); // lui $25, %hi(func)
- 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);
+void MicroMipsThunk::writeTo(uint8_t *buf) {
+ uint64_t s = destination.getVA();
+ write16(buf, 0x41b9); // lui $25, %hi(func)
+ 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);
}
-void MicroMipsThunk::addSymbols(ThunkSection &IS) {
- Defined *D = addSymbol(
- Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
- D->StOther |= STO_MIPS_MICROMIPS;
+void MicroMipsThunk::addSymbols(ThunkSection &isec) {
+ Defined *d = addSymbol(
+ saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
+ d->stOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsThunk::getTargetInputSection() const {
- auto &DR = cast<Defined>(Destination);
- return dyn_cast<InputSection>(DR.Section);
+ auto &dr = cast<Defined>(destination);
+ return dyn_cast<InputSection>(dr.section);
}
// Write microMIPS R6 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
- uint64_t S = Destination.getVA() | 1;
- uint64_t P = getThunkTargetSym()->getVA();
- 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);
+void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
+ uint64_t s = destination.getVA();
+ uint64_t p = getThunkTargetSym()->getVA();
+ 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);
}
-void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
- Defined *D = addSymbol(
- Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
- D->StOther |= STO_MIPS_MICROMIPS;
+void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
+ Defined *d = addSymbol(
+ saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
+ d->stOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
- auto &DR = cast<Defined>(Destination);
- return dyn_cast<InputSection>(DR.Section);
+ auto &dr = cast<Defined>(destination);
+ return dyn_cast<InputSection>(dr.section);
}
-static void writePPCLoadAndBranch(uint8_t *Buf, int64_t Offset) {
- uint16_t OffHa = (Offset + 0x8000) >> 16;
- uint16_t OffLo = Offset & 0xffff;
+void PPC32PltCallStub::writeTo(uint8_t *buf) {
+ if (!config->isPic) {
+ uint64_t va = destination.getGotPltVA();
+ write32(buf + 0, 0x3d600000 | (va + 0x8000) >> 16); // lis r11,ha
+ write32(buf + 4, 0x816b0000 | (uint16_t)va); // lwz r11,l(r11)
+ write32(buf + 8, 0x7d6903a6); // mtctr r11
+ write32(buf + 12, 0x4e800420); // bctr
+ return;
+ }
+ uint32_t offset;
+ if (addend >= 0x8000) {
+ // The stub loads an address relative to r30 (.got2+Addend). Addend is
+ // almost always 0x8000. The address of .got2 is different in another object
+ // file, so a stub cannot be shared.
+ offset = destination.getGotPltVA() - (in.ppc32Got2->getParent()->getVA() +
+ file->ppc32Got2OutSecOff + addend);
+ } else {
+ // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
+ // currently the address of .got).
+ offset = destination.getGotPltVA() - in.got->getVA();
+ }
+ uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset;
+ if (ha == 0) {
+ write32(buf + 0, 0x817e0000 | l); // lwz r11,l(r30)
+ write32(buf + 4, 0x7d6903a6); // mtctr r11
+ write32(buf + 8, 0x4e800420); // bctr
+ write32(buf + 12, 0x60000000); // nop
+ } else {
+ write32(buf + 0, 0x3d7e0000 | ha); // addis r11,r30,ha
+ write32(buf + 4, 0x816b0000 | l); // lwz r11,l(r11)
+ write32(buf + 8, 0x7d6903a6); // mtctr r11
+ write32(buf + 12, 0x4e800420); // bctr
+ }
+}
+
+void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
+ std::string buf;
+ raw_string_ostream os(buf);
+ os << format_hex_no_prefix(addend, 8);
+ if (!config->isPic)
+ os << ".plt_call32.";
+ else if (addend >= 0x8000)
+ os << ".got2.plt_pic32.";
+ else
+ os << ".plt_pic32.";
+ os << destination.getName();
+ addSymbol(saver.save(os.str()), STT_FUNC, 0, isec);
+}
+
+bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
+ const Relocation &rel) const {
+ return !config->isPic || (isec.file == file && rel.addend == addend);
+}
- write32(Buf + 0, 0x3d820000 | OffHa); // addis r12, r2, OffHa
- write32(Buf + 4, 0xe98c0000 | OffLo); // ld r12, OffLo(r12)
- write32(Buf + 8, 0x7d8903a6); // mtctr r12
- write32(Buf + 12, 0x4e800420); // bctr
+static void writePPCLoadAndBranch(uint8_t *buf, int64_t offset) {
+ uint16_t offHa = (offset + 0x8000) >> 16;
+ uint16_t offLo = offset & 0xffff;
+
+ write32(buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa
+ write32(buf + 4, 0xe98c0000 | offLo); // ld r12, OffLo(r12)
+ write32(buf + 8, 0x7d8903a6); // mtctr r12
+ write32(buf + 12, 0x4e800420); // bctr
}
-void PPC64PltCallStub::writeTo(uint8_t *Buf) {
- int64_t Offset = Destination.getGotPltVA() - getPPC64TocBase();
+void PPC64PltCallStub::writeTo(uint8_t *buf) {
+ int64_t offset = destination.getGotPltVA() - getPPC64TocBase();
// Save the TOC pointer to the save-slot reserved in the call frame.
- write32(Buf + 0, 0xf8410018); // std r2,24(r1)
- writePPCLoadAndBranch(Buf + 4, Offset);
+ write32(buf + 0, 0xf8410018); // std r2,24(r1)
+ writePPCLoadAndBranch(buf + 4, offset);
}
-void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
- Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC,
- 0, IS);
- S->NeedsTocRestore = true;
+void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
+ Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC,
+ 0, isec);
+ s->needsTocRestore = true;
}
-void PPC64LongBranchThunk::writeTo(uint8_t *Buf) {
- int64_t Offset = Destination.getPPC64LongBranchTableVA() - getPPC64TocBase();
- writePPCLoadAndBranch(Buf, Offset);
+void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
+ int64_t offset = destination.getPPC64LongBranchTableVA() - getPPC64TocBase();
+ writePPCLoadAndBranch(buf, offset);
}
-void PPC64LongBranchThunk::addSymbols(ThunkSection &IS) {
- addSymbol(Saver.save("__long_branch_" + Destination.getName()), STT_FUNC, 0,
- IS);
+void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
+ addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0,
+ isec);
}
-Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
+Thunk::Thunk(Symbol &d) : destination(d), offset(0) {}
Thunk::~Thunk() = default;
-static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
- if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+static Thunk *addThunkAArch64(RelType type, Symbol &s) {
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
fatal("unrecognized relocation type");
- if (Config->Pic)
- return make<AArch64ADRPThunk>(S);
- return make<AArch64ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<AArch64ADRPThunk>(s);
+ return make<AArch64ABSLongThunk>(s);
}
// Creates a thunk for Thumb-ARM interworking.
@@ -732,18 +812,18 @@ static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
// - MOVT and MOVW instructions cannot be used
// - Only Thumb relocation that can generate a Thunk is a BL, this can always
// be transformed into a BLX
-static Thunk *addThunkPreArmv7(RelType Reloc, Symbol &S) {
- switch (Reloc) {
+static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s) {
+ switch (reloc) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
case R_ARM_CALL:
case R_ARM_THM_CALL:
- if (Config->Pic)
- return make<ARMV5PILongThunk>(S);
- return make<ARMV5ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<ARMV5PILongThunk>(s);
+ return make<ARMV5ABSLongThunk>(s);
}
- fatal("relocation " + toString(Reloc) + " to " + toString(S) +
+ fatal("relocation " + toString(reloc) + " to " + toString(s) +
" not supported for Armv5 or Armv6 targets");
}
@@ -752,21 +832,21 @@ static Thunk *addThunkPreArmv7(RelType Reloc, Symbol &S) {
// - MOVT and MOVW instructions cannot be used.
// - Only a limited number of instructions can access registers r8 and above
// - No interworking support is needed (all Thumb).
-static Thunk *addThunkV6M(RelType Reloc, Symbol &S) {
- switch (Reloc) {
+static Thunk *addThunkV6M(RelType reloc, Symbol &s) {
+ switch (reloc) {
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- if (Config->Pic)
- return make<ThumbV6MPILongThunk>(S);
- return make<ThumbV6MABSLongThunk>(S);
+ if (config->isPic)
+ return make<ThumbV6MPILongThunk>(s);
+ return make<ThumbV6MABSLongThunk>(s);
}
- fatal("relocation " + toString(Reloc) + " to " + toString(S) +
+ fatal("relocation " + toString(reloc) + " to " + toString(s) +
" not supported for Armv6-M targets");
}
// Creates a thunk for Thumb-ARM interworking or branch range extension.
-static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
+static Thunk *addThunkArm(RelType reloc, Symbol &s) {
// Decide which Thunk is needed based on:
// Available instruction set
// - An Arm Thunk can only be used if Arm state is available.
@@ -783,61 +863,72 @@ static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
// can use in Thunks. The flags below are set by reading the BuildAttributes
// of the input objects. InputFiles.cpp contains the mapping from ARM
// architecture to flag.
- if (!Config->ARMHasMovtMovw) {
- if (!Config->ARMJ1J2BranchEncoding)
- return addThunkPreArmv7(Reloc, S);
- return addThunkV6M(Reloc, S);
+ if (!config->armHasMovtMovw) {
+ if (!config->armJ1J2BranchEncoding)
+ return addThunkPreArmv7(reloc, s);
+ return addThunkV6M(reloc, s);
}
- switch (Reloc) {
+ switch (reloc) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
case R_ARM_CALL:
- if (Config->Pic)
- return make<ARMV7PILongThunk>(S);
- return make<ARMV7ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<ARMV7PILongThunk>(s);
+ return make<ARMV7ABSLongThunk>(s);
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- if (Config->Pic)
- return make<ThumbV7PILongThunk>(S);
- return make<ThumbV7ABSLongThunk>(S);
+ if (config->picThunk)
+ return make<ThumbV7PILongThunk>(s);
+ return make<ThumbV7ABSLongThunk>(s);
}
fatal("unrecognized relocation type");
}
-static Thunk *addThunkMips(RelType Type, Symbol &S) {
- if ((S.StOther & STO_MIPS_MICROMIPS) && isMipsR6())
- return make<MicroMipsR6Thunk>(S);
- if (S.StOther & STO_MIPS_MICROMIPS)
- return make<MicroMipsThunk>(S);
- return make<MipsThunk>(S);
+static Thunk *addThunkMips(RelType type, Symbol &s) {
+ if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6())
+ return make<MicroMipsR6Thunk>(s);
+ if (s.stOther & STO_MIPS_MICROMIPS)
+ return make<MicroMipsThunk>(s);
+ return make<MipsThunk>(s);
+}
+
+static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel, Symbol &s) {
+ assert((rel.type == R_PPC_REL24 || rel.type == R_PPC_PLTREL24) &&
+ "unexpected relocation type for thunk");
+ return make<PPC32PltCallStub>(isec, rel, s);
}
-static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
- assert(Type == R_PPC64_REL24 && "unexpected relocation type for thunk");
- if (S.isInPlt())
- return make<PPC64PltCallStub>(S);
+static Thunk *addThunkPPC64(RelType type, Symbol &s) {
+ assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk");
+ if (s.isInPlt())
+ return make<PPC64PltCallStub>(s);
- if (Config->Pic)
- return make<PPC64PILongBranchThunk>(S);
+ if (config->picThunk)
+ return make<PPC64PILongBranchThunk>(s);
- return make<PPC64PDLongBranchThunk>(S);
+ return make<PPC64PDLongBranchThunk>(s);
}
-Thunk *addThunk(RelType Type, Symbol &S) {
- if (Config->EMachine == EM_AARCH64)
- return addThunkAArch64(Type, S);
+Thunk *addThunk(const InputSection &isec, Relocation &rel) {
+ Symbol &s = *rel.sym;
+
+ if (config->emachine == EM_AARCH64)
+ return addThunkAArch64(rel.type, s);
+
+ if (config->emachine == EM_ARM)
+ return addThunkArm(rel.type, s);
- if (Config->EMachine == EM_ARM)
- return addThunkArm(Type, S);
+ if (config->emachine == EM_MIPS)
+ return addThunkMips(rel.type, s);
- if (Config->EMachine == EM_MIPS)
- return addThunkMips(Type, S);
+ if (config->emachine == EM_PPC)
+ return addThunkPPC32(isec, rel, s);
- if (Config->EMachine == EM_PPC64)
- return addThunkPPC64(Type, S);
+ if (config->emachine == EM_PPC64)
+ return addThunkPPC64(rel.type, s);
llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
}
diff --git a/ELF/Thunks.h b/ELF/Thunks.h
index ed82b4d946ac..2d27ee5f6c38 100644
--- a/ELF/Thunks.h
+++ b/ELF/Thunks.h
@@ -1,9 +1,8 @@
//===- Thunks.h --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -28,42 +27,45 @@ class ThunkSection;
// Thunks are assigned to synthetic ThunkSections
class Thunk {
public:
- Thunk(Symbol &Destination);
+ Thunk(Symbol &destination);
virtual ~Thunk();
virtual uint32_t size() = 0;
- virtual void writeTo(uint8_t *Buf) = 0;
+ virtual void writeTo(uint8_t *buf) = 0;
// All Thunks must define at least one symbol, known as the thunk target
// symbol, so that we can redirect relocations to it. The thunk may define
// additional symbols, but these are never targets for relocations.
- virtual void addSymbols(ThunkSection &IS) = 0;
+ virtual void addSymbols(ThunkSection &isec) = 0;
- void setOffset(uint64_t Offset);
- Defined *addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
- InputSectionBase &Section);
+ void setOffset(uint64_t offset);
+ Defined *addSymbol(StringRef name, uint8_t type, uint64_t value,
+ InputSectionBase &section);
// Some Thunks must be placed immediately before their Target as they elide
// a branch and fall through to the first Symbol in the Target.
virtual InputSection *getTargetInputSection() const { return nullptr; }
- // To reuse a Thunk the caller as identified by the Type must be
- // compatible with it.
- virtual bool isCompatibleWith(RelType Type) const { return true; }
+ // To reuse a Thunk the InputSection and the relocation must be compatible
+ // with it.
+ virtual bool isCompatibleWith(const InputSection &,
+ const Relocation &) const {
+ return true;
+ }
- Defined *getThunkTargetSym() const { return Syms[0]; }
+ Defined *getThunkTargetSym() const { return syms[0]; }
// The alignment requirement for this Thunk, defaults to the size of the
// typical code section alignment.
- Symbol &Destination;
- llvm::SmallVector<Defined *, 3> Syms;
- uint64_t Offset = 0;
- uint32_t Alignment = 4;
+ Symbol &destination;
+ llvm::SmallVector<Defined *, 3> syms;
+ uint64_t offset = 0;
+ uint32_t alignment = 4;
};
// For a Relocation to symbol S create a Thunk to be added to a synthetic
-// ThunkSection. At present there are implementations for ARM and Mips Thunks.
-Thunk *addThunk(RelType Type, Symbol &S);
+// ThunkSection.
+Thunk *addThunk(const InputSection &isec, Relocation &rel);
} // namespace elf
} // namespace lld
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 17f4c7961d30..b8c8891648a4 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -1,9 +1,8 @@
//===- Writer.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -11,7 +10,6 @@
#include "AArch64ErrataFix.h"
#include "CallGraphSort.h"
#include "Config.h"
-#include "Filesystem.h"
#include "LinkerScript.h"
#include "MapFile.h"
#include "OutputSections.h"
@@ -20,11 +18,15 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#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/RandomNumberGenerator.h"
+#include "llvm/Support/SHA1.h"
+#include "llvm/Support/xxhash.h"
#include <climits>
using namespace llvm;
@@ -40,31 +42,32 @@ namespace {
// The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer {
public:
- Writer() : Buffer(errorHandler().OutputBuffer) {}
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Ehdr Elf_Ehdr;
- typedef typename ELFT::Phdr Elf_Phdr;
+ Writer() : buffer(errorHandler().outputBuffer) {}
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Ehdr = typename ELFT::Ehdr;
+ using Elf_Phdr = typename ELFT::Phdr;
void run();
private:
void copyLocalSymbols();
void addSectionSymbols();
- void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> Fn);
+ void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> fn);
void sortSections();
void resolveShfLinkOrder();
- void maybeAddThunks();
+ void finalizeAddressDependentContent();
void sortInputSections();
void finalizeSections();
void checkExecuteOnly();
void setReservedSymbolSections();
- std::vector<PhdrEntry *> createPhdrs();
- void removeEmptyPTLoad();
- void addPtArmExid(std::vector<PhdrEntry *> &Phdrs);
+ std::vector<PhdrEntry *> createPhdrs(Partition &part);
+ void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrEntry);
+ void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
+ unsigned pFlags);
void assignFileOffsets();
void assignFileOffsetsBinary();
- void setPhdrs();
+ void setPhdrs(Partition &part);
void checkSections();
void fixSectionAlignments();
void openFile();
@@ -74,36 +77,34 @@ private:
void writeSectionsBinary();
void writeBuildId();
- std::unique_ptr<FileOutputBuffer> &Buffer;
+ std::unique_ptr<FileOutputBuffer> &buffer;
void addRelIpltSymbols();
void addStartEndSymbols();
- void addStartStopSymbols(OutputSection *Sec);
-
- std::vector<PhdrEntry *> Phdrs;
+ void addStartStopSymbols(OutputSection *sec);
- uint64_t FileSize;
- uint64_t SectionHeaderOff;
+ uint64_t fileSize;
+ uint64_t sectionHeaderOff;
};
} // anonymous namespace
-static bool isSectionPrefix(StringRef Prefix, StringRef Name) {
- return Name.startswith(Prefix) || Name == Prefix.drop_back();
+static bool isSectionPrefix(StringRef prefix, StringRef name) {
+ return name.startswith(prefix) || name == prefix.drop_back();
}
-StringRef elf::getOutputSectionName(const InputSectionBase *S) {
- if (Config->Relocatable)
- return S->Name;
+StringRef elf::getOutputSectionName(const InputSectionBase *s) {
+ if (config->relocatable)
+ return s->name;
// This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want
// to emit .rela.text.foo as .rela.text.bar for consistency (this is not
// technically required, but not doing it is odd). This code guarantees that.
- if (auto *IS = dyn_cast<InputSection>(S)) {
- if (InputSectionBase *Rel = IS->getRelocatedSection()) {
- OutputSection *Out = Rel->getOutputSection();
- if (S->Type == SHT_RELA)
- return Saver.save(".rela" + Out->Name);
- return Saver.save(".rel" + Out->Name);
+ if (auto *isec = dyn_cast<InputSection>(s)) {
+ if (InputSectionBase *rel = isec->getRelocatedSection()) {
+ OutputSection *out = rel->getOutputSection();
+ if (s->type == SHT_RELA)
+ return saver.save(".rela" + out->name);
+ return saver.save(".rel" + out->name);
}
}
@@ -113,97 +114,133 @@ StringRef elf::getOutputSectionName(const InputSectionBase *S) {
// 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.
- if (Config->ZKeepTextSectionPrefix)
- for (StringRef V :
+ if (config->zKeepTextSectionPrefix)
+ for (StringRef v :
{".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."})
- if (isSectionPrefix(V, S->Name))
- return V.drop_back();
+ if (isSectionPrefix(v, s->name))
+ return v.drop_back();
- for (StringRef V :
+ for (StringRef v :
{".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."})
- if (isSectionPrefix(V, S->Name))
- return V.drop_back();
+ 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")
+ if (s->name == "COMMON")
return ".bss";
- return S->Name;
+ return s->name;
}
static bool needsInterpSection() {
- return !SharedFiles.empty() && !Config->DynamicLinker.empty() &&
- Script->needsInterpSection();
+ return !sharedFiles.empty() && !config->dynamicLinker.empty() &&
+ script->needsInterpSection();
}
template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); }
-template <class ELFT> void Writer<ELFT>::removeEmptyPTLoad() {
- llvm::erase_if(Phdrs, [&](const PhdrEntry *P) {
- if (P->p_type != PT_LOAD)
+template <class ELFT>
+void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
+ llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
+ if (p->p_type != PT_LOAD)
return false;
- if (!P->FirstSec)
+ if (!p->firstSec)
return true;
- uint64_t Size = P->LastSec->Addr + P->LastSec->Size - P->FirstSec->Addr;
- return Size == 0;
+ uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr;
+ return size == 0;
});
}
-template <class ELFT> static void combineEhFrameSections() {
- for (InputSectionBase *&S : InputSections) {
- EhInputSection *ES = dyn_cast<EhInputSection>(S);
- if (!ES || !ES->Live)
+template <class ELFT> static void copySectionsIntoPartitions() {
+ std::vector<InputSectionBase *> newSections;
+ for (unsigned part = 2; part != partitions.size() + 1; ++part) {
+ for (InputSectionBase *s : inputSections) {
+ if (!(s->flags & SHF_ALLOC) || !s->isLive())
+ continue;
+ InputSectionBase *copy;
+ if (s->type == SHT_NOTE)
+ copy = make<InputSection>(cast<InputSection>(*s));
+ else if (auto *es = dyn_cast<EhInputSection>(s))
+ copy = make<EhInputSection>(*es);
+ else
+ continue;
+ copy->partition = part;
+ newSections.push_back(copy);
+ }
+ }
+
+ inputSections.insert(inputSections.end(), newSections.begin(),
+ newSections.end());
+}
+
+template <class ELFT> static void combineEhSections() {
+ for (InputSectionBase *&s : inputSections) {
+ // Ignore dead sections and the partition end marker (.part.end),
+ // whose partition number is out of bounds.
+ if (!s->isLive() || s->partition == 255)
continue;
- In.EhFrame->addSection<ELFT>(ES);
- S = nullptr;
+ Partition &part = s->getPartition();
+ if (auto *es = dyn_cast<EhInputSection>(s)) {
+ part.ehFrame->addSection<ELFT>(es);
+ s = nullptr;
+ } else if (s->kind() == SectionBase::Regular && part.armExidx &&
+ part.armExidx->addSection(cast<InputSection>(s))) {
+ s = nullptr;
+ }
}
- std::vector<InputSectionBase *> &V = InputSections;
- V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
+ std::vector<InputSectionBase *> &v = inputSections;
+ v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
}
-static Defined *addOptionalRegular(StringRef Name, SectionBase *Sec,
- uint64_t Val, uint8_t StOther = STV_HIDDEN,
- uint8_t Binding = STB_GLOBAL) {
- Symbol *S = Symtab->find(Name);
- if (!S || S->isDefined())
+static Defined *addOptionalRegular(StringRef name, SectionBase *sec,
+ uint64_t val, uint8_t stOther = STV_HIDDEN,
+ uint8_t binding = STB_GLOBAL) {
+ Symbol *s = symtab->find(name);
+ if (!s || s->isDefined())
return nullptr;
- return Symtab->addDefined(Name, StOther, STT_NOTYPE, Val,
- /*Size=*/0, Binding, Sec,
- /*File=*/nullptr);
+
+ s->resolve(Defined{/*file=*/nullptr, name, binding, stOther, STT_NOTYPE, val,
+ /*size=*/0, sec});
+ return cast<Defined>(s);
}
-static Defined *addAbsolute(StringRef Name) {
- return Symtab->addDefined(Name, STV_HIDDEN, STT_NOTYPE, 0, 0, STB_GLOBAL,
- nullptr, nullptr);
+static Defined *addAbsolute(StringRef name) {
+ Symbol *sym = symtab->addSymbol(Defined{nullptr, name, STB_GLOBAL, STV_HIDDEN,
+ STT_NOTYPE, 0, 0, nullptr});
+ return cast<Defined>(sym);
}
// The linker is expected to define some symbols depending on
// the linking result. This function defines such symbols.
void elf::addReservedSymbols() {
- if (Config->EMachine == EM_MIPS) {
+ 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
// to GOT. Default offset is 0x7ff0.
// See "Global Data Symbols" in Chapter 6 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- ElfSym::MipsGp = addAbsolute("_gp");
+ ElfSym::mipsGp = addAbsolute("_gp");
// On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
// start of function and 'gp' pointer into GOT.
- if (Symtab->find("_gp_disp"))
- ElfSym::MipsGpDisp = addAbsolute("_gp_disp");
+ if (symtab->find("_gp_disp"))
+ ElfSym::mipsGpDisp = addAbsolute("_gp_disp");
// The __gnu_local_gp is a magic symbol equal to the current value of 'gp'
// pointer. This symbol is used in the code generated by .cpload pseudo-op
// in case of using -mno-shared option.
// https://sourceware.org/ml/binutils/2004-12/msg00094.html
- if (Symtab->find("__gnu_local_gp"))
- ElfSym::MipsLocalGp = addAbsolute("__gnu_local_gp");
+ if (symtab->find("__gnu_local_gp"))
+ ElfSym::mipsLocalGp = addAbsolute("__gnu_local_gp");
+ } else if (config->emachine == EM_PPC) {
+ // 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);
}
// The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which
@@ -214,56 +251,62 @@ void elf::addReservedSymbols() {
// the .got section.
// We do not allow _GLOBAL_OFFSET_TABLE_ to be defined by input objects as the
// correctness of some relocations depends on its value.
- StringRef GotTableSymName =
- (Config->EMachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_";
- if (Symbol *S = Symtab->find(GotTableSymName)) {
- if (S->isDefined())
- error(toString(S->File) + " cannot redefine linker defined symbol '" +
- GotTableSymName + "'");
- else
- ElfSym::GlobalOffsetTable = Symtab->addDefined(
- GotTableSymName, STV_HIDDEN, STT_NOTYPE, Target->GotBaseSymOff,
- /*Size=*/0, STB_GLOBAL, Out::ElfHeader,
- /*File=*/nullptr);
+ StringRef gotSymName =
+ (config->emachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_";
+
+ if (Symbol *s = symtab->find(gotSymName)) {
+ if (s->isDefined()) {
+ error(toString(s->file) + " cannot redefine linker defined symbol '" +
+ gotSymName + "'");
+ return;
+ }
+
+ uint64_t gotOff = 0;
+ if (config->emachine == EM_PPC64)
+ gotOff = 0x8000;
+
+ s->resolve(Defined{/*file=*/nullptr, gotSymName, STB_GLOBAL, STV_HIDDEN,
+ STT_NOTYPE, gotOff, /*size=*/0, Out::elfHeader});
+ ElfSym::globalOffsetTable = cast<Defined>(s);
}
// __ehdr_start is the location of ELF file headers. Note that we define
// this symbol unconditionally even when using a linker script, which
// differs from the behavior implemented by GNU linker which only define
// this symbol if ELF headers are in the memory mapped segment.
- addOptionalRegular("__ehdr_start", Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__ehdr_start", Out::elfHeader, 0, STV_HIDDEN);
// __executable_start is not documented, but the expectation of at
// least the Android libc is that it points to the ELF header.
- addOptionalRegular("__executable_start", Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__executable_start", Out::elfHeader, 0, STV_HIDDEN);
// __dso_handle symbol is passed to cxa_finalize as a marker to identify
// each DSO. The address of the symbol doesn't matter as long as they are
// different in different DSOs, so we chose the start address of the DSO.
- addOptionalRegular("__dso_handle", Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__dso_handle", Out::elfHeader, 0, STV_HIDDEN);
// If linker script do layout we do not need to create any standart symbols.
- if (Script->HasSectionsCommand)
+ if (script->hasSectionsCommand)
return;
- auto Add = [](StringRef S, int64_t Pos) {
- return addOptionalRegular(S, Out::ElfHeader, Pos, STV_DEFAULT);
+ auto add = [](StringRef s, int64_t pos) {
+ return addOptionalRegular(s, Out::elfHeader, pos, STV_DEFAULT);
};
- ElfSym::Bss = Add("__bss_start", 0);
- ElfSym::End1 = Add("end", -1);
- ElfSym::End2 = Add("_end", -1);
- ElfSym::Etext1 = Add("etext", -1);
- ElfSym::Etext2 = Add("_etext", -1);
- ElfSym::Edata1 = Add("edata", -1);
- ElfSym::Edata2 = Add("_edata", -1);
+ ElfSym::bss = add("__bss_start", 0);
+ ElfSym::end1 = add("end", -1);
+ ElfSym::end2 = add("_end", -1);
+ ElfSym::etext1 = add("etext", -1);
+ ElfSym::etext2 = add("_etext", -1);
+ ElfSym::edata1 = add("edata", -1);
+ ElfSym::edata2 = add("_edata", -1);
}
-static OutputSection *findSection(StringRef Name) {
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- if (Sec->Name == Name)
- return Sec;
+static OutputSection *findSection(StringRef name, unsigned partition = 1) {
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ if (sec->name == name && sec->partition == partition)
+ return sec;
return nullptr;
}
@@ -271,202 +314,263 @@ static OutputSection *findSection(StringRef Name) {
template <class ELFT> static void 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));
+ memset(&Out::first, 0, sizeof(Out));
- auto Add = [](InputSectionBase *Sec) { InputSections.push_back(Sec); };
+ auto add = [](InputSectionBase *sec) { inputSections.push_back(sec); };
- In.DynStrTab = make<StringTableSection>(".dynstr", true);
- In.Dynamic = make<DynamicSection<ELFT>>();
- if (Config->AndroidPackDynRelocs) {
- In.RelaDyn = make<AndroidPackedRelocationSection<ELFT>>(
- Config->IsRela ? ".rela.dyn" : ".rel.dyn");
- } else {
- In.RelaDyn = make<RelocationSection<ELFT>>(
- Config->IsRela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc);
- }
- In.ShStrTab = make<StringTableSection>(".shstrtab", false);
+ in.shStrTab = make<StringTableSection>(".shstrtab", false);
- Out::ProgramHeaders = make<OutputSection>("", 0, SHF_ALLOC);
- Out::ProgramHeaders->Alignment = Config->Wordsize;
+ Out::programHeaders = make<OutputSection>("", 0, SHF_ALLOC);
+ Out::programHeaders->alignment = config->wordsize;
- if (needsInterpSection()) {
- In.Interp = createInterpSection();
- Add(In.Interp);
+ if (config->strip != StripPolicy::All) {
+ in.strTab = make<StringTableSection>(".strtab", false);
+ in.symTab = make<SymbolTableSection<ELFT>>(*in.strTab);
+ in.symTabShndx = make<SymtabShndxSection>();
}
- if (Config->Strip != StripPolicy::All) {
- In.StrTab = make<StringTableSection>(".strtab", false);
- In.SymTab = make<SymbolTableSection<ELFT>>(*In.StrTab);
- In.SymTabShndx = make<SymtabShndxSection>();
- }
-
- if (Config->BuildId != BuildIdKind::None) {
- In.BuildId = make<BuildIdSection>();
- Add(In.BuildId);
- }
-
- In.Bss = make<BssSection>(".bss", 0, 1);
- Add(In.Bss);
+ in.bss = make<BssSection>(".bss", 0, 1);
+ add(in.bss);
// If there is a SECTIONS command and a .data.rel.ro section name use name
// .data.rel.ro.bss so that we match in the .data.rel.ro output section.
// This makes sure our relro is contiguous.
- bool HasDataRelRo = Script->HasSectionsCommand && findSection(".data.rel.ro");
- In.BssRelRo =
- make<BssSection>(HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
- Add(In.BssRelRo);
+ bool hasDataRelRo =
+ script->hasSectionsCommand && findSection(".data.rel.ro", 0);
+ in.bssRelRo =
+ make<BssSection>(hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
+ add(in.bssRelRo);
// Add MIPS-specific sections.
- if (Config->EMachine == EM_MIPS) {
- if (!Config->Shared && Config->HasDynSymTab) {
- In.MipsRldMap = make<MipsRldMapSection>();
- Add(In.MipsRldMap);
+ if (config->emachine == EM_MIPS) {
+ if (!config->shared && config->hasDynSymTab) {
+ in.mipsRldMap = make<MipsRldMapSection>();
+ add(in.mipsRldMap);
}
- if (auto *Sec = MipsAbiFlagsSection<ELFT>::create())
- Add(Sec);
- if (auto *Sec = MipsOptionsSection<ELFT>::create())
- Add(Sec);
- if (auto *Sec = MipsReginfoSection<ELFT>::create())
- Add(Sec);
+ if (auto *sec = MipsAbiFlagsSection<ELFT>::create())
+ add(sec);
+ if (auto *sec = MipsOptionsSection<ELFT>::create())
+ add(sec);
+ if (auto *sec = MipsReginfoSection<ELFT>::create())
+ add(sec);
}
- if (Config->HasDynSymTab) {
- In.DynSymTab = make<SymbolTableSection<ELFT>>(*In.DynStrTab);
- Add(In.DynSymTab);
+ for (Partition &part : partitions) {
+ auto add = [&](InputSectionBase *sec) {
+ sec->partition = part.getNumber();
+ inputSections.push_back(sec);
+ };
+
+ if (!part.name.empty()) {
+ part.elfHeader = make<PartitionElfHeaderSection<ELFT>>();
+ part.elfHeader->name = part.name;
+ add(part.elfHeader);
- InX<ELFT>::VerSym = make<VersionTableSection<ELFT>>();
- Add(InX<ELFT>::VerSym);
+ part.programHeaders = make<PartitionProgramHeadersSection<ELFT>>();
+ add(part.programHeaders);
+ }
- if (!Config->VersionDefinitions.empty()) {
- In.VerDef = make<VersionDefinitionSection>();
- Add(In.VerDef);
+ if (config->buildId != BuildIdKind::None) {
+ part.buildId = make<BuildIdSection>();
+ add(part.buildId);
}
- InX<ELFT>::VerNeed = make<VersionNeedSection<ELFT>>();
- Add(InX<ELFT>::VerNeed);
+ part.dynStrTab = make<StringTableSection>(".dynstr", true);
+ part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
+ part.dynamic = make<DynamicSection<ELFT>>();
+ if (config->androidPackDynRelocs) {
+ part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(
+ config->isRela ? ".rela.dyn" : ".rel.dyn");
+ } else {
+ part.relaDyn = make<RelocationSection<ELFT>>(
+ config->isRela ? ".rela.dyn" : ".rel.dyn", config->zCombreloc);
+ }
+
+ if (needsInterpSection())
+ add(createInterpSection());
+
+ if (config->hasDynSymTab) {
+ part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
+ add(part.dynSymTab);
+
+ part.verSym = make<VersionTableSection>();
+ add(part.verSym);
+
+ if (!config->versionDefinitions.empty()) {
+ part.verDef = make<VersionDefinitionSection>();
+ add(part.verDef);
+ }
+
+ part.verNeed = make<VersionNeedSection<ELFT>>();
+ add(part.verNeed);
+
+ if (config->gnuHash) {
+ part.gnuHashTab = make<GnuHashTableSection>();
+ add(part.gnuHashTab);
+ }
+
+ if (config->sysvHash) {
+ part.hashTab = make<HashTableSection>();
+ add(part.hashTab);
+ }
- if (Config->GnuHash) {
- In.GnuHashTab = make<GnuHashTableSection>();
- Add(In.GnuHashTab);
+ add(part.dynamic);
+ add(part.dynStrTab);
+ add(part.relaDyn);
}
- if (Config->SysvHash) {
- In.HashTab = make<HashTableSection>();
- Add(In.HashTab);
+ if (config->relrPackDynRelocs) {
+ part.relrDyn = make<RelrSection<ELFT>>();
+ add(part.relrDyn);
}
- Add(In.Dynamic);
- Add(In.DynStrTab);
- Add(In.RelaDyn);
+ if (!config->relocatable) {
+ if (config->ehFrameHdr) {
+ part.ehFrameHdr = make<EhFrameHeader>();
+ add(part.ehFrameHdr);
+ }
+ part.ehFrame = make<EhFrameSection>();
+ add(part.ehFrame);
+ }
+
+ if (config->emachine == EM_ARM && !config->relocatable) {
+ // The ARMExidxsyntheticsection replaces all the individual .ARM.exidx
+ // InputSections.
+ part.armExidx = make<ARMExidxSyntheticSection>();
+ add(part.armExidx);
+ }
}
- if (Config->RelrPackDynRelocs) {
- In.RelrDyn = make<RelrSection<ELFT>>();
- Add(In.RelrDyn);
+ if (partitions.size() != 1) {
+ // Create the partition end marker. This needs to be in partition number 255
+ // so that it is sorted after all other partitions. It also has other
+ // special handling (see createPhdrs() and combineEhSections()).
+ in.partEnd = make<BssSection>(".part.end", config->maxPageSize, 1);
+ in.partEnd->partition = 255;
+ add(in.partEnd);
+
+ in.partIndex = make<PartitionIndexSection>();
+ addOptionalRegular("__part_index_begin", in.partIndex, 0);
+ addOptionalRegular("__part_index_end", in.partIndex,
+ in.partIndex->getSize());
+ add(in.partIndex);
}
// Add .got. MIPS' .got is so different from the other archs,
// it has its own class.
- if (Config->EMachine == EM_MIPS) {
- In.MipsGot = make<MipsGotSection>();
- Add(In.MipsGot);
+ if (config->emachine == EM_MIPS) {
+ in.mipsGot = make<MipsGotSection>();
+ add(in.mipsGot);
} else {
- In.Got = make<GotSection>();
- Add(In.Got);
+ in.got = make<GotSection>();
+ add(in.got);
+ }
+
+ if (config->emachine == EM_PPC) {
+ in.ppc32Got2 = make<PPC32Got2Section>();
+ add(in.ppc32Got2);
}
- if (Config->EMachine == EM_PPC64) {
- In.PPC64LongBranchTarget = make<PPC64LongBranchTargetSection>();
- Add(In.PPC64LongBranchTarget);
+ if (config->emachine == EM_PPC64) {
+ in.ppc64LongBranchTarget = make<PPC64LongBranchTargetSection>();
+ add(in.ppc64LongBranchTarget);
}
- In.GotPlt = make<GotPltSection>();
- Add(In.GotPlt);
- In.IgotPlt = make<IgotPltSection>();
- Add(In.IgotPlt);
+ if (config->emachine == EM_RISCV) {
+ in.riscvSdata = make<RISCVSdataSection>();
+ add(in.riscvSdata);
+ }
+
+ in.gotPlt = make<GotPltSection>();
+ add(in.gotPlt);
+ in.igotPlt = make<IgotPltSection>();
+ add(in.igotPlt);
- if (Config->GdbIndex) {
- In.GdbIndex = GdbIndexSection::create<ELFT>();
- Add(In.GdbIndex);
+ // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat
+ // it as a relocation and ensure the referenced section is created.
+ if (ElfSym::globalOffsetTable && config->emachine != EM_MIPS) {
+ if (target->gotBaseSymInGotPlt)
+ in.gotPlt->hasGotPltOffRel = true;
+ else
+ in.got->hasGotOffRel = true;
}
+ if (config->gdbIndex)
+ add(GdbIndexSection::create<ELFT>());
+
// We always need to add rel[a].plt to output if it has entries.
// Even for static linking it can contain R_[*]_IRELATIVE relocations.
- In.RelaPlt = make<RelocationSection<ELFT>>(
- Config->IsRela ? ".rela.plt" : ".rel.plt", false /*Sort*/);
- Add(In.RelaPlt);
+ in.relaPlt = make<RelocationSection<ELFT>>(
+ config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
+ add(in.relaPlt);
- // The RelaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
+ // The relaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
// that the IRelative relocations are processed last by the dynamic loader.
// We cannot place the iplt section in .rel.dyn when Android relocation
// packing is enabled because that would cause a section type mismatch.
// However, because the Android dynamic loader reads .rel.plt after .rel.dyn,
// we can get the desired behaviour by placing the iplt section in .rel.plt.
- In.RelaIplt = make<RelocationSection<ELFT>>(
- (Config->EMachine == EM_ARM && !Config->AndroidPackDynRelocs)
+ in.relaIplt = make<RelocationSection<ELFT>>(
+ (config->emachine == EM_ARM && !config->androidPackDynRelocs)
? ".rel.dyn"
- : In.RelaPlt->Name,
- false /*Sort*/);
- Add(In.RelaIplt);
+ : in.relaPlt->name,
+ /*sort=*/false);
+ add(in.relaIplt);
+
+ in.plt = make<PltSection>(false);
+ add(in.plt);
+ in.iplt = make<PltSection>(true);
+ add(in.iplt);
- In.Plt = make<PltSection>(false);
- Add(In.Plt);
- In.Iplt = make<PltSection>(true);
- Add(In.Iplt);
+ if (config->andFeatures)
+ add(make<GnuPropertySection>());
// .note.GNU-stack is always added when we are creating a re-linkable
// object file. Other linkers are using the presence of this marker
// section to control the executable-ness of the stack area, but that
// is irrelevant these days. Stack area should always be non-executable
// by default. So we emit this section unconditionally.
- if (Config->Relocatable)
- Add(make<GnuStackSection>());
-
- if (!Config->Relocatable) {
- if (Config->EhFrameHdr) {
- In.EhFrameHdr = make<EhFrameHeader>();
- Add(In.EhFrameHdr);
- }
- In.EhFrame = make<EhFrameSection>();
- Add(In.EhFrame);
- }
-
- if (In.SymTab)
- Add(In.SymTab);
- if (In.SymTabShndx)
- Add(In.SymTabShndx);
- Add(In.ShStrTab);
- if (In.StrTab)
- Add(In.StrTab);
+ if (config->relocatable)
+ add(make<GnuStackSection>());
- if (Config->EMachine == EM_ARM && !Config->Relocatable)
- // Add a sentinel to terminate .ARM.exidx. It helps an unwinder
- // to find the exact address range of the last entry.
- Add(make<ARMExidxSentinelSection>());
+ if (in.symTab)
+ add(in.symTab);
+ if (in.symTabShndx)
+ add(in.symTabShndx);
+ add(in.shStrTab);
+ if (in.strTab)
+ add(in.strTab);
}
// The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() {
+ // Make copies of any input sections that need to be copied into each
+ // partition.
+ copySectionsIntoPartitions<ELFT>();
+
// Create linker-synthesized sections such as .got or .plt.
// Such sections are of type input section.
createSyntheticSections<ELFT>();
- if (!Config->Relocatable)
- combineEhFrameSections<ELFT>();
+ // Some input sections that are used for exception handling need to be moved
+ // into synthetic sections. Do that now so that they aren't assigned to
+ // output sections in the usual way.
+ if (!config->relocatable)
+ combineEhSections<ELFT>();
// We want to process linker script commands. When SECTIONS command
// is given we let it create sections.
- Script->processSectionCommands();
+ script->processSectionCommands();
// Linker scripts controls how input sections are assigned to output sections.
// Input sections that were not handled by scripts are called "orphans", and
// they are assigned to output sections by the default rule. Process that.
- Script->addOrphanSections();
+ script->addOrphanSections();
- if (Config->Discard != DiscardPolicy::All)
+ if (config->discard != DiscardPolicy::All)
copyLocalSymbols();
- if (Config->CopyRelocs)
+ if (config->copyRelocs)
addSectionSymbols();
// Now that we have a complete set of output sections. This function
@@ -478,33 +582,35 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- Script->assignAddresses();
+ script->assignAddresses();
// If -compressed-debug-sections is specified, we need to compress
// .debug_* sections. Do it right now because it changes the size of
// output sections.
- for (OutputSection *Sec : OutputSections)
- Sec->maybeCompress<ELFT>();
+ for (OutputSection *sec : outputSections)
+ sec->maybeCompress<ELFT>();
- Script->allocateHeaders(Phdrs);
+ script->allocateHeaders(mainPart->phdrs);
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
// 0 sized region. This has to be done late since only after assignAddresses
// we know the size of the sections.
- removeEmptyPTLoad();
+ for (Partition &part : partitions)
+ removeEmptyPTLoad(part.phdrs);
- if (!Config->OFormatBinary)
+ if (!config->oFormatBinary)
assignFileOffsets();
else
assignFileOffsetsBinary();
- setPhdrs();
+ for (Partition &part : partitions)
+ setPhdrs(part);
- if (Config->Relocatable)
- for (OutputSection *Sec : OutputSections)
- Sec->Addr = 0;
+ if (config->relocatable)
+ for (OutputSection *sec : outputSections)
+ sec->addr = 0;
- if (Config->CheckSections)
+ if (config->checkSections)
checkSections();
// It does not make sense try to open the file if we have error already.
@@ -515,7 +621,7 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- if (!Config->OFormatBinary) {
+ if (!config->oFormatBinary) {
writeTrapInstr();
writeHeader();
writeSections();
@@ -535,16 +641,20 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- if (auto E = Buffer->commit())
- error("failed to write to the output file: " + toString(std::move(E)));
+ if (auto e = buffer->commit())
+ error("failed to write to the output file: " + toString(std::move(e)));
}
-static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
- const Symbol &B) {
- if (B.isSection())
+static bool shouldKeepInSymtab(const Defined &sym) {
+ if (sym.isSection())
return false;
- if (Config->Discard == DiscardPolicy::None)
+ if (config->discard == DiscardPolicy::None)
+ 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)
return true;
// In ELF assembly .L symbols are normally discarded by the assembler.
@@ -552,61 +662,62 @@ static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
// * --discard-locals is used.
// * The symbol is in a SHF_MERGE section, which is normally the reason for
// the assembler keeping the .L symbol.
- if (!SymName.startswith(".L") && !SymName.empty())
+ StringRef name = sym.getName();
+ bool isLocal = name.startswith(".L") || name.empty();
+ if (!isLocal)
return true;
- if (Config->Discard == DiscardPolicy::Locals)
+ if (config->discard == DiscardPolicy::Locals)
return false;
- return !Sec || !(Sec->Flags & SHF_MERGE);
+ SectionBase *sec = sym.section;
+ return !sec || !(sec->flags & SHF_MERGE);
}
-static bool includeInSymtab(const Symbol &B) {
- if (!B.isLocal() && !B.IsUsedInRegularObj)
+static bool includeInSymtab(const Symbol &b) {
+ if (!b.isLocal() && !b.isUsedInRegularObj)
return false;
- if (auto *D = dyn_cast<Defined>(&B)) {
+ if (auto *d = dyn_cast<Defined>(&b)) {
// Always include absolute symbols.
- SectionBase *Sec = D->Section;
- if (!Sec)
+ SectionBase *sec = d->section;
+ if (!sec)
return true;
- Sec = Sec->Repl;
+ sec = sec->repl;
// Exclude symbols pointing to garbage-collected sections.
- if (isa<InputSectionBase>(Sec) && !Sec->Live)
+ if (isa<InputSectionBase>(sec) && !sec->isLive())
return false;
- if (auto *S = dyn_cast<MergeInputSection>(Sec))
- if (!S->getSectionPiece(D->Value)->Live)
+ if (auto *s = dyn_cast<MergeInputSection>(sec))
+ if (!s->getSectionPiece(d->value)->live)
return false;
return true;
}
- return B.Used;
+ return b.used;
}
// Local symbols are not in the linker's symbol table. This function scans
// each object file's symbol table to copy local symbols to the output.
template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
- if (!In.SymTab)
+ if (!in.symTab)
return;
- for (InputFile *File : ObjectFiles) {
- ObjFile<ELFT> *F = cast<ObjFile<ELFT>>(File);
- for (Symbol *B : F->getLocalSymbols()) {
- if (!B->isLocal())
- fatal(toString(F) +
+ 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");
- auto *DR = dyn_cast<Defined>(B);
+ auto *dr = dyn_cast<Defined>(b);
// No reason to keep local undefined symbol in symtab.
- if (!DR)
+ if (!dr)
continue;
- if (!includeInSymtab(*B))
+ if (!includeInSymtab(*b))
continue;
-
- SectionBase *Sec = DR->Section;
- if (!shouldKeepInSymtab(Sec, B->getName(), *B))
+ if (!shouldKeepInSymtab(*dr))
continue;
- In.SymTab->addSymbol(B);
+ in.symTab->addSymbol(b);
}
}
}
@@ -616,33 +727,33 @@ template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
// referring to a section (that happens if the section is a synthetic one), we
// don't create a section symbol for that section.
template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
- for (BaseCommand *Base : Script->SectionCommands) {
- auto *Sec = dyn_cast<OutputSection>(Base);
- if (!Sec)
+ for (BaseCommand *base : script->sectionCommands) {
+ auto *sec = dyn_cast<OutputSection>(base);
+ if (!sec)
continue;
- auto I = llvm::find_if(Sec->SectionCommands, [](BaseCommand *Base) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- return !ISD->Sections.empty();
+ auto i = llvm::find_if(sec->sectionCommands, [](BaseCommand *base) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ return !isd->sections.empty();
return false;
});
- if (I == Sec->SectionCommands.end())
+ if (i == sec->sectionCommands.end())
continue;
- InputSection *IS = cast<InputSectionDescription>(*I)->Sections[0];
+ InputSection *isec = cast<InputSectionDescription>(*i)->sections[0];
// Relocations are not using REL[A] section symbols.
- if (IS->Type == SHT_REL || IS->Type == SHT_RELA)
+ if (isec->type == SHT_REL || isec->type == SHT_RELA)
continue;
// Unlike other synthetic sections, mergeable output sections contain data
// copied from input sections, and there may be a relocation pointing to its
// contents if -r or -emit-reloc are given.
- if (isa<SyntheticSection>(IS) && !(IS->Flags & SHF_MERGE))
+ if (isa<SyntheticSection>(isec) && !(isec->flags & SHF_MERGE))
continue;
- auto *Sym =
- make<Defined>(IS->File, "", STB_LOCAL, /*StOther=*/0, STT_SECTION,
- /*Value=*/0, /*Size=*/0, IS);
- In.SymTab->addSymbol(Sym);
+ auto *sym =
+ make<Defined>(isec->file, "", STB_LOCAL, /*stOther=*/0, STT_SECTION,
+ /*value=*/0, /*size=*/0, isec);
+ in.symTab->addSymbol(sym);
}
}
@@ -652,25 +763,25 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
//
// This function returns true if a section needs to be put into a
// PT_GNU_RELRO segment.
-static bool isRelroSection(const OutputSection *Sec) {
- if (!Config->ZRelro)
+static bool isRelroSection(const OutputSection *sec) {
+ if (!config->zRelro)
return false;
- uint64_t Flags = Sec->Flags;
+ uint64_t flags = sec->flags;
// Non-allocatable or non-writable sections don't need RELRO because
// they are not writable or not even mapped to memory in the first place.
// RELRO is for sections that are essentially read-only but need to
// be writable only at process startup to allow dynamic linker to
// apply relocations.
- if (!(Flags & SHF_ALLOC) || !(Flags & SHF_WRITE))
+ if (!(flags & SHF_ALLOC) || !(flags & SHF_WRITE))
return false;
// Once initialized, TLS data segments are used as data templates
// for a thread-local storage. For each new thread, runtime
// allocates memory for a TLS and copy templates there. No thread
// are supposed to use templates directly. Thus, it can be in RELRO.
- if (Flags & SHF_TLS)
+ if (flags & SHF_TLS)
return true;
// .init_array, .preinit_array and .fini_array contain pointers to
@@ -679,15 +790,15 @@ static bool isRelroSection(const OutputSection *Sec) {
// to change at runtime. But if you are an attacker, you could do
// interesting things by manipulating pointers in .fini_array, for
// example. So they are put into RELRO.
- uint32_t Type = Sec->Type;
- if (Type == SHT_INIT_ARRAY || Type == SHT_FINI_ARRAY ||
- Type == SHT_PREINIT_ARRAY)
+ uint32_t type = sec->type;
+ if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY ||
+ type == SHT_PREINIT_ARRAY)
return true;
// .got contains pointers to external symbols. They are resolved by
// the dynamic linker when a module is loaded into memory, and after
// that they are not expected to change. So, it can be in RELRO.
- if (In.Got && Sec == In.Got->getParent())
+ if (in.got && sec == in.got->getParent())
return true;
// .toc is a GOT-ish section for PowerPC64. Their contents are accessed
@@ -695,30 +806,30 @@ static bool isRelroSection(const OutputSection *Sec) {
// for accessing .got as well, .got and .toc need to be close enough in the
// virtual address space. Usually, .toc comes just after .got. Since we place
// .got into RELRO, .toc needs to be placed into RELRO too.
- if (Sec->Name.equals(".toc"))
+ if (sec->name.equals(".toc"))
return true;
// .got.plt contains pointers to external function symbols. They are
// by default resolved lazily, so we usually cannot put it into RELRO.
// However, if "-z now" is given, the lazy symbol resolution is
// disabled, which enables us to put it into RELRO.
- if (Sec == In.GotPlt->getParent())
- return Config->ZNow;
+ if (sec == in.gotPlt->getParent())
+ return config->zNow;
// .dynamic section contains data for the dynamic linker, and
// there's no need to write to it at runtime, so it's better to put
// it into RELRO.
- if (Sec == In.Dynamic->getParent())
+ if (sec->name == ".dynamic")
return true;
// Sections with some special names are put into RELRO. This is a
// bit unfortunate because section names shouldn't be significant in
// ELF in spirit. But in reality many linker features depend on
// magic section names.
- 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";
+ 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";
}
// We compute a rank for each section. The rank indicates where the
@@ -729,16 +840,18 @@ static bool isRelroSection(const OutputSection *Sec) {
// * It is easy to check if a give branch was taken.
// * It is easy two see how similar two ranks are (see getRankProximity).
enum RankFlags {
- RF_NOT_ADDR_SET = 1 << 18,
- RF_NOT_ALLOC = 1 << 17,
- RF_NOT_INTERP = 1 << 16,
- RF_NOT_NOTE = 1 << 15,
- RF_WRITE = 1 << 14,
- RF_EXEC_WRITE = 1 << 13,
- RF_EXEC = 1 << 12,
- RF_RODATA = 1 << 11,
- RF_NON_TLS_BSS = 1 << 10,
- RF_NON_TLS_BSS_RO = 1 << 9,
+ RF_NOT_ADDR_SET = 1 << 27,
+ RF_NOT_ALLOC = 1 << 26,
+ RF_PARTITION = 1 << 18, // Partition number (8 bits)
+ RF_NOT_PART_EHDR = 1 << 17,
+ RF_NOT_PART_PHDR = 1 << 16,
+ RF_NOT_INTERP = 1 << 15,
+ RF_NOT_NOTE = 1 << 14,
+ RF_WRITE = 1 << 13,
+ RF_EXEC_WRITE = 1 << 12,
+ RF_EXEC = 1 << 11,
+ RF_RODATA = 1 << 10,
+ RF_NOT_RELRO = 1 << 9,
RF_NOT_TLS = 1 << 8,
RF_BSS = 1 << 7,
RF_PPC_NOT_TOCBSS = 1 << 6,
@@ -750,33 +863,41 @@ enum RankFlags {
RF_MIPS_NOT_GOT = 1 << 0
};
-static unsigned getSectionRank(const OutputSection *Sec) {
- unsigned Rank = 0;
+static unsigned getSectionRank(const OutputSection *sec) {
+ unsigned rank = sec->partition * RF_PARTITION;
// We want to put section specified by -T option first, so we
// can start assigning VA starting from them later.
- if (Config->SectionStartMap.count(Sec->Name))
- return Rank;
- Rank |= RF_NOT_ADDR_SET;
+ if (config->sectionStartMap.count(sec->name))
+ return rank;
+ rank |= RF_NOT_ADDR_SET;
// Allocatable sections go first to reduce the total PT_LOAD size and
// so debug info doesn't change addresses in actual code.
- if (!(Sec->Flags & SHF_ALLOC))
- return Rank | RF_NOT_ALLOC;
+ if (!(sec->flags & SHF_ALLOC))
+ return rank | RF_NOT_ALLOC;
+
+ if (sec->type == SHT_LLVM_PART_EHDR)
+ return rank;
+ rank |= RF_NOT_PART_EHDR;
+
+ if (sec->type == SHT_LLVM_PART_PHDR)
+ return rank;
+ rank |= RF_NOT_PART_PHDR;
// Put .interp first because some loaders want to see that section
// on the first page of the executable file when loaded into memory.
- if (Sec->Name == ".interp")
- return Rank;
- Rank |= RF_NOT_INTERP;
+ if (sec->name == ".interp")
+ return rank;
+ rank |= RF_NOT_INTERP;
// Put .note sections (which make up one PT_NOTE) at the beginning so that
// they are likely to be included in a core file even if core file size is
// limited. In particular, we want a .note.gnu.build-id and a .note.tag to be
// included in a core to match core files with executables.
- if (Sec->Type == SHT_NOTE)
- return Rank;
- Rank |= RF_NOT_NOTE;
+ if (sec->type == SHT_NOTE)
+ return rank;
+ rank |= RF_NOT_NOTE;
// Sort sections based on their access permission in the following
// order: R, RX, RWX, RW. This order is based on the following
@@ -789,116 +910,105 @@ static unsigned getSectionRank(const OutputSection *Sec) {
// between .text and .data.
// * Writable sections come last, such that .bss lands at the very
// end of the last PT_LOAD.
- bool IsExec = Sec->Flags & SHF_EXECINSTR;
- bool IsWrite = Sec->Flags & SHF_WRITE;
+ bool isExec = sec->flags & SHF_EXECINSTR;
+ bool isWrite = sec->flags & SHF_WRITE;
- if (IsExec) {
- if (IsWrite)
- Rank |= RF_EXEC_WRITE;
+ if (isExec) {
+ if (isWrite)
+ rank |= RF_EXEC_WRITE;
else
- Rank |= RF_EXEC;
- } else if (IsWrite) {
- Rank |= RF_WRITE;
- } else if (Sec->Type == SHT_PROGBITS) {
+ rank |= RF_EXEC;
+ } else if (isWrite) {
+ rank |= RF_WRITE;
+ } else if (sec->type == SHT_PROGBITS) {
// Make non-executable and non-writable PROGBITS sections (e.g .rodata
// .eh_frame) closer to .text. They likely contain PC or GOT relative
// relocations and there could be relocation overflow if other huge sections
// (.dynstr .dynsym) were placed in between.
- Rank |= RF_RODATA;
+ rank |= RF_RODATA;
}
- // If we got here we know that both A and B are in the same PT_LOAD.
+ // Place RelRo sections first. After considering SHT_NOBITS below, the
+ // ordering is PT_LOAD(PT_GNU_RELRO(.data.rel.ro .bss.rel.ro) | .data .bss),
+ // where | marks where page alignment happens. An alternative ordering is
+ // PT_LOAD(.data | PT_GNU_RELRO( .data.rel.ro .bss.rel.ro) | .bss), but it may
+ // waste more bytes due to 2 alignment places.
+ if (!isRelroSection(sec))
+ rank |= RF_NOT_RELRO;
- bool IsTls = Sec->Flags & SHF_TLS;
- bool IsNoBits = Sec->Type == SHT_NOBITS;
-
- // The first requirement we have is to put (non-TLS) nobits sections last. The
- // reason is that the only thing the dynamic linker will see about them is a
- // p_memsz that is larger than p_filesz. Seeing that it zeros the end of the
- // PT_LOAD, so that has to correspond to the nobits sections.
- bool IsNonTlsNoBits = IsNoBits && !IsTls;
- if (IsNonTlsNoBits)
- Rank |= RF_NON_TLS_BSS;
-
- // We place nobits RelRo sections before plain r/w ones, and non-nobits RelRo
- // sections after r/w ones, so that the RelRo sections are contiguous.
- bool IsRelRo = isRelroSection(Sec);
- if (IsNonTlsNoBits && !IsRelRo)
- Rank |= RF_NON_TLS_BSS_RO;
- if (!IsNonTlsNoBits && IsRelRo)
- Rank |= RF_NON_TLS_BSS_RO;
+ // If we got here we know that both A and B are in the same PT_LOAD.
// The TLS initialization block needs to be a single contiguous block in a R/W
// PT_LOAD, so stick TLS sections directly before the other RelRo R/W
- // sections. The TLS NOBITS sections are placed here as they don't take up
- // virtual address space in the PT_LOAD.
- if (!IsTls)
- Rank |= RF_NOT_TLS;
+ // sections. Since p_filesz can be less than p_memsz, place NOBITS sections
+ // after PROGBITS.
+ if (!(sec->flags & SHF_TLS))
+ rank |= RF_NOT_TLS;
- // Within the TLS initialization block, the non-nobits sections need to appear
- // first.
- if (IsNoBits)
- Rank |= RF_BSS;
+ // Within TLS sections, or within other RelRo sections, or within non-RelRo
+ // sections, place non-NOBITS sections first.
+ if (sec->type == SHT_NOBITS)
+ rank |= RF_BSS;
// Some architectures have additional ordering restrictions for sections
// within the same PT_LOAD.
- if (Config->EMachine == EM_PPC64) {
+ if (config->emachine == EM_PPC64) {
// PPC64 has a number of special SHT_PROGBITS+SHF_ALLOC+SHF_WRITE sections
// that we would like to make sure appear is a specific order to maximize
// their coverage by a single signed 16-bit offset from the TOC base
// pointer. Conversely, the special .tocbss section should be first among
// all SHT_NOBITS sections. This will put it next to the loaded special
// PPC64 sections (and, thus, within reach of the TOC base pointer).
- StringRef Name = Sec->Name;
- if (Name != ".tocbss")
- Rank |= RF_PPC_NOT_TOCBSS;
+ StringRef name = sec->name;
+ if (name != ".tocbss")
+ rank |= RF_PPC_NOT_TOCBSS;
- if (Name == ".toc1")
- Rank |= RF_PPC_TOCL;
+ if (name == ".toc1")
+ rank |= RF_PPC_TOCL;
- if (Name == ".toc")
- Rank |= RF_PPC_TOC;
+ if (name == ".toc")
+ rank |= RF_PPC_TOC;
- if (Name == ".got")
- Rank |= RF_PPC_GOT;
+ if (name == ".got")
+ rank |= RF_PPC_GOT;
- if (Name == ".branch_lt")
- Rank |= RF_PPC_BRANCH_LT;
+ if (name == ".branch_lt")
+ rank |= RF_PPC_BRANCH_LT;
}
- if (Config->EMachine == EM_MIPS) {
+ if (config->emachine == EM_MIPS) {
// All sections with SHF_MIPS_GPREL flag should be grouped together
// because data in these sections is addressable with a gp relative address.
- if (Sec->Flags & SHF_MIPS_GPREL)
- Rank |= RF_MIPS_GPREL;
+ if (sec->flags & SHF_MIPS_GPREL)
+ rank |= RF_MIPS_GPREL;
- if (Sec->Name != ".got")
- Rank |= RF_MIPS_NOT_GOT;
+ if (sec->name != ".got")
+ rank |= RF_MIPS_NOT_GOT;
}
- return Rank;
+ return rank;
}
-static bool compareSections(const BaseCommand *ACmd, const BaseCommand *BCmd) {
- const OutputSection *A = cast<OutputSection>(ACmd);
- const OutputSection *B = cast<OutputSection>(BCmd);
+static bool compareSections(const BaseCommand *aCmd, const BaseCommand *bCmd) {
+ const OutputSection *a = cast<OutputSection>(aCmd);
+ const OutputSection *b = cast<OutputSection>(bCmd);
- if (A->SortRank != B->SortRank)
- return A->SortRank < B->SortRank;
+ if (a->sortRank != b->sortRank)
+ return a->sortRank < b->sortRank;
- if (!(A->SortRank & RF_NOT_ADDR_SET))
- return Config->SectionStartMap.lookup(A->Name) <
- Config->SectionStartMap.lookup(B->Name);
+ if (!(a->sortRank & RF_NOT_ADDR_SET))
+ return config->sectionStartMap.lookup(a->name) <
+ config->sectionStartMap.lookup(b->name);
return false;
}
-void PhdrEntry::add(OutputSection *Sec) {
- LastSec = Sec;
- if (!FirstSec)
- FirstSec = Sec;
- p_align = std::max(p_align, Sec->Alignment);
+void PhdrEntry::add(OutputSection *sec) {
+ lastSec = sec;
+ if (!firstSec)
+ firstSec = sec;
+ p_align = std::max(p_align, sec->alignment);
if (p_type == PT_LOAD)
- Sec->PtLoad = this;
+ sec->ptLoad = this;
}
// The beginning and the ending of .rel[a].plt section are marked
@@ -908,35 +1018,40 @@ void PhdrEntry::add(OutputSection *Sec) {
// need these symbols, since IRELATIVE relocs are resolved through GOT
// and PLT. For details, see http://www.airs.com/blog/archives/403.
template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() {
- if (Config->Relocatable || needsInterpSection())
+ if (config->relocatable || needsInterpSection())
return;
// By default, __rela_iplt_{start,end} belong to a dummy section 0
// because .rela.plt might be empty and thus removed from output.
- // We'll override Out::ElfHeader with In.RelaIplt later when we are
+ // We'll override Out::elfHeader with In.relaIplt later when we are
// sure that .rela.plt exists in output.
- ElfSym::RelaIpltStart = addOptionalRegular(
- Config->IsRela ? "__rela_iplt_start" : "__rel_iplt_start",
- Out::ElfHeader, 0, STV_HIDDEN, STB_WEAK);
+ ElfSym::relaIpltStart = addOptionalRegular(
+ config->isRela ? "__rela_iplt_start" : "__rel_iplt_start",
+ Out::elfHeader, 0, STV_HIDDEN, STB_WEAK);
- ElfSym::RelaIpltEnd = addOptionalRegular(
- Config->IsRela ? "__rela_iplt_end" : "__rel_iplt_end",
- Out::ElfHeader, 0, STV_HIDDEN, STB_WEAK);
+ ElfSym::relaIpltEnd = addOptionalRegular(
+ config->isRela ? "__rela_iplt_end" : "__rel_iplt_end",
+ Out::elfHeader, 0, STV_HIDDEN, STB_WEAK);
}
template <class ELFT>
void Writer<ELFT>::forEachRelSec(
- llvm::function_ref<void(InputSectionBase &)> Fn) {
+ llvm::function_ref<void(InputSectionBase &)> fn) {
// Scan all relocations. Each relocation goes through a series
// of tests to determine if it needs special treatment, such as
// creating GOT, PLT, copy relocations, etc.
// Note that relocations for non-alloc sections are directly
// processed by InputSection::relocateNonAlloc.
- for (InputSectionBase *IS : InputSections)
- if (IS->Live && isa<InputSection>(IS) && (IS->Flags & SHF_ALLOC))
- Fn(*IS);
- for (EhInputSection *ES : In.EhFrame->Sections)
- Fn(*ES);
+ for (InputSectionBase *isec : inputSections)
+ if (isec->isLive() && isa<InputSection>(isec) && (isec->flags & SHF_ALLOC))
+ fn(*isec);
+ for (Partition &part : partitions) {
+ for (EhInputSection *es : part.ehFrame->sections)
+ fn(*es);
+ if (part.armExidx && part.armExidx->isLive())
+ for (InputSection *ex : part.armExidx->exidxSections)
+ fn(*ex);
+ }
}
// This function generates assignments for predefined symbols (e.g. _end or
@@ -945,76 +1060,78 @@ void Writer<ELFT>::forEachRelSec(
// time any references to these symbols are processed and is equivalent to
// defining these symbols explicitly in the linker script.
template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
- if (ElfSym::GlobalOffsetTable) {
+ if (ElfSym::globalOffsetTable) {
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
// to the start of the .got or .got.plt section.
- InputSection *GotSection = In.GotPlt;
- if (!Target->GotBaseSymInGotPlt)
- GotSection = In.MipsGot ? cast<InputSection>(In.MipsGot)
- : cast<InputSection>(In.Got);
- ElfSym::GlobalOffsetTable->Section = GotSection;
+ InputSection *gotSection = in.gotPlt;
+ if (!target->gotBaseSymInGotPlt)
+ gotSection = in.mipsGot ? cast<InputSection>(in.mipsGot)
+ : cast<InputSection>(in.got);
+ ElfSym::globalOffsetTable->section = gotSection;
}
// .rela_iplt_{start,end} mark the start and the end of .rela.plt section.
- if (ElfSym::RelaIpltStart && !In.RelaIplt->empty()) {
- ElfSym::RelaIpltStart->Section = In.RelaIplt;
- ElfSym::RelaIpltEnd->Section = In.RelaIplt;
- ElfSym::RelaIpltEnd->Value = In.RelaIplt->getSize();
+ if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) {
+ ElfSym::relaIpltStart->section = in.relaIplt;
+ ElfSym::relaIpltEnd->section = in.relaIplt;
+ ElfSym::relaIpltEnd->value = in.relaIplt->getSize();
}
- PhdrEntry *Last = nullptr;
- PhdrEntry *LastRO = nullptr;
+ PhdrEntry *last = nullptr;
+ PhdrEntry *lastRO = nullptr;
- for (PhdrEntry *P : Phdrs) {
- if (P->p_type != PT_LOAD)
- continue;
- Last = P;
- if (!(P->p_flags & PF_W))
- LastRO = P;
+ for (Partition &part : partitions) {
+ for (PhdrEntry *p : part.phdrs) {
+ if (p->p_type != PT_LOAD)
+ continue;
+ last = p;
+ if (!(p->p_flags & PF_W))
+ lastRO = p;
+ }
}
- if (LastRO) {
+ if (lastRO) {
// _etext is the first location after the last read-only loadable segment.
- if (ElfSym::Etext1)
- ElfSym::Etext1->Section = LastRO->LastSec;
- if (ElfSym::Etext2)
- ElfSym::Etext2->Section = LastRO->LastSec;
+ if (ElfSym::etext1)
+ ElfSym::etext1->section = lastRO->lastSec;
+ if (ElfSym::etext2)
+ ElfSym::etext2->section = lastRO->lastSec;
}
- if (Last) {
+ if (last) {
// _edata points to the end of the last mapped initialized section.
- OutputSection *Edata = nullptr;
- for (OutputSection *OS : OutputSections) {
- if (OS->Type != SHT_NOBITS)
- Edata = OS;
- if (OS == Last->LastSec)
+ OutputSection *edata = nullptr;
+ for (OutputSection *os : outputSections) {
+ if (os->type != SHT_NOBITS)
+ edata = os;
+ if (os == last->lastSec)
break;
}
- if (ElfSym::Edata1)
- ElfSym::Edata1->Section = Edata;
- if (ElfSym::Edata2)
- ElfSym::Edata2->Section = Edata;
+ if (ElfSym::edata1)
+ ElfSym::edata1->section = edata;
+ if (ElfSym::edata2)
+ ElfSym::edata2->section = edata;
// _end is the first location after the uninitialized data region.
- if (ElfSym::End1)
- ElfSym::End1->Section = Last->LastSec;
- if (ElfSym::End2)
- ElfSym::End2->Section = Last->LastSec;
+ if (ElfSym::end1)
+ ElfSym::end1->section = last->lastSec;
+ if (ElfSym::end2)
+ ElfSym::end2->section = last->lastSec;
}
- if (ElfSym::Bss)
- ElfSym::Bss->Section = findSection(".bss");
+ if (ElfSym::bss)
+ ElfSym::bss->section = findSection(".bss");
// Setup MIPS _gp_disp/__gnu_local_gp symbols which should
// be equal to the _gp symbol's value.
- if (ElfSym::MipsGp) {
+ if (ElfSym::mipsGp) {
// Find GP-relative section with the lowest address
// and use this address to calculate default _gp value.
- for (OutputSection *OS : OutputSections) {
- if (OS->Flags & SHF_MIPS_GPREL) {
- ElfSym::MipsGp->Section = OS;
- ElfSym::MipsGp->Value = 0x7ff0;
+ for (OutputSection *os : outputSections) {
+ if (os->flags & SHF_MIPS_GPREL) {
+ ElfSym::mipsGp->section = os;
+ ElfSym::mipsGp->value = 0x7ff0;
break;
}
}
@@ -1025,14 +1142,13 @@ template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
// The more branches in getSectionRank that match, the more similar they are.
// Since each branch corresponds to a bit flag, we can just use
// countLeadingZeros.
-static int getRankProximityAux(OutputSection *A, OutputSection *B) {
- return countLeadingZeros(A->SortRank ^ B->SortRank);
+static int getRankProximityAux(OutputSection *a, OutputSection *b) {
+ return countLeadingZeros(a->sortRank ^ b->sortRank);
}
-static int getRankProximity(OutputSection *A, BaseCommand *B) {
- if (auto *Sec = dyn_cast<OutputSection>(B))
- return getRankProximityAux(A, Sec);
- return -1;
+static int getRankProximity(OutputSection *a, BaseCommand *b) {
+ auto *sec = dyn_cast<OutputSection>(b);
+ return (sec && sec->hasInputSections) ? getRankProximityAux(a, sec) : -1;
}
// When placing orphan sections, we want to place them after symbol assignments
@@ -1049,137 +1165,142 @@ static int getRankProximity(OutputSection *A, BaseCommand *B) {
// /* The RW PT_LOAD starts here*/
// rw_sec : { *(rw_sec) }
// would mean that the RW PT_LOAD would become unaligned.
-static bool shouldSkip(BaseCommand *Cmd) {
- if (auto *Assign = dyn_cast<SymbolAssignment>(Cmd))
- return Assign->Name != ".";
+static bool shouldSkip(BaseCommand *cmd) {
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
+ return assign->name != ".";
return false;
}
// We want to place orphan sections so that they share as much
// characteristics with their neighbors as possible. For example, if
// both are rw, or both are tls.
-template <typename ELFT>
static std::vector<BaseCommand *>::iterator
-findOrphanPos(std::vector<BaseCommand *>::iterator B,
- std::vector<BaseCommand *>::iterator E) {
- OutputSection *Sec = cast<OutputSection>(*E);
+findOrphanPos(std::vector<BaseCommand *>::iterator b,
+ std::vector<BaseCommand *>::iterator e) {
+ OutputSection *sec = cast<OutputSection>(*e);
// Find the first element that has as close a rank as possible.
- auto I = std::max_element(B, E, [=](BaseCommand *A, BaseCommand *B) {
- return getRankProximity(Sec, A) < getRankProximity(Sec, B);
+ auto i = std::max_element(b, e, [=](BaseCommand *a, BaseCommand *b) {
+ return getRankProximity(sec, a) < getRankProximity(sec, b);
});
- if (I == E)
- return E;
+ if (i == e)
+ return e;
// Consider all existing sections with the same proximity.
- int Proximity = getRankProximity(Sec, *I);
- for (; I != E; ++I) {
- auto *CurSec = dyn_cast<OutputSection>(*I);
- if (!CurSec)
+ int proximity = getRankProximity(sec, *i);
+ for (; i != e; ++i) {
+ auto *curSec = dyn_cast<OutputSection>(*i);
+ if (!curSec || !curSec->hasInputSections)
continue;
- if (getRankProximity(Sec, CurSec) != Proximity ||
- Sec->SortRank < CurSec->SortRank)
+ if (getRankProximity(sec, curSec) != proximity ||
+ sec->sortRank < curSec->sortRank)
break;
}
- auto IsOutputSec = [](BaseCommand *Cmd) { return isa<OutputSection>(Cmd); };
- auto J = std::find_if(llvm::make_reverse_iterator(I),
- llvm::make_reverse_iterator(B), IsOutputSec);
- I = J.base();
+ auto isOutputSecWithInputSections = [](BaseCommand *cmd) {
+ auto *os = dyn_cast<OutputSection>(cmd);
+ return os && os->hasInputSections;
+ };
+ auto j = std::find_if(llvm::make_reverse_iterator(i),
+ llvm::make_reverse_iterator(b),
+ isOutputSecWithInputSections);
+ i = j.base();
// As a special case, if the orphan section is the last section, put
// it at the very end, past any other commands.
// This matches bfd's behavior and is convenient when the linker script fully
// specifies the start of the file, but doesn't care about the end (the non
// alloc sections for example).
- auto NextSec = std::find_if(I, E, IsOutputSec);
- if (NextSec == E)
- return E;
+ auto nextSec = std::find_if(i, e, isOutputSecWithInputSections);
+ if (nextSec == e)
+ return e;
- while (I != E && shouldSkip(*I))
- ++I;
- return I;
+ while (i != e && shouldSkip(*i))
+ ++i;
+ return i;
}
// Builds section order for handling --symbol-ordering-file.
static DenseMap<const InputSectionBase *, int> buildSectionOrder() {
- DenseMap<const InputSectionBase *, int> SectionOrder;
+ DenseMap<const InputSectionBase *, int> sectionOrder;
// Use the rarely used option -call-graph-ordering-file to sort sections.
- if (!Config->CallGraphProfile.empty())
+ if (!config->callGraphProfile.empty())
return computeCallGraphProfileOrder();
- if (Config->SymbolOrderingFile.empty())
- return SectionOrder;
+ if (config->symbolOrderingFile.empty())
+ return sectionOrder;
struct SymbolOrderEntry {
- int Priority;
- bool Present;
+ int priority;
+ bool present;
};
// Build a map from symbols to their priorities. Symbols that didn't
// appear in the symbol ordering file have the lowest priority 0.
// All explicitly mentioned symbols have negative (higher) priorities.
- DenseMap<StringRef, SymbolOrderEntry> SymbolOrder;
- int Priority = -Config->SymbolOrderingFile.size();
- for (StringRef S : Config->SymbolOrderingFile)
- SymbolOrder.insert({S, {Priority++, false}});
+ DenseMap<StringRef, SymbolOrderEntry> symbolOrder;
+ int priority = -config->symbolOrderingFile.size();
+ for (StringRef s : config->symbolOrderingFile)
+ symbolOrder.insert({s, {priority++, false}});
// Build a map from sections to their priorities.
- auto AddSym = [&](Symbol &Sym) {
- auto It = SymbolOrder.find(Sym.getName());
- if (It == SymbolOrder.end())
+ auto addSym = [&](Symbol &sym) {
+ auto it = symbolOrder.find(sym.getName());
+ if (it == symbolOrder.end())
return;
- SymbolOrderEntry &Ent = It->second;
- Ent.Present = true;
+ SymbolOrderEntry &ent = it->second;
+ ent.present = true;
- maybeWarnUnorderableSymbol(&Sym);
+ maybeWarnUnorderableSymbol(&sym);
- if (auto *D = dyn_cast<Defined>(&Sym)) {
- if (auto *Sec = dyn_cast_or_null<InputSectionBase>(D->Section)) {
- int &Priority = SectionOrder[cast<InputSectionBase>(Sec->Repl)];
- Priority = std::min(Priority, Ent.Priority);
+ if (auto *d = dyn_cast<Defined>(&sym)) {
+ if (auto *sec = dyn_cast_or_null<InputSectionBase>(d->section)) {
+ int &priority = sectionOrder[cast<InputSectionBase>(sec->repl)];
+ priority = std::min(priority, ent.priority);
}
}
};
// We want both global and local symbols. We get the global ones from the
// symbol table and iterate the object files for the local ones.
- for (Symbol *Sym : Symtab->getSymbols())
- if (!Sym->isLazy())
- AddSym(*Sym);
- for (InputFile *File : ObjectFiles)
- for (Symbol *Sym : File->getSymbols())
- if (Sym->isLocal())
- AddSym(*Sym);
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (!sym->isLazy())
+ addSym(*sym);
+ });
- if (Config->WarnSymbolOrdering)
- for (auto OrderEntry : SymbolOrder)
- if (!OrderEntry.second.Present)
- warn("symbol ordering file: no such symbol: " + OrderEntry.first);
+ for (InputFile *file : objectFiles)
+ for (Symbol *sym : file->getSymbols())
+ if (sym->isLocal())
+ addSym(*sym);
- return SectionOrder;
+ if (config->warnSymbolOrdering)
+ for (auto orderEntry : symbolOrder)
+ if (!orderEntry.second.present)
+ warn("symbol ordering file: no such symbol: " + orderEntry.first);
+
+ return sectionOrder;
}
// Sorts the sections in ISD according to the provided section order.
static void
-sortISDBySectionOrder(InputSectionDescription *ISD,
- const DenseMap<const InputSectionBase *, int> &Order) {
- std::vector<InputSection *> UnorderedSections;
- std::vector<std::pair<InputSection *, int>> OrderedSections;
- uint64_t UnorderedSize = 0;
-
- for (InputSection *IS : ISD->Sections) {
- auto I = Order.find(IS);
- if (I == Order.end()) {
- UnorderedSections.push_back(IS);
- UnorderedSize += IS->getSize();
+sortISDBySectionOrder(InputSectionDescription *isd,
+ const DenseMap<const InputSectionBase *, int> &order) {
+ std::vector<InputSection *> unorderedSections;
+ std::vector<std::pair<InputSection *, int>> orderedSections;
+ uint64_t unorderedSize = 0;
+
+ for (InputSection *isec : isd->sections) {
+ auto i = order.find(isec);
+ if (i == order.end()) {
+ unorderedSections.push_back(isec);
+ unorderedSize += isec->getSize();
continue;
}
- OrderedSections.push_back({IS, I->second});
+ orderedSections.push_back({isec, i->second});
}
- llvm::sort(OrderedSections, [&](std::pair<InputSection *, int> A,
- std::pair<InputSection *, int> B) {
- return A.second < B.second;
+ llvm::sort(orderedSections, [&](std::pair<InputSection *, int> a,
+ std::pair<InputSection *, int> b) {
+ return a.second < b.second;
});
// Find an insertion point for the ordered section list in the unordered
@@ -1209,96 +1330,114 @@ sortISDBySectionOrder(InputSectionDescription *ISD,
// of the second block of cold code can call the hot code without a thunk. So
// we effectively double the amount of code that could potentially call into
// the hot code without a thunk.
- size_t InsPt = 0;
- if (Target->getThunkSectionSpacing() && !OrderedSections.empty()) {
- uint64_t UnorderedPos = 0;
- for (; InsPt != UnorderedSections.size(); ++InsPt) {
- UnorderedPos += UnorderedSections[InsPt]->getSize();
- if (UnorderedPos > UnorderedSize / 2)
+ size_t insPt = 0;
+ if (target->getThunkSectionSpacing() && !orderedSections.empty()) {
+ uint64_t unorderedPos = 0;
+ for (; insPt != unorderedSections.size(); ++insPt) {
+ unorderedPos += unorderedSections[insPt]->getSize();
+ if (unorderedPos > unorderedSize / 2)
break;
}
}
- ISD->Sections.clear();
- for (InputSection *IS : makeArrayRef(UnorderedSections).slice(0, InsPt))
- ISD->Sections.push_back(IS);
- for (std::pair<InputSection *, int> P : OrderedSections)
- ISD->Sections.push_back(P.first);
- for (InputSection *IS : makeArrayRef(UnorderedSections).slice(InsPt))
- ISD->Sections.push_back(IS);
+ isd->sections.clear();
+ for (InputSection *isec : makeArrayRef(unorderedSections).slice(0, insPt))
+ isd->sections.push_back(isec);
+ for (std::pair<InputSection *, int> p : orderedSections)
+ isd->sections.push_back(p.first);
+ for (InputSection *isec : makeArrayRef(unorderedSections).slice(insPt))
+ isd->sections.push_back(isec);
}
-static void sortSection(OutputSection *Sec,
- const DenseMap<const InputSectionBase *, int> &Order) {
- StringRef Name = Sec->Name;
+static void sortSection(OutputSection *sec,
+ const DenseMap<const InputSectionBase *, int> &order) {
+ StringRef name = sec->name;
// Sort input sections by section name suffixes for
// __attribute__((init_priority(N))).
- if (Name == ".init_array" || Name == ".fini_array") {
- if (!Script->HasSectionsCommand)
- Sec->sortInitFini();
+ if (name == ".init_array" || name == ".fini_array") {
+ if (!script->hasSectionsCommand)
+ sec->sortInitFini();
return;
}
// Sort input sections by the special rule for .ctors and .dtors.
- if (Name == ".ctors" || Name == ".dtors") {
- if (!Script->HasSectionsCommand)
- Sec->sortCtorsDtors();
+ if (name == ".ctors" || name == ".dtors") {
+ if (!script->hasSectionsCommand)
+ sec->sortCtorsDtors();
return;
}
// Never sort these.
- if (Name == ".init" || Name == ".fini")
+ 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.
+ // To reduce the risk of relocation overflow, .toc contents are sorted so that
+ // sections having smaller relocation offsets are at beginning of .toc
+ if (config->emachine == EM_PPC64 && name == ".toc") {
+ if (script->hasSectionsCommand)
+ return;
+ assert(sec->sectionCommands.size() == 1);
+ auto *isd = cast<InputSectionDescription>(sec->sectionCommands[0]);
+ llvm::stable_sort(isd->sections,
+ [](const InputSection *a, const InputSection *b) -> bool {
+ return a->file->ppc64SmallCodeModelTocRelocs &&
+ !b->file->ppc64SmallCodeModelTocRelocs;
+ });
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 (!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
// sorting for special input sections. This also handles --symbol-ordering-file.
template <class ELFT> void Writer<ELFT>::sortInputSections() {
// Build the order once since it is expensive.
- DenseMap<const InputSectionBase *, int> Order = buildSectionOrder();
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- sortSection(Sec, Order);
+ DenseMap<const InputSectionBase *, int> order = buildSectionOrder();
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ sortSection(sec, order);
}
template <class ELFT> void Writer<ELFT>::sortSections() {
- Script->adjustSectionsBeforeSorting();
+ script->adjustSectionsBeforeSorting();
// Don't sort if using -r. It is not necessary and we want to preserve the
// relative order for SHF_LINK_ORDER sections.
- if (Config->Relocatable)
+ if (config->relocatable)
return;
sortInputSections();
- for (BaseCommand *Base : Script->SectionCommands) {
- auto *OS = dyn_cast<OutputSection>(Base);
- if (!OS)
+ for (BaseCommand *base : script->sectionCommands) {
+ auto *os = dyn_cast<OutputSection>(base);
+ if (!os)
continue;
- OS->SortRank = getSectionRank(OS);
+ os->sortRank = getSectionRank(os);
- // We want to assign rude approximation values to OutSecOff fields
+ // We want to assign rude approximation values to outSecOff fields
// to know the relative order of the input sections. We use it for
// sorting SHF_LINK_ORDER sections. See resolveShfLinkOrder().
- uint64_t I = 0;
- for (InputSection *Sec : getInputSections(OS))
- Sec->OutSecOff = I++;
+ uint64_t i = 0;
+ for (InputSection *sec : getInputSections(os))
+ sec->outSecOff = i++;
}
- if (!Script->HasSectionsCommand) {
+ if (!script->hasSectionsCommand) {
// We know that all the OutputSections are contiguous in this case.
- auto IsSection = [](BaseCommand *Base) { return isa<OutputSection>(Base); };
+ auto isSection = [](BaseCommand *base) { return isa<OutputSection>(base); };
std::stable_sort(
- llvm::find_if(Script->SectionCommands, IsSection),
- llvm::find_if(llvm::reverse(Script->SectionCommands), IsSection).base(),
+ llvm::find_if(script->sectionCommands, isSection),
+ llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(),
compareSections);
return;
}
@@ -1342,211 +1481,127 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
// after another commands. For the details, look at shouldSkip
// function.
- auto I = Script->SectionCommands.begin();
- auto E = Script->SectionCommands.end();
- auto NonScriptI = std::find_if(I, E, [](BaseCommand *Base) {
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- return Sec->SectionIndex == UINT32_MAX;
+ auto i = script->sectionCommands.begin();
+ auto e = script->sectionCommands.end();
+ auto nonScriptI = std::find_if(i, e, [](BaseCommand *base) {
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ return sec->sectionIndex == UINT32_MAX;
return false;
});
// Sort the orphan sections.
- std::stable_sort(NonScriptI, E, compareSections);
+ std::stable_sort(nonScriptI, e, compareSections);
// As a horrible special case, skip the first . assignment if it is before any
// section. We do this because it is common to set a load address by starting
// the script with ". = 0xabcd" and the expectation is that every section is
// after that.
- auto FirstSectionOrDotAssignment =
- std::find_if(I, E, [](BaseCommand *Cmd) { return !shouldSkip(Cmd); });
- if (FirstSectionOrDotAssignment != E &&
- isa<SymbolAssignment>(**FirstSectionOrDotAssignment))
- ++FirstSectionOrDotAssignment;
- I = FirstSectionOrDotAssignment;
+ auto firstSectionOrDotAssignment =
+ std::find_if(i, e, [](BaseCommand *cmd) { return !shouldSkip(cmd); });
+ if (firstSectionOrDotAssignment != e &&
+ isa<SymbolAssignment>(**firstSectionOrDotAssignment))
+ ++firstSectionOrDotAssignment;
+ i = firstSectionOrDotAssignment;
- while (NonScriptI != E) {
- auto Pos = findOrphanPos<ELFT>(I, NonScriptI);
- OutputSection *Orphan = cast<OutputSection>(*NonScriptI);
+ while (nonScriptI != e) {
+ auto pos = findOrphanPos(i, nonScriptI);
+ OutputSection *orphan = cast<OutputSection>(*nonScriptI);
// As an optimization, find all sections with the same sort rank
// and insert them with one rotate.
- unsigned Rank = Orphan->SortRank;
- auto End = std::find_if(NonScriptI + 1, E, [=](BaseCommand *Cmd) {
- return cast<OutputSection>(Cmd)->SortRank != Rank;
+ unsigned rank = orphan->sortRank;
+ auto end = std::find_if(nonScriptI + 1, e, [=](BaseCommand *cmd) {
+ return cast<OutputSection>(cmd)->sortRank != rank;
});
- std::rotate(Pos, NonScriptI, End);
- NonScriptI = End;
+ std::rotate(pos, nonScriptI, end);
+ nonScriptI = end;
}
- Script->adjustSectionsAfterSorting();
+ script->adjustSectionsAfterSorting();
}
-static bool compareByFilePosition(InputSection *A, InputSection *B) {
- // Synthetic, i. e. a sentinel section, should go last.
- if (A->kind() == InputSectionBase::Synthetic ||
- B->kind() == InputSectionBase::Synthetic)
- return A->kind() != InputSectionBase::Synthetic;
-
- InputSection *LA = A->getLinkOrderDep();
- InputSection *LB = B->getLinkOrderDep();
- OutputSection *AOut = LA->getParent();
- OutputSection *BOut = LB->getParent();
-
- if (AOut != BOut)
- return AOut->SectionIndex < BOut->SectionIndex;
- return LA->OutSecOff < LB->OutSecOff;
-}
-
-// This function is used by the --merge-exidx-entries to detect duplicate
-// .ARM.exidx sections. It is Arm only.
-//
-// The .ARM.exidx section is of the form:
-// | PREL31 offset to function | Unwind instructions for function |
-// where the unwind instructions are either a small number of unwind
-// instructions inlined into the table entry, the special CANT_UNWIND value of
-// 0x1 or a PREL31 offset into a .ARM.extab Section that contains unwind
-// instructions.
-//
-// We return true if all the unwind instructions in the .ARM.exidx entries of
-// Cur can be merged into the last entry of Prev.
-static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) {
-
- // References to .ARM.Extab Sections have bit 31 clear and are not the
- // special EXIDX_CANTUNWIND bit-pattern.
- auto IsExtabRef = [](uint32_t Unwind) {
- return (Unwind & 0x80000000) == 0 && Unwind != 0x1;
- };
-
- struct ExidxEntry {
- ulittle32_t Fn;
- ulittle32_t Unwind;
- };
-
- // Get the last table Entry from the previous .ARM.exidx section.
- const ExidxEntry &PrevEntry = Prev->getDataAs<ExidxEntry>().back();
- if (IsExtabRef(PrevEntry.Unwind))
- return false;
-
- // We consider the unwind instructions of an .ARM.exidx table entry
- // a duplicate if the previous unwind instructions if:
- // - Both are the special EXIDX_CANTUNWIND.
- // - Both are the same inline unwind instructions.
- // We do not attempt to follow and check links into .ARM.extab tables as
- // consecutive identical entries are rare and the effort to check that they
- // are identical is high.
+static bool compareByFilePosition(InputSection *a, InputSection *b) {
+ InputSection *la = a->getLinkOrderDep();
+ InputSection *lb = b->getLinkOrderDep();
+ OutputSection *aOut = la->getParent();
+ OutputSection *bOut = lb->getParent();
- for (const ExidxEntry Entry : Cur->getDataAs<ExidxEntry>())
- if (IsExtabRef(Entry.Unwind) || Entry.Unwind != PrevEntry.Unwind)
- return false;
-
- // All table entries in this .ARM.exidx Section can be merged into the
- // previous Section.
- return true;
+ if (aOut != bOut)
+ return aOut->sectionIndex < bOut->sectionIndex;
+ return la->outSecOff < lb->outSecOff;
}
template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
- for (OutputSection *Sec : OutputSections) {
- if (!(Sec->Flags & SHF_LINK_ORDER))
+ for (OutputSection *sec : outputSections) {
+ if (!(sec->flags & SHF_LINK_ORDER))
continue;
// Link order may be distributed across several InputSectionDescriptions
// but sort must consider them all at once.
- std::vector<InputSection **> ScriptSections;
- std::vector<InputSection *> Sections;
- for (BaseCommand *Base : Sec->SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
- for (InputSection *&IS : ISD->Sections) {
- ScriptSections.push_back(&IS);
- Sections.push_back(IS);
+ std::vector<InputSection **> scriptSections;
+ std::vector<InputSection *> sections;
+ for (BaseCommand *base : sec->sectionCommands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (InputSection *&isec : isd->sections) {
+ scriptSections.push_back(&isec);
+ sections.push_back(isec);
}
}
}
- std::stable_sort(Sections.begin(), Sections.end(), compareByFilePosition);
-
- if (!Config->Relocatable && Config->EMachine == EM_ARM &&
- Sec->Type == SHT_ARM_EXIDX) {
-
- if (auto *Sentinel = dyn_cast<ARMExidxSentinelSection>(Sections.back())) {
- assert(Sections.size() >= 2 &&
- "We should create a sentinel section only if there are "
- "alive regular exidx sections.");
- // The last executable section is required to fill the sentinel.
- // Remember it here so that we don't have to find it again.
- Sentinel->Highest = Sections[Sections.size() - 2]->getLinkOrderDep();
- }
-
- // The EHABI for the Arm Architecture permits consecutive identical
- // table entries to be merged. We use a simple implementation that
- // removes a .ARM.exidx Input Section if it can be merged into the
- // previous one. This does not require any rewriting of InputSection
- // contents but misses opportunities for fine grained deduplication
- // where only a subset of the InputSection contents can be merged.
- if (Config->MergeArmExidx) {
- size_t Prev = 0;
- // The last one is a sentinel entry which should not be removed.
- for (size_t I = 1; I < Sections.size() - 1; ++I) {
- if (isDuplicateArmExidxSec(Sections[Prev], Sections[I]))
- Sections[I] = nullptr;
- else
- Prev = I;
- }
- }
- }
+ // The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated
+ // this processing inside the ARMExidxsyntheticsection::finalizeContents().
+ if (!config->relocatable && config->emachine == EM_ARM &&
+ sec->type == SHT_ARM_EXIDX)
+ continue;
- for (int I = 0, N = Sections.size(); I < N; ++I)
- *ScriptSections[I] = Sections[I];
+ llvm::stable_sort(sections, compareByFilePosition);
- // Remove the Sections we marked as duplicate earlier.
- for (BaseCommand *Base : Sec->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- llvm::erase_if(ISD->Sections, [](InputSection *IS) { return !IS; });
+ for (int i = 0, n = sections.size(); i < n; ++i)
+ *scriptSections[i] = sections[i];
}
}
-// For most RISC ISAs, we need to generate content that depends on the address
-// of InputSections. For example some architectures such as AArch64 use small
-// displacements for jump instructions that is the linker's responsibility for
-// creating range extension thunks for. As the generation of the content may
-// also alter InputSection addresses we must converge to a fixed point.
-template <class ELFT> void Writer<ELFT>::maybeAddThunks() {
- if (!Target->NeedsThunks && !Config->AndroidPackDynRelocs &&
- !Config->RelrPackDynRelocs)
- return;
-
- ThunkCreator TC;
- AArch64Err843419Patcher A64P;
+// 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
+// in Writer<ELFT>::finalizeSections().
+template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
+ ThunkCreator tc;
+ AArch64Err843419Patcher a64p;
+ // For some targets, like x86, this loop iterates only once.
for (;;) {
- bool Changed = false;
+ bool changed = false;
- Script->assignAddresses();
+ script->assignAddresses();
- if (Target->NeedsThunks)
- Changed |= TC.createThunks(OutputSections);
+ if (target->needsThunks)
+ changed |= tc.createThunks(outputSections);
- if (Config->FixCortexA53Errata843419) {
- if (Changed)
- Script->assignAddresses();
- Changed |= A64P.createFixes();
+ if (config->fixCortexA53Errata843419) {
+ if (changed)
+ script->assignAddresses();
+ changed |= a64p.createFixes();
}
- if (In.MipsGot)
- In.MipsGot->updateAllocSize();
-
- Changed |= In.RelaDyn->updateAllocSize();
+ if (in.mipsGot)
+ in.mipsGot->updateAllocSize();
- if (In.RelrDyn)
- Changed |= In.RelrDyn->updateAllocSize();
+ for (Partition &part : partitions) {
+ changed |= part.relaDyn->updateAllocSize();
+ if (part.relrDyn)
+ changed |= part.relrDyn->updateAllocSize();
+ }
- if (!Changed)
+ if (!changed)
return;
}
}
-static void finalizeSynthetic(SyntheticSection *Sec) {
- if (Sec && !Sec->empty() && Sec->getParent())
- Sec->finalizeContents();
+static void finalizeSynthetic(SyntheticSection *sec) {
+ if (sec && sec->isNeeded() && sec->getParent())
+ sec->finalizeContents();
}
// In order to allow users to manipulate linker-synthesized sections,
@@ -1565,128 +1620,190 @@ static void removeUnusedSyntheticSections() {
// All input synthetic sections that can be empty are placed after
// all regular ones. We iterate over them all and exit at first
// non-synthetic.
- for (InputSectionBase *S : llvm::reverse(InputSections)) {
- SyntheticSection *SS = dyn_cast<SyntheticSection>(S);
- if (!SS)
+ for (InputSectionBase *s : llvm::reverse(inputSections)) {
+ SyntheticSection *ss = dyn_cast<SyntheticSection>(s);
+ if (!ss)
return;
- OutputSection *OS = SS->getParent();
- if (!OS || !SS->empty())
+ OutputSection *os = ss->getParent();
+ 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.
- for (BaseCommand *B : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(B))
- llvm::erase_if(ISD->Sections,
- [=](InputSection *IS) { return IS == SS; });
+ for (BaseCommand *b : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ llvm::erase_if(isd->sections,
+ [=](InputSection *isec) { return isec == ss; });
}
}
// 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.
-static bool computeIsPreemptible(const Symbol &B) {
- assert(!B.isLocal());
+static bool computeIsPreemptible(const Symbol &b) {
+ assert(!b.isLocal());
// Only symbols that appear in dynsym can be preempted.
- if (!B.includeInDynsym())
+ if (!b.includeInDynsym())
return false;
// Only default visibility symbols can be preempted.
- if (B.Visibility != STV_DEFAULT)
+ if (b.visibility != STV_DEFAULT)
return false;
// At this point copy relocations have not been created yet, so any
// symbol that is not defined locally is preemptible.
- if (!B.isDefined())
+ if (!b.isDefined())
return true;
// If we have a dynamic list it specifies which local symbols are preemptible.
- if (Config->HasDynamicList)
+ if (config->hasDynamicList)
return false;
- if (!Config->Shared)
+ if (!config->shared)
return false;
// -Bsymbolic means that definitions are not preempted.
- if (Config->Bsymbolic || (Config->BsymbolicFunctions && B.isFunc()))
+ if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
return false;
return true;
}
// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::finalizeSections() {
- Out::PreinitArray = findSection(".preinit_array");
- Out::InitArray = findSection(".init_array");
- Out::FiniArray = findSection(".fini_array");
+ Out::preinitArray = findSection(".preinit_array");
+ Out::initArray = findSection(".init_array");
+ Out::finiArray = findSection(".fini_array");
// The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop
// symbols for sections, so that the runtime can get the start and end
// addresses of each section by section name. Add such symbols.
- if (!Config->Relocatable) {
+ if (!config->relocatable) {
addStartEndSymbols();
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- addStartStopSymbols(Sec);
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ addStartStopSymbols(sec);
}
// Add _DYNAMIC symbol. Unlike GNU gold, our _DYNAMIC symbol has no type.
// It should be okay as no one seems to care about the type.
// Even the author of gold doesn't remember why gold behaves that way.
// https://sourceware.org/ml/binutils/2002-03/msg00360.html
- if (In.Dynamic->Parent)
- Symtab->addDefined("_DYNAMIC", STV_HIDDEN, STT_NOTYPE, 0 /*Value*/,
- /*Size=*/0, STB_WEAK, In.Dynamic,
- /*File=*/nullptr);
+ if (mainPart->dynamic->parent)
+ symtab->addSymbol(Defined{/*file=*/nullptr, "_DYNAMIC", STB_WEAK,
+ STV_HIDDEN, STT_NOTYPE,
+ /*value=*/0, /*size=*/0, mainPart->dynamic});
// Define __rel[a]_iplt_{start,end} symbols if needed.
addRelIpltSymbols();
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800 if not defined.
- if (Config->EMachine == EM_RISCV)
- if (!dyn_cast_or_null<Defined>(Symtab->find("__global_pointer$")))
- addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800);
+ // This symbol should only be defined in an executable.
+ if (config->emachine == EM_RISCV && !config->shared)
+ ElfSym::riscvGlobalPointer =
+ addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800,
+ STV_DEFAULT, STB_GLOBAL);
+
+ if (config->emachine == EM_X86_64) {
+ // On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
+ // way that:
+ //
+ // 1) Without relaxation: it produces a dynamic TLSDESC relocation that
+ // computes 0.
+ // 2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address in
+ // the TLS block).
+ //
+ // 2) is special cased in @tpoff computation. To satisfy 1), we define it as
+ // an absolute symbol of zero. This is different from GNU linkers which
+ // define _TLS_MODULE_BASE_ relative to the first TLS section.
+ Symbol *s = symtab->find("_TLS_MODULE_BASE_");
+ if (s && s->isUndefined()) {
+ s->resolve(Defined{/*file=*/nullptr, s->getName(), STB_GLOBAL, STV_HIDDEN,
+ STT_TLS, /*value=*/0, 0,
+ /*section=*/nullptr});
+ ElfSym::tlsModuleBase = cast<Defined>(s);
+ }
+ }
// This responsible for splitting up .eh_frame section into
// pieces. The relocation scan uses those pieces, so this has to be
// earlier.
- finalizeSynthetic(In.EhFrame);
+ for (Partition &part : partitions)
+ finalizeSynthetic(part.ehFrame);
- for (Symbol *S : Symtab->getSymbols())
- if (!S->IsPreemptible)
- S->IsPreemptible = computeIsPreemptible(*S);
+ symtab->forEachSymbol([](Symbol *s) {
+ if (!s->isPreemptible)
+ s->isPreemptible = computeIsPreemptible(*s);
+ });
// Scan relocations. This must be done after every symbol is declared so that
// we can correctly decide if a dynamic relocation is needed.
- if (!Config->Relocatable)
+ if (!config->relocatable) {
forEachRelSec(scanRelocations<ELFT>);
+ reportUndefinedSymbols<ELFT>();
+ }
- if (In.Plt && !In.Plt->empty())
- In.Plt->addSymbols();
- if (In.Iplt && !In.Iplt->empty())
- In.Iplt->addSymbols();
+ addIRelativeRelocs();
+
+ if (in.plt && in.plt->isNeeded())
+ in.plt->addSymbols();
+ if (in.iplt && in.iplt->isNeeded())
+ in.iplt->addSymbols();
+
+ if (!config->allowShlibUndefined) {
+ // Error on undefined symbols in a shared object, if all of its DT_NEEDED
+ // entires are seen. These cases would otherwise lead to runtime errors
+ // reported by the dynamic linker.
+ //
+ // ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker to
+ // catch more cases. That is too much for us. Our approach resembles the one
+ // used in ld.gold, achieves a good balance to be useful but not too smart.
+ for (SharedFile *file : sharedFiles)
+ file->allNeededIsKnown =
+ llvm::all_of(file->dtNeeded, [&](StringRef needed) {
+ return symtab->soNames.count(needed);
+ });
+
+ symtab->forEachSymbol([](Symbol *sym) {
+ 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));
+ });
+ }
// Now that we have defined all possible global symbols including linker-
// synthesized ones. Visit all symbols to give the finishing touches.
- for (Symbol *Sym : Symtab->getSymbols()) {
- if (!includeInSymtab(*Sym))
- continue;
- if (In.SymTab)
- In.SymTab->addSymbol(Sym);
-
- if (Sym->includeInDynsym()) {
- In.DynSymTab->addSymbol(Sym);
- if (auto *File = dyn_cast_or_null<SharedFile<ELFT>>(Sym->File))
- if (File->IsNeeded && !Sym->isUndefined())
- InX<ELFT>::VerNeed->addSymbol(Sym);
+ symtab->forEachSymbol([](Symbol *sym) {
+ if (!includeInSymtab(*sym))
+ return;
+ if (in.symTab)
+ in.symTab->addSymbol(sym);
+
+ if (sym->includeInDynsym()) {
+ partitions[sym->partition - 1].dynSymTab->addSymbol(sym);
+ if (auto *file = dyn_cast_or_null<SharedFile>(sym->file))
+ if (file->isNeeded && !sym->isUndefined())
+ addVerneed(sym);
}
+ });
+
+ // We also need to scan the dynamic relocation tables of the other partitions
+ // and add any referenced symbols to the partition's dynsym.
+ for (Partition &part : MutableArrayRef<Partition>(partitions).slice(1)) {
+ DenseSet<Symbol *> syms;
+ for (const SymbolTableEntry &e : part.dynSymTab->getSymbols())
+ syms.insert(e.sym);
+ for (DynamicReloc &reloc : part.relaDyn->relocs)
+ if (reloc.sym && !reloc.useSymVA && syms.insert(reloc.sym).second)
+ part.dynSymTab->addSymbol(reloc.sym);
}
// Do not proceed if there was an undefined symbol.
if (errorCount())
return;
- if (In.MipsGot)
- In.MipsGot->build<ELFT>();
+ if (in.mipsGot)
+ in.mipsGot->build();
removeUnusedSyntheticSections();
@@ -1694,116 +1811,147 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Now that we have the final list, create a list of all the
// OutputSections for convenience.
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- OutputSections.push_back(Sec);
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ outputSections.push_back(sec);
// Prefer command line supplied address over other constraints.
- for (OutputSection *Sec : OutputSections) {
- auto I = Config->SectionStartMap.find(Sec->Name);
- if (I != Config->SectionStartMap.end())
- Sec->AddrExpr = [=] { return I->second; };
+ for (OutputSection *sec : outputSections) {
+ auto i = config->sectionStartMap.find(sec->name);
+ if (i != config->sectionStartMap.end())
+ sec->addrExpr = [=] { return i->second; };
}
// 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.
- Out::ElfHeader->SectionIndex = 1;
+ Out::elfHeader->sectionIndex = 1;
- for (size_t I = 0, E = OutputSections.size(); I != E; ++I) {
- OutputSection *Sec = OutputSections[I];
- Sec->SectionIndex = I + 1;
- Sec->ShName = In.ShStrTab->addString(Sec->Name);
+ for (size_t i = 0, e = outputSections.size(); i != e; ++i) {
+ OutputSection *sec = outputSections[i];
+ sec->sectionIndex = i + 1;
+ sec->shName = in.shStrTab->addString(sec->name);
}
// Binary and relocatable output does not have PHDRS.
// The headers have to be created before finalize as that can influence the
// image base and the dynamic section on mips includes the image base.
- if (!Config->Relocatable && !Config->OFormatBinary) {
- Phdrs = Script->hasPhdrsCommands() ? Script->createPhdrs() : createPhdrs();
- addPtArmExid(Phdrs);
- Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size();
+ if (!config->relocatable && !config->oFormatBinary) {
+ for (Partition &part : partitions) {
+ part.phdrs = script->hasPhdrsCommands() ? script->createPhdrs()
+ : createPhdrs(part);
+ if (config->emachine == EM_ARM) {
+ // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
+ addPhdrForSection(part, SHT_ARM_EXIDX, PT_ARM_EXIDX, PF_R);
+ }
+ if (config->emachine == EM_MIPS) {
+ // Add separate segments for MIPS-specific sections.
+ addPhdrForSection(part, SHT_MIPS_REGINFO, PT_MIPS_REGINFO, PF_R);
+ addPhdrForSection(part, SHT_MIPS_OPTIONS, PT_MIPS_OPTIONS, PF_R);
+ addPhdrForSection(part, SHT_MIPS_ABIFLAGS, PT_MIPS_ABIFLAGS, PF_R);
+ }
+ }
+ Out::programHeaders->size = sizeof(Elf_Phdr) * mainPart->phdrs.size();
// Find the TLS segment. This happens before the section layout loop so that
- // Android relocation packing can look up TLS symbol addresses.
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_TLS)
- Out::TlsPhdr = P;
+ // Android relocation packing can look up TLS symbol addresses. We only need
+ // to care about the main partition here because all TLS symbols were moved
+ // to the main partition (see MarkLive.cpp).
+ for (PhdrEntry *p : mainPart->phdrs)
+ if (p->p_type == PT_TLS)
+ Out::tlsPhdr = p;
}
// Some symbols are defined in term of program headers. Now that we
// have the headers, we can find out which sections they point to.
setReservedSymbolSections();
+ finalizeSynthetic(in.bss);
+ finalizeSynthetic(in.bssRelRo);
+ finalizeSynthetic(in.symTabShndx);
+ finalizeSynthetic(in.shStrTab);
+ finalizeSynthetic(in.strTab);
+ finalizeSynthetic(in.got);
+ finalizeSynthetic(in.mipsGot);
+ finalizeSynthetic(in.igotPlt);
+ finalizeSynthetic(in.gotPlt);
+ finalizeSynthetic(in.relaIplt);
+ finalizeSynthetic(in.relaPlt);
+ finalizeSynthetic(in.plt);
+ finalizeSynthetic(in.iplt);
+ finalizeSynthetic(in.ppc32Got2);
+ finalizeSynthetic(in.riscvSdata);
+ finalizeSynthetic(in.partIndex);
+
// Dynamic section must be the last one in this list and dynamic
- // symbol table section (DynSymTab) must be the first one.
- finalizeSynthetic(In.DynSymTab);
- finalizeSynthetic(In.Bss);
- finalizeSynthetic(In.BssRelRo);
- finalizeSynthetic(In.GnuHashTab);
- finalizeSynthetic(In.HashTab);
- finalizeSynthetic(In.SymTabShndx);
- finalizeSynthetic(In.ShStrTab);
- finalizeSynthetic(In.StrTab);
- finalizeSynthetic(In.VerDef);
- finalizeSynthetic(In.DynStrTab);
- finalizeSynthetic(In.Got);
- finalizeSynthetic(In.MipsGot);
- finalizeSynthetic(In.IgotPlt);
- finalizeSynthetic(In.GotPlt);
- finalizeSynthetic(In.RelaDyn);
- finalizeSynthetic(In.RelrDyn);
- finalizeSynthetic(In.RelaIplt);
- finalizeSynthetic(In.RelaPlt);
- finalizeSynthetic(In.Plt);
- finalizeSynthetic(In.Iplt);
- finalizeSynthetic(In.EhFrameHdr);
- finalizeSynthetic(InX<ELFT>::VerSym);
- finalizeSynthetic(InX<ELFT>::VerNeed);
- finalizeSynthetic(In.Dynamic);
-
- if (!Script->HasSectionsCommand && !Config->Relocatable)
+ // 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);
+ finalizeSynthetic(part.verDef);
+ finalizeSynthetic(part.relaDyn);
+ finalizeSynthetic(part.relrDyn);
+ finalizeSynthetic(part.ehFrameHdr);
+ finalizeSynthetic(part.verSym);
+ finalizeSynthetic(part.verNeed);
+ finalizeSynthetic(part.dynamic);
+ }
+
+ if (!script->hasSectionsCommand && !config->relocatable)
fixSectionAlignments();
- // After link order processing .ARM.exidx sections can be deduplicated, which
- // needs to be resolved before any other address dependent operation.
+ // SHFLinkOrder processing must be processed after relative section placements are
+ // known but before addresses are allocated.
resolveShfLinkOrder();
- // Jump instructions in many ISAs have small displacements, and therefore they
- // cannot jump to arbitrary addresses in memory. For example, RISC-V JAL
- // instruction can target only +-1 MiB from PC. It is a linker's
- // responsibility to create and insert small pieces of code between sections
- // to extend the ranges if jump targets are out of range. Such code pieces are
- // called "thunks".
+ // This is used to:
+ // 1) Create "thunks":
+ // Jump instructions in many ISAs have small displacements, and therefore
+ // they cannot jump to arbitrary addresses in memory. For example, RISC-V
+ // JAL instruction can target only +-1 MiB from PC. It is a linker's
+ // responsibility to create and insert small pieces of code between
+ // sections to extend the ranges if jump targets are out of range. Such
+ // code pieces are called "thunks".
//
- // We add thunks at this stage. We couldn't do this before this point because
- // this is the earliest point where we know sizes of sections and their
- // layouts (that are needed to determine if jump targets are in range).
- maybeAddThunks();
+ // We add thunks at this stage. We couldn't do this before this point
+ // because this is the earliest point where we know sizes of sections and
+ // their layouts (that are needed to determine if jump targets are in
+ // range).
+ //
+ // 2) Update the sections. We need to generate content that depends on the
+ // address of InputSections. For example, MIPS GOT section content or
+ // android packed relocations sections content.
+ //
+ // 3) Assign the final values for the linker script symbols. Linker scripts
+ // sometimes using forward symbol declarations. We want to set the correct
+ // values. They also might change after adding the thunks.
+ finalizeAddressDependentContent();
- // maybeAddThunks may have added local symbols to the static symbol table.
- finalizeSynthetic(In.SymTab);
- finalizeSynthetic(In.PPC64LongBranchTarget);
+ // finalizeAddressDependentContent may have added local symbols to the static symbol table.
+ finalizeSynthetic(in.symTab);
+ finalizeSynthetic(in.ppc64LongBranchTarget);
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
// of finalizing other sections.
- for (OutputSection *Sec : OutputSections)
- Sec->finalize<ELFT>();
+ for (OutputSection *sec : outputSections)
+ sec->finalize();
}
// Ensure data sections are not mixed with executable sections when
// -execute-only is used. -execute-only is a feature to make pages executable
// but not readable, and the feature is currently supported only on AArch64.
template <class ELFT> void Writer<ELFT>::checkExecuteOnly() {
- if (!Config->ExecuteOnly)
+ if (!config->executeOnly)
return;
- for (OutputSection *OS : OutputSections)
- if (OS->Flags & SHF_EXECINSTR)
- for (InputSection *IS : getInputSections(OS))
- if (!(IS->Flags & SHF_EXECINSTR))
- error("cannot place " + toString(IS) + " into " + toString(OS->Name) +
+ for (OutputSection *os : outputSections)
+ if (os->flags & SHF_EXECINSTR)
+ for (InputSection *isec : getInputSections(os))
+ if (!(isec->flags & SHF_EXECINSTR))
+ error("cannot place " + toString(isec) + " into " + toString(os->name) +
": -execute-only does not support intermingling data and code");
}
@@ -1828,24 +1976,24 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
// case, use the image base address as a last resort.
OutputSection *Default = findSection(".text");
if (!Default)
- Default = Out::ElfHeader;
+ Default = Out::elfHeader;
- auto Define = [=](StringRef Start, StringRef End, OutputSection *OS) {
- if (OS) {
- addOptionalRegular(Start, OS, 0);
- addOptionalRegular(End, OS, -1);
+ auto define = [=](StringRef start, StringRef end, OutputSection *os) {
+ if (os) {
+ addOptionalRegular(start, os, 0);
+ addOptionalRegular(end, os, -1);
} else {
- addOptionalRegular(Start, Default, 0);
- addOptionalRegular(End, Default, 0);
+ addOptionalRegular(start, Default, 0);
+ addOptionalRegular(end, Default, 0);
}
};
- Define("__preinit_array_start", "__preinit_array_end", Out::PreinitArray);
- Define("__init_array_start", "__init_array_end", Out::InitArray);
- Define("__fini_array_start", "__fini_array_end", Out::FiniArray);
+ define("__preinit_array_start", "__preinit_array_end", Out::preinitArray);
+ define("__init_array_start", "__init_array_end", Out::initArray);
+ define("__fini_array_start", "__fini_array_end", Out::finiArray);
- if (OutputSection *Sec = findSection(".ARM.exidx"))
- Define("__exidx_start", "__exidx_end", Sec);
+ if (OutputSection *sec = findSection(".ARM.exidx"))
+ define("__exidx_start", "__exidx_end", sec);
}
// If a section name is valid as a C identifier (which is rare because of
@@ -1854,22 +2002,22 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
// respectively. This is not requested by the ELF standard, but GNU ld and
// gold provide the feature, and used by many programs.
template <class ELFT>
-void Writer<ELFT>::addStartStopSymbols(OutputSection *Sec) {
- StringRef S = Sec->Name;
- if (!isValidCIdentifier(S))
+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, STV_PROTECTED);
+ addOptionalRegular(saver.save("__stop_" + s), sec, -1, STV_PROTECTED);
}
-static bool needsPtLoad(OutputSection *Sec) {
- if (!(Sec->Flags & SHF_ALLOC) || Sec->Noload)
+static bool needsPtLoad(OutputSection *sec) {
+ if (!(sec->flags & SHF_ALLOC) || sec->noload)
return false;
// Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is
// responsible for allocating space for them, not the PT_LOAD that
// contains the TLS initialization image.
- if ((Sec->Flags & SHF_TLS) && Sec->Type == SHT_NOBITS)
+ if ((sec->flags & SHF_TLS) && sec->type == SHT_NOBITS)
return false;
return true;
}
@@ -1878,46 +2026,93 @@ static bool needsPtLoad(OutputSection *Sec) {
// linker scripts are designed for creating two PT_LOADs only, one RX and one
// RW. This means that there is no alignment in the RO to RX transition and we
// cannot create a PT_LOAD there.
-static uint64_t computeFlags(uint64_t Flags) {
- if (Config->Omagic)
+static uint64_t computeFlags(uint64_t flags) {
+ if (config->omagic)
return PF_R | PF_W | PF_X;
- if (Config->ExecuteOnly && (Flags & PF_X))
- return Flags & ~PF_R;
- if (Config->SingleRoRx && !(Flags & PF_W))
- return Flags | PF_X;
- return Flags;
+ if (config->executeOnly && (flags & PF_X))
+ return flags & ~PF_R;
+ if (config->singleRoRx && !(flags & PF_W))
+ return flags | PF_X;
+ return flags;
}
// Decide which program headers to create and which sections to include in each
// one.
-template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() {
- std::vector<PhdrEntry *> Ret;
- auto AddHdr = [&](unsigned Type, unsigned Flags) -> PhdrEntry * {
- Ret.push_back(make<PhdrEntry>(Type, Flags));
- return Ret.back();
+template <class ELFT>
+std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
+ std::vector<PhdrEntry *> ret;
+ auto addHdr = [&](unsigned type, unsigned flags) -> PhdrEntry * {
+ ret.push_back(make<PhdrEntry>(type, flags));
+ return ret.back();
};
+ unsigned partNo = part.getNumber();
+ bool isMain = partNo == 1;
+
// The first phdr entry is PT_PHDR which describes the program header itself.
- AddHdr(PT_PHDR, PF_R)->add(Out::ProgramHeaders);
+ if (isMain)
+ addHdr(PT_PHDR, PF_R)->add(Out::programHeaders);
+ else
+ addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent());
// PT_INTERP must be the second entry if exists.
- if (OutputSection *Cmd = findSection(".interp"))
- AddHdr(PT_INTERP, Cmd->getPhdrFlags())->add(Cmd);
+ if (OutputSection *cmd = findSection(".interp", partNo))
+ addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
// Add the first PT_LOAD segment for regular output sections.
- uint64_t Flags = computeFlags(PF_R);
- PhdrEntry *Load = AddHdr(PT_LOAD, Flags);
+ uint64_t flags = computeFlags(PF_R);
+ PhdrEntry *load = nullptr;
// Add the headers. We will remove them if they don't fit.
- Load->add(Out::ElfHeader);
- Load->add(Out::ProgramHeaders);
+ // In the other partitions the headers are ordinary sections, so they don't
+ // need to be added here.
+ if (isMain) {
+ load = addHdr(PT_LOAD, flags);
+ load->add(Out::elfHeader);
+ load->add(Out::programHeaders);
+ }
+
+ // PT_GNU_RELRO includes all sections that should be marked as
+ // read-only by dynamic linker after proccessing relocations.
+ // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give
+ // an error message if more than one PT_GNU_RELRO PHDR is required.
+ PhdrEntry *relRo = make<PhdrEntry>(PT_GNU_RELRO, PF_R);
+ bool inRelroPhdr = false;
+ OutputSection *relroEnd = nullptr;
+ for (OutputSection *sec : outputSections) {
+ if (sec->partition != partNo || !needsPtLoad(sec))
+ continue;
+ if (isRelroSection(sec)) {
+ inRelroPhdr = true;
+ if (!relroEnd)
+ relRo->add(sec);
+ else
+ error("section: " + sec->name + " is not contiguous with other relro" +
+ " sections");
+ } else if (inRelroPhdr) {
+ inRelroPhdr = false;
+ relroEnd = sec;
+ }
+ }
- for (OutputSection *Sec : OutputSections) {
- if (!(Sec->Flags & SHF_ALLOC))
+ for (OutputSection *sec : outputSections) {
+ if (!(sec->flags & SHF_ALLOC))
break;
- if (!needsPtLoad(Sec))
+ if (!needsPtLoad(sec))
continue;
+ // Normally, sections in partitions other than the current partition are
+ // ignored. But partition number 255 is a special case: it contains the
+ // partition end marker (.part.end). It needs to be added to the main
+ // partition so that a segment is created for it in the main partition,
+ // which will cause the dynamic loader to reserve space for the other
+ // partitions.
+ if (sec->partition != partNo) {
+ if (isMain && sec->partition == 255)
+ addHdr(PT_LOAD, computeFlags(sec->getPhdrFlags()))->add(sec);
+ continue;
+ }
+
// Segments are contiguous memory regions that has the same attributes
// (e.g. executable or writable). There is one phdr for each segment.
// Therefore, we need to create a new phdr when the next section has
@@ -1925,222 +2120,187 @@ template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() {
// region using AT or AT> linker script command, respectively. At the same
// 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 (((Sec->LMAExpr ||
- (Sec->LMARegion && (Sec->LMARegion != Load->FirstSec->LMARegion))) &&
- Load->LastSec != Out::ProgramHeaders) ||
- Sec->MemRegion != Load->FirstSec->MemRegion || Flags != NewFlags) {
-
- Load = AddHdr(PT_LOAD, NewFlags);
- Flags = NewFlags;
+ 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) {
+ load = addHdr(PT_LOAD, newFlags);
+ flags = newFlags;
}
- Load->add(Sec);
+ load->add(sec);
}
// Add a TLS segment if any.
- PhdrEntry *TlsHdr = make<PhdrEntry>(PT_TLS, PF_R);
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_TLS)
- TlsHdr->add(Sec);
- if (TlsHdr->FirstSec)
- Ret.push_back(TlsHdr);
+ PhdrEntry *tlsHdr = make<PhdrEntry>(PT_TLS, PF_R);
+ for (OutputSection *sec : outputSections)
+ if (sec->partition == partNo && sec->flags & SHF_TLS)
+ tlsHdr->add(sec);
+ if (tlsHdr->firstSec)
+ ret.push_back(tlsHdr);
// Add an entry for .dynamic.
- if (OutputSection *Sec = In.Dynamic->getParent())
- AddHdr(PT_DYNAMIC, Sec->getPhdrFlags())->add(Sec);
+ if (OutputSection *sec = part.dynamic->getParent())
+ addHdr(PT_DYNAMIC, sec->getPhdrFlags())->add(sec);
- // PT_GNU_RELRO includes all sections that should be marked as
- // read-only by dynamic linker after proccessing relocations.
- // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give
- // an error message if more than one PT_GNU_RELRO PHDR is required.
- PhdrEntry *RelRo = make<PhdrEntry>(PT_GNU_RELRO, PF_R);
- bool InRelroPhdr = false;
- bool IsRelroFinished = false;
- for (OutputSection *Sec : OutputSections) {
- if (!needsPtLoad(Sec))
- continue;
- if (isRelroSection(Sec)) {
- InRelroPhdr = true;
- if (!IsRelroFinished)
- RelRo->add(Sec);
- else
- error("section: " + Sec->Name + " is not contiguous with other relro" +
- " sections");
- } else if (InRelroPhdr) {
- InRelroPhdr = false;
- IsRelroFinished = true;
- }
- }
- if (RelRo->FirstSec)
- Ret.push_back(RelRo);
+ if (relRo->firstSec)
+ ret.push_back(relRo);
// PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr.
- if (!In.EhFrame->empty() && In.EhFrameHdr && In.EhFrame->getParent() &&
- In.EhFrameHdr->getParent())
- AddHdr(PT_GNU_EH_FRAME, In.EhFrameHdr->getParent()->getPhdrFlags())
- ->add(In.EhFrameHdr->getParent());
+ if (part.ehFrame->isNeeded() && part.ehFrameHdr &&
+ part.ehFrame->getParent() && part.ehFrameHdr->getParent())
+ addHdr(PT_GNU_EH_FRAME, part.ehFrameHdr->getParent()->getPhdrFlags())
+ ->add(part.ehFrameHdr->getParent());
// PT_OPENBSD_RANDOMIZE is an OpenBSD-specific feature. That makes
// the dynamic linker fill the segment with random data.
- if (OutputSection *Cmd = findSection(".openbsd.randomdata"))
- AddHdr(PT_OPENBSD_RANDOMIZE, Cmd->getPhdrFlags())->add(Cmd);
+ if (OutputSection *cmd = findSection(".openbsd.randomdata", partNo))
+ addHdr(PT_OPENBSD_RANDOMIZE, cmd->getPhdrFlags())->add(cmd);
// PT_GNU_STACK is a special section to tell the loader to make the
// pages for the stack non-executable. If you really want an executable
// stack, you can pass -z execstack, but that's not recommended for
// security reasons.
- unsigned Perm = PF_R | PF_W;
- if (Config->ZExecstack)
- Perm |= PF_X;
- AddHdr(PT_GNU_STACK, Perm)->p_memsz = Config->ZStackSize;
+ unsigned perm = PF_R | PF_W;
+ if (config->zExecstack)
+ perm |= PF_X;
+ addHdr(PT_GNU_STACK, perm)->p_memsz = config->zStackSize;
// PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable
// is expected to perform W^X violations, such as calling mprotect(2) or
// mmap(2) with PROT_WRITE | PROT_EXEC, which is prohibited by default on
// OpenBSD.
- if (Config->ZWxneeded)
- AddHdr(PT_OPENBSD_WXNEEDED, PF_X);
-
- // Create one PT_NOTE per a group of contiguous .note sections.
- PhdrEntry *Note = nullptr;
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Type == SHT_NOTE && (Sec->Flags & SHF_ALLOC)) {
- if (!Note || Sec->LMAExpr)
- Note = AddHdr(PT_NOTE, PF_R);
- Note->add(Sec);
+ if (config->zWxneeded)
+ addHdr(PT_OPENBSD_WXNEEDED, PF_X);
+
+ // Create one PT_NOTE per a group of contiguous SHT_NOTE sections with the
+ // same alignment.
+ PhdrEntry *note = nullptr;
+ for (OutputSection *sec : outputSections) {
+ if (sec->partition != partNo)
+ continue;
+ if (sec->type == SHT_NOTE && (sec->flags & SHF_ALLOC)) {
+ if (!note || sec->lmaExpr || note->lastSec->alignment != sec->alignment)
+ note = addHdr(PT_NOTE, PF_R);
+ note->add(sec);
} else {
- Note = nullptr;
+ note = nullptr;
}
}
- return Ret;
+ return ret;
}
template <class ELFT>
-void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry *> &Phdrs) {
- if (Config->EMachine != EM_ARM)
- return;
- auto I = llvm::find_if(OutputSections, [](OutputSection *Cmd) {
- return Cmd->Type == SHT_ARM_EXIDX;
+void Writer<ELFT>::addPhdrForSection(Partition &part, unsigned shType,
+ unsigned pType, unsigned pFlags) {
+ unsigned partNo = part.getNumber();
+ auto i = llvm::find_if(outputSections, [=](OutputSection *cmd) {
+ return cmd->partition == partNo && cmd->type == shType;
});
- if (I == OutputSections.end())
+ if (i == outputSections.end())
return;
- // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
- PhdrEntry *ARMExidx = make<PhdrEntry>(PT_ARM_EXIDX, PF_R);
- ARMExidx->add(*I);
- Phdrs.push_back(ARMExidx);
+ PhdrEntry *entry = make<PhdrEntry>(pType, pFlags);
+ entry->add(*i);
+ part.phdrs.push_back(entry);
}
// The first section of each PT_LOAD, the first section in PT_GNU_RELRO and the
// first section after PT_GNU_RELRO have to be page aligned so that the dynamic
// linker can set the permissions.
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
- auto PageAlign = [](OutputSection *Cmd) {
- if (Cmd && !Cmd->AddrExpr)
- Cmd->AddrExpr = [=] {
- return alignTo(Script->getDot(), Config->MaxPageSize);
+ auto pageAlign = [](OutputSection *cmd) {
+ if (cmd && !cmd->addrExpr)
+ cmd->addrExpr = [=] {
+ return alignTo(script->getDot(), config->maxPageSize);
};
};
- for (const PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD && P->FirstSec)
- PageAlign(P->FirstSec);
-
- for (const PhdrEntry *P : Phdrs) {
- if (P->p_type != PT_GNU_RELRO)
- continue;
-
- if (P->FirstSec)
- PageAlign(P->FirstSec);
-
- // Find the first section after PT_GNU_RELRO. If it is in a PT_LOAD we
- // have to align it to a page.
- auto End = OutputSections.end();
- auto I = std::find(OutputSections.begin(), End, P->LastSec);
- if (I == End || (I + 1) == End)
- continue;
-
- OutputSection *Cmd = (*(I + 1));
- if (needsPtLoad(Cmd))
- PageAlign(Cmd);
+ for (Partition &part : partitions) {
+ for (const PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD && p->firstSec)
+ pageAlign(p->firstSec);
}
}
// Compute an in-file position for a given section. The file offset must be the
// same with its virtual address modulo the page size, so that the loader can
// load executables without any address adjustment.
-static uint64_t computeFileOffset(OutputSection *OS, uint64_t Off) {
+static uint64_t computeFileOffset(OutputSection *os, uint64_t off) {
// File offsets are not significant for .bss sections. By convention, we keep
// section offsets monotonically increasing rather than setting to zero.
- if (OS->Type == SHT_NOBITS)
- return Off;
+ if (os->type == SHT_NOBITS)
+ return off;
// If the section is not in a PT_LOAD, we just have to align it.
- if (!OS->PtLoad)
- return alignTo(Off, OS->Alignment);
+ if (!os->ptLoad)
+ return alignTo(off, os->alignment);
// The first section in a PT_LOAD has to have congruent offset and address
// module the page size.
- OutputSection *First = OS->PtLoad->FirstSec;
- if (OS == First) {
- uint64_t Alignment = std::max<uint64_t>(OS->Alignment, Config->MaxPageSize);
- return alignTo(Off, Alignment, OS->Addr);
+ OutputSection *first = os->ptLoad->firstSec;
+ if (os == first) {
+ uint64_t alignment = std::max<uint64_t>(os->alignment, config->maxPageSize);
+ return alignTo(off, alignment, os->addr);
}
// If two sections share the same PT_LOAD the file offset is calculated
// using this formula: Off2 = Off1 + (VA2 - VA1).
- return First->Offset + OS->Addr - First->Addr;
+ return first->offset + os->addr - first->addr;
}
// Set an in-file position to a given section and returns the end position of
// the section.
-static uint64_t setFileOffset(OutputSection *OS, uint64_t Off) {
- Off = computeFileOffset(OS, Off);
- OS->Offset = Off;
+static uint64_t setFileOffset(OutputSection *os, uint64_t off) {
+ off = computeFileOffset(os, off);
+ os->offset = off;
- if (OS->Type == SHT_NOBITS)
- return Off;
- return Off + OS->Size;
+ if (os->type == SHT_NOBITS)
+ return off;
+ return off + os->size;
}
template <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() {
- uint64_t Off = 0;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_ALLOC)
- Off = setFileOffset(Sec, Off);
- FileSize = alignTo(Off, Config->Wordsize);
+ uint64_t off = 0;
+ for (OutputSection *sec : outputSections)
+ if (sec->flags & SHF_ALLOC)
+ off = setFileOffset(sec, off);
+ fileSize = alignTo(off, config->wordsize);
}
-static std::string rangeToString(uint64_t Addr, uint64_t Len) {
- return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]";
+static std::string rangeToString(uint64_t addr, uint64_t len) {
+ return "[0x" + utohexstr(addr) + ", 0x" + utohexstr(addr + len - 1) + "]";
}
// Assign file offsets to output sections.
template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
- uint64_t Off = 0;
- Off = setFileOffset(Out::ElfHeader, Off);
- Off = setFileOffset(Out::ProgramHeaders, Off);
-
- PhdrEntry *LastRX = nullptr;
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD && (P->p_flags & PF_X))
- LastRX = P;
-
- for (OutputSection *Sec : OutputSections) {
- Off = setFileOffset(Sec, Off);
- if (Script->HasSectionsCommand)
+ uint64_t off = 0;
+ off = setFileOffset(Out::elfHeader, off);
+ off = setFileOffset(Out::programHeaders, off);
+
+ PhdrEntry *lastRX = nullptr;
+ for (Partition &part : partitions)
+ for (PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD && (p->p_flags & PF_X))
+ lastRX = p;
+
+ for (OutputSection *sec : outputSections) {
+ off = setFileOffset(sec, off);
+ if (script->hasSectionsCommand)
continue;
// If this is a last section of the last executable segment and that
// segment is the last loadable segment, align the offset of the
// following section to avoid loading non-segments parts of the file.
- if (LastRX && LastRX->LastSec == Sec)
- Off = alignTo(Off, Target->PageSize);
+ if (lastRX && lastRX->lastSec == sec)
+ off = alignTo(off, config->commonPageSize);
}
- SectionHeaderOff = alignTo(Off, Config->Wordsize);
- FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr);
+ sectionHeaderOff = alignTo(off, config->wordsize);
+ fileSize = sectionHeaderOff + (outputSections.size() + 1) * sizeof(Elf_Shdr);
// Our logic assumes that sections have rising VA within the same segment.
// With use of linker scripts it is possible to violate this rule and get file
@@ -2151,62 +2311,49 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
// backwards, so we have to allow doing that to support linking them. We
// perform non-critical checks for overlaps in checkSectionOverlap(), but here
// we want to prevent file size overflows because it would crash the linker.
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Type == SHT_NOBITS)
+ for (OutputSection *sec : outputSections) {
+ if (sec->type == SHT_NOBITS)
continue;
- if ((Sec->Offset > FileSize) || (Sec->Offset + Sec->Size > FileSize))
- error("unable to place section " + Sec->Name + " at file offset " +
- rangeToString(Sec->Offset, Sec->Size) +
+ if ((sec->offset > fileSize) || (sec->offset + sec->size > fileSize))
+ error("unable to place section " + sec->name + " at file offset " +
+ rangeToString(sec->offset, sec->size) +
"; check your linker script for overflows");
}
}
// Finalize the program headers. We call this function after we assign
// file offsets and VAs to all sections.
-template <class ELFT> void Writer<ELFT>::setPhdrs() {
- for (PhdrEntry *P : Phdrs) {
- OutputSection *First = P->FirstSec;
- OutputSection *Last = P->LastSec;
-
- if (First) {
- P->p_filesz = Last->Offset - First->Offset;
- if (Last->Type != SHT_NOBITS)
- P->p_filesz += Last->Size;
-
- P->p_memsz = Last->Addr + Last->Size - First->Addr;
- P->p_offset = First->Offset;
- P->p_vaddr = First->Addr;
-
- if (!P->HasLMA)
- P->p_paddr = First->getLMA();
+template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
+ for (PhdrEntry *p : part.phdrs) {
+ OutputSection *first = p->firstSec;
+ OutputSection *last = p->lastSec;
+
+ if (first) {
+ p->p_filesz = last->offset - first->offset;
+ if (last->type != SHT_NOBITS)
+ p->p_filesz += last->size;
+
+ p->p_memsz = last->addr + last->size - first->addr;
+ p->p_offset = first->offset;
+ p->p_vaddr = first->addr;
+
+ // File offsets in partitions other than the main partition are relative
+ // to the offset of the ELF headers. Perform that adjustment now.
+ if (part.elfHeader)
+ p->p_offset -= part.elfHeader->getParent()->offset;
+
+ if (!p->hasLMA)
+ p->p_paddr = first->getLMA();
}
- if (P->p_type == PT_LOAD) {
- P->p_align = std::max<uint64_t>(P->p_align, Config->MaxPageSize);
- } else if (P->p_type == PT_GNU_RELRO) {
- P->p_align = 1;
+ if (p->p_type == PT_LOAD) {
+ p->p_align = std::max<uint64_t>(p->p_align, config->maxPageSize);
+ } else if (p->p_type == PT_GNU_RELRO) {
+ p->p_align = 1;
// The glibc dynamic loader rounds the size down, so we need to round up
// to protect the last page. This is a no-op on FreeBSD which always
// rounds up.
- P->p_memsz = alignTo(P->p_memsz, Target->PageSize);
- }
-
- if (P->p_type == PT_TLS && P->p_memsz) {
- if (!Config->Shared &&
- (Config->EMachine == EM_ARM || Config->EMachine == EM_AARCH64)) {
- // On ARM/AArch64, reserve extra space (8 words) between the thread
- // pointer and an executable's TLS segment by overaligning the segment.
- // This reservation is needed for backwards compatibility with Android's
- // TCB, which allocates several slots after the thread pointer (e.g.
- // TLS_SLOT_STACK_GUARD==5). For simplicity, this overalignment is also
- // done on other operating systems.
- P->p_align = std::max<uint64_t>(P->p_align, Config->Wordsize * 8);
- }
-
- // The TLS pointer goes after PT_TLS for variant 2 targets. At least glibc
- // will align it, so round up the size to make sure the offsets are
- // correct.
- P->p_memsz = alignTo(P->p_memsz, P->p_align);
+ p->p_memsz = alignTo(p->p_memsz, config->commonPageSize);
}
}
}
@@ -2214,37 +2361,37 @@ template <class ELFT> void Writer<ELFT>::setPhdrs() {
// A helper struct for checkSectionOverlap.
namespace {
struct SectionOffset {
- OutputSection *Sec;
- uint64_t Offset;
+ OutputSection *sec;
+ uint64_t offset;
};
} // namespace
// Check whether sections overlap for a specific address range (file offsets,
// load and virtual adresses).
-static void checkOverlap(StringRef Name, std::vector<SectionOffset> &Sections,
- bool IsVirtualAddr) {
- llvm::sort(Sections, [=](const SectionOffset &A, const SectionOffset &B) {
- return A.Offset < B.Offset;
+static void checkOverlap(StringRef name, std::vector<SectionOffset> &sections,
+ bool isVirtualAddr) {
+ llvm::sort(sections, [=](const SectionOffset &a, const SectionOffset &b) {
+ return a.offset < b.offset;
});
// Finding overlap is easy given a vector is sorted by start position.
// If an element starts before the end of the previous element, they overlap.
- for (size_t I = 1, End = Sections.size(); I < End; ++I) {
- SectionOffset A = Sections[I - 1];
- SectionOffset B = Sections[I];
- if (B.Offset >= A.Offset + A.Sec->Size)
+ for (size_t i = 1, end = sections.size(); i < end; ++i) {
+ SectionOffset a = sections[i - 1];
+ SectionOffset b = sections[i];
+ if (b.offset >= a.offset + a.sec->size)
continue;
// If both sections are in OVERLAY we allow the overlapping of virtual
// addresses, because it is what OVERLAY was designed for.
- if (IsVirtualAddr && A.Sec->InOverlay && B.Sec->InOverlay)
+ if (isVirtualAddr && a.sec->inOverlay && b.sec->inOverlay)
continue;
- errorOrWarn("section " + A.Sec->Name + " " + Name +
- " range overlaps with " + B.Sec->Name + "\n>>> " + A.Sec->Name +
- " range is " + rangeToString(A.Offset, A.Sec->Size) + "\n>>> " +
- B.Sec->Name + " range is " +
- rangeToString(B.Offset, B.Sec->Size));
+ errorOrWarn("section " + a.sec->name + " " + name +
+ " range overlaps with " + b.sec->name + "\n>>> " + a.sec->name +
+ " range is " + rangeToString(a.offset, a.sec->size) + "\n>>> " +
+ b.sec->name + " range is " +
+ rangeToString(b.offset, b.sec->size));
}
}
@@ -2255,11 +2402,11 @@ static void checkOverlap(StringRef Name, std::vector<SectionOffset> &Sections,
// ranges and the virtual address ranges don't overlap
template <class ELFT> void Writer<ELFT>::checkSections() {
// First, check that section's VAs fit in available address space for target.
- for (OutputSection *OS : OutputSections)
- if ((OS->Addr + OS->Size < OS->Addr) ||
- (!ELFT::Is64Bits && OS->Addr + OS->Size > UINT32_MAX))
- errorOrWarn("section " + OS->Name + " at 0x" + utohexstr(OS->Addr) +
- " of size 0x" + utohexstr(OS->Size) +
+ for (OutputSection *os : outputSections)
+ if ((os->addr + os->size < os->addr) ||
+ (!ELFT::Is64Bits && os->addr + os->size > UINT32_MAX))
+ errorOrWarn("section " + os->name + " at 0x" + utohexstr(os->addr) +
+ " of size 0x" + utohexstr(os->size) +
" exceeds available address space");
// Check for overlapping file offsets. In this case we need to skip any
@@ -2267,17 +2414,17 @@ template <class ELFT> void Writer<ELFT>::checkSections() {
// the file so Sec->Offset + Sec->Size can overlap with others. If --oformat
// binary is specified only add SHF_ALLOC sections are added to the output
// file so we skip any non-allocated sections in that case.
- std::vector<SectionOffset> FileOffs;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Size > 0 && Sec->Type != SHT_NOBITS &&
- (!Config->OFormatBinary || (Sec->Flags & SHF_ALLOC)))
- FileOffs.push_back({Sec, Sec->Offset});
- checkOverlap("file", FileOffs, false);
+ std::vector<SectionOffset> fileOffs;
+ for (OutputSection *sec : outputSections)
+ if (sec->size > 0 && sec->type != SHT_NOBITS &&
+ (!config->oFormatBinary || (sec->flags & SHF_ALLOC)))
+ fileOffs.push_back({sec, sec->offset});
+ checkOverlap("file", fileOffs, false);
// When linking with -r there is no need to check for overlapping virtual/load
// addresses since those addresses will only be assigned when the final
// executable/shared object is created.
- if (Config->Relocatable)
+ if (config->relocatable)
return;
// Checking for overlapping virtual and load addresses only needs to take
@@ -2285,20 +2432,20 @@ template <class ELFT> void Writer<ELFT>::checkSections() {
// Furthermore, we also need to skip SHF_TLS sections since these will be
// mapped to other addresses at runtime and can therefore have overlapping
// ranges in the file.
- std::vector<SectionOffset> VMAs;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Size > 0 && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS))
- VMAs.push_back({Sec, Sec->Addr});
- checkOverlap("virtual address", VMAs, true);
+ std::vector<SectionOffset> vmas;
+ for (OutputSection *sec : outputSections)
+ if (sec->size > 0 && (sec->flags & SHF_ALLOC) && !(sec->flags & SHF_TLS))
+ vmas.push_back({sec, sec->addr});
+ checkOverlap("virtual address", vmas, true);
// Finally, check that the load addresses don't overlap. This will usually be
// the same as the virtual addresses but can be different when using a linker
// script with AT().
- std::vector<SectionOffset> LMAs;
- for (OutputSection *Sec : OutputSections)
- if (Sec->Size > 0 && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS))
- LMAs.push_back({Sec, Sec->getLMA()});
- checkOverlap("load address", LMAs, false);
+ std::vector<SectionOffset> lmas;
+ for (OutputSection *sec : outputSections)
+ if (sec->size > 0 && (sec->flags & SHF_ALLOC) && !(sec->flags & SHF_TLS))
+ lmas.push_back({sec, sec->getLMA()});
+ checkOverlap("load address", lmas, false);
}
// The entry point address is chosen in the following ways.
@@ -2311,89 +2458,45 @@ template <class ELFT> void Writer<ELFT>::checkSections() {
// 6. the address 0.
static uint64_t getEntryAddr() {
// Case 1, 2 or 3
- if (Symbol *B = Symtab->find(Config->Entry))
- return B->getVA();
+ if (Symbol *b = symtab->find(config->entry))
+ return b->getVA();
// Case 4
- uint64_t Addr;
- if (to_integer(Config->Entry, Addr))
- return Addr;
+ uint64_t addr;
+ if (to_integer(config->entry, addr))
+ return addr;
// Case 5
- if (OutputSection *Sec = findSection(".text")) {
- if (Config->WarnMissingEntry)
- warn("cannot find entry symbol " + Config->Entry + "; defaulting to 0x" +
- utohexstr(Sec->Addr));
- return Sec->Addr;
+ if (OutputSection *sec = findSection(".text")) {
+ if (config->warnMissingEntry)
+ warn("cannot find entry symbol " + config->entry + "; defaulting to 0x" +
+ utohexstr(sec->addr));
+ return sec->addr;
}
// Case 6
- if (Config->WarnMissingEntry)
- warn("cannot find entry symbol " + Config->Entry +
+ if (config->warnMissingEntry)
+ warn("cannot find entry symbol " + config->entry +
"; not setting start address");
return 0;
}
static uint16_t getELFType() {
- if (Config->Pic)
+ if (config->isPic)
return ET_DYN;
- if (Config->Relocatable)
+ if (config->relocatable)
return ET_REL;
return ET_EXEC;
}
-static uint8_t getAbiVersion() {
- // MIPS non-PIC executable gets ABI version 1.
- if (Config->EMachine == EM_MIPS && getELFType() == ET_EXEC &&
- (Config->EFlags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
- return 1;
- return 0;
-}
-
template <class ELFT> void Writer<ELFT>::writeHeader() {
- uint8_t *Buf = Buffer->getBufferStart();
-
- // 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.
- memset(Buf, 0, sizeof(Elf_Ehdr));
- memcpy(Buf, "\177ELF", 4);
-
- // Write the ELF header.
- auto *EHdr = reinterpret_cast<Elf_Ehdr *>(Buf);
- EHdr->e_ident[EI_CLASS] = Config->Is64 ? ELFCLASS64 : ELFCLASS32;
- EHdr->e_ident[EI_DATA] = Config->IsLE ? ELFDATA2LSB : ELFDATA2MSB;
- EHdr->e_ident[EI_VERSION] = EV_CURRENT;
- EHdr->e_ident[EI_OSABI] = Config->OSABI;
- EHdr->e_ident[EI_ABIVERSION] = getAbiVersion();
- EHdr->e_type = getELFType();
- EHdr->e_machine = Config->EMachine;
- EHdr->e_version = EV_CURRENT;
- EHdr->e_entry = getEntryAddr();
- EHdr->e_shoff = SectionHeaderOff;
- EHdr->e_flags = Config->EFlags;
- EHdr->e_ehsize = sizeof(Elf_Ehdr);
- EHdr->e_phnum = Phdrs.size();
- EHdr->e_shentsize = sizeof(Elf_Shdr);
-
- if (!Config->Relocatable) {
- EHdr->e_phoff = sizeof(Elf_Ehdr);
- EHdr->e_phentsize = sizeof(Elf_Phdr);
- }
-
- // Write the program header table.
- auto *HBuf = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
- for (PhdrEntry *P : Phdrs) {
- HBuf->p_type = P->p_type;
- HBuf->p_flags = P->p_flags;
- HBuf->p_offset = P->p_offset;
- HBuf->p_vaddr = P->p_vaddr;
- HBuf->p_paddr = P->p_paddr;
- HBuf->p_filesz = P->p_filesz;
- HBuf->p_memsz = P->p_memsz;
- HBuf->p_align = P->p_align;
- ++HBuf;
- }
+ writeEhdr<ELFT>(Out::bufferStart, *mainPart);
+ writePhdrs<ELFT>(Out::bufferStart + sizeof(Elf_Ehdr), *mainPart);
+
+ auto *eHdr = reinterpret_cast<Elf_Ehdr *>(Out::bufferStart);
+ eHdr->e_type = getELFType();
+ eHdr->e_entry = getEntryAddr();
+ eHdr->e_shoff = sectionHeaderOff;
// Write the section header table.
//
@@ -2404,57 +2507,58 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
// the value. The sentinel values and fields are:
// e_shnum = 0, SHdrs[0].sh_size = number of sections.
// e_shstrndx = SHN_XINDEX, SHdrs[0].sh_link = .shstrtab section index.
- auto *SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff);
- size_t Num = OutputSections.size() + 1;
- if (Num >= SHN_LORESERVE)
- SHdrs->sh_size = Num;
+ auto *sHdrs = reinterpret_cast<Elf_Shdr *>(Out::bufferStart + eHdr->e_shoff);
+ size_t num = outputSections.size() + 1;
+ if (num >= SHN_LORESERVE)
+ sHdrs->sh_size = num;
else
- EHdr->e_shnum = Num;
+ eHdr->e_shnum = num;
- uint32_t StrTabIndex = In.ShStrTab->getParent()->SectionIndex;
- if (StrTabIndex >= SHN_LORESERVE) {
- SHdrs->sh_link = StrTabIndex;
- EHdr->e_shstrndx = SHN_XINDEX;
+ uint32_t strTabIndex = in.shStrTab->getParent()->sectionIndex;
+ if (strTabIndex >= SHN_LORESERVE) {
+ sHdrs->sh_link = strTabIndex;
+ eHdr->e_shstrndx = SHN_XINDEX;
} else {
- EHdr->e_shstrndx = StrTabIndex;
+ eHdr->e_shstrndx = strTabIndex;
}
- for (OutputSection *Sec : OutputSections)
- Sec->writeHeaderTo<ELFT>(++SHdrs);
+ for (OutputSection *sec : outputSections)
+ sec->writeHeaderTo<ELFT>(++sHdrs);
}
// Open a result file.
template <class ELFT> void Writer<ELFT>::openFile() {
- uint64_t MaxSize = Config->Is64 ? INT64_MAX : UINT32_MAX;
- if (MaxSize < FileSize) {
- error("output file too large: " + Twine(FileSize) + " bytes");
+ uint64_t maxSize = config->is64 ? INT64_MAX : UINT32_MAX;
+ if (fileSize != size_t(fileSize) || maxSize < fileSize) {
+ error("output file too large: " + Twine(fileSize) + " bytes");
return;
}
- unlinkAsync(Config->OutputFile);
- unsigned Flags = 0;
- if (!Config->Relocatable)
- Flags = FileOutputBuffer::F_executable;
- Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(Config->OutputFile, FileSize, Flags);
+ unlinkAsync(config->outputFile);
+ unsigned flags = 0;
+ if (!config->relocatable)
+ flags = FileOutputBuffer::F_executable;
+ Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr =
+ FileOutputBuffer::create(config->outputFile, fileSize, flags);
- if (!BufferOrErr)
- error("failed to open " + Config->OutputFile + ": " +
- llvm::toString(BufferOrErr.takeError()));
- else
- Buffer = std::move(*BufferOrErr);
+ if (!bufferOrErr) {
+ error("failed to open " + config->outputFile + ": " +
+ llvm::toString(bufferOrErr.takeError()));
+ return;
+ }
+ buffer = std::move(*bufferOrErr);
+ Out::bufferStart = buffer->getBufferStart();
}
template <class ELFT> void Writer<ELFT>::writeSectionsBinary() {
- uint8_t *Buf = Buffer->getBufferStart();
- for (OutputSection *Sec : OutputSections)
- if (Sec->Flags & SHF_ALLOC)
- Sec->writeTo<ELFT>(Buf + Sec->Offset);
+ for (OutputSection *sec : outputSections)
+ if (sec->flags & SHF_ALLOC)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
}
-static void fillTrap(uint8_t *I, uint8_t *End) {
- for (; I + 4 <= End; I += 4)
- memcpy(I, &Target->TrapInstr, 4);
+static void fillTrap(uint8_t *i, uint8_t *end) {
+ for (; i + 4 <= end; i += 4)
+ memcpy(i, &target->trapInstr, 4);
}
// Fill the last page of executable segments with trap instructions
@@ -2464,61 +2568,119 @@ static void fillTrap(uint8_t *I, uint8_t *End) {
// We'll leave other pages in segments as-is because the rest will be
// overwritten by output sections.
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
- if (Script->HasSectionsCommand)
+ if (script->hasSectionsCommand)
return;
- // Fill the last page.
- uint8_t *Buf = Buffer->getBufferStart();
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD && (P->p_flags & PF_X))
- fillTrap(Buf + alignDown(P->p_offset + P->p_filesz, Target->PageSize),
- Buf + alignTo(P->p_offset + P->p_filesz, Target->PageSize));
-
- // Round up the file size of the last segment to the page boundary iff it is
- // an executable segment to ensure that other tools don't accidentally
- // trim the instruction padding (e.g. when stripping the file).
- PhdrEntry *Last = nullptr;
- for (PhdrEntry *P : Phdrs)
- if (P->p_type == PT_LOAD)
- Last = P;
-
- if (Last && (Last->p_flags & PF_X))
- Last->p_memsz = Last->p_filesz = alignTo(Last->p_filesz, Target->PageSize);
+ for (Partition &part : partitions) {
+ // Fill the last page.
+ for (PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD && (p->p_flags & PF_X))
+ fillTrap(Out::bufferStart + alignDown(p->firstSec->offset + p->p_filesz,
+ config->commonPageSize),
+ Out::bufferStart + alignTo(p->firstSec->offset + p->p_filesz,
+ config->commonPageSize));
+
+ // Round up the file size of the last segment to the page boundary iff it is
+ // an executable segment to ensure that other tools don't accidentally
+ // trim the instruction padding (e.g. when stripping the file).
+ PhdrEntry *last = nullptr;
+ for (PhdrEntry *p : part.phdrs)
+ if (p->p_type == PT_LOAD)
+ last = p;
+
+ if (last && (last->p_flags & PF_X))
+ last->p_memsz = last->p_filesz =
+ alignTo(last->p_filesz, config->commonPageSize);
+ }
}
// Write section contents to a mmap'ed file.
template <class ELFT> void Writer<ELFT>::writeSections() {
- uint8_t *Buf = Buffer->getBufferStart();
-
- OutputSection *EhFrameHdr = nullptr;
- if (In.EhFrameHdr && !In.EhFrameHdr->empty())
- EhFrameHdr = In.EhFrameHdr->getParent();
-
// In -r or -emit-relocs mode, write the relocation sections first as in
// ELf_Rel targets we might find out that we need to modify the relocated
// section while doing it.
- for (OutputSection *Sec : OutputSections)
- if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
- Sec->writeTo<ELFT>(Buf + Sec->Offset);
+ for (OutputSection *sec : outputSections)
+ if (sec->type == SHT_REL || sec->type == SHT_RELA)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+
+ for (OutputSection *sec : outputSections)
+ if (sec->type != SHT_REL && sec->type != SHT_RELA)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+}
+
+// Split one uint8 array into small pieces of uint8 arrays.
+static std::vector<ArrayRef<uint8_t>> split(ArrayRef<uint8_t> arr,
+ size_t chunkSize) {
+ std::vector<ArrayRef<uint8_t>> ret;
+ while (arr.size() > chunkSize) {
+ ret.push_back(arr.take_front(chunkSize));
+ arr = arr.drop_front(chunkSize);
+ }
+ if (!arr.empty())
+ ret.push_back(arr);
+ return ret;
+}
- for (OutputSection *Sec : OutputSections)
- if (Sec != EhFrameHdr && Sec->Type != SHT_REL && Sec->Type != SHT_RELA)
- Sec->writeTo<ELFT>(Buf + Sec->Offset);
+// Computes a hash value of Data using a given hash function.
+// In order to utilize multiple cores, we first split data into 1MB
+// chunks, compute a hash for each chunk, and then compute a hash value
+// of the hash values.
+static void
+computeHash(llvm::MutableArrayRef<uint8_t> hashBuf,
+ llvm::ArrayRef<uint8_t> data,
+ std::function<void(uint8_t *dest, ArrayRef<uint8_t> arr)> hashFn) {
+ std::vector<ArrayRef<uint8_t>> chunks = split(data, 1024 * 1024);
+ std::vector<uint8_t> hashes(chunks.size() * hashBuf.size());
+
+ // Compute hash values.
+ parallelForEachN(0, chunks.size(), [&](size_t i) {
+ hashFn(hashes.data() + i * hashBuf.size(), chunks[i]);
+ });
- // The .eh_frame_hdr depends on .eh_frame section contents, therefore
- // it should be written after .eh_frame is written.
- if (EhFrameHdr)
- EhFrameHdr->writeTo<ELFT>(Buf + EhFrameHdr->Offset);
+ // Write to the final output buffer.
+ hashFn(hashBuf.data(), hashes);
}
template <class ELFT> void Writer<ELFT>::writeBuildId() {
- if (!In.BuildId || !In.BuildId->getParent())
+ if (!mainPart->buildId || !mainPart->buildId->getParent())
return;
+ if (config->buildId == BuildIdKind::Hexstring) {
+ for (Partition &part : partitions)
+ part.buildId->writeBuildId(config->buildIdVector);
+ return;
+ }
+
// Compute a hash of all sections of the output file.
- uint8_t *Start = Buffer->getBufferStart();
- uint8_t *End = Start + FileSize;
- In.BuildId->writeBuildId({Start, End});
+ size_t hashSize = mainPart->buildId->hashSize;
+ std::vector<uint8_t> buildId(hashSize);
+ llvm::ArrayRef<uint8_t> buf{Out::bufferStart, size_t(fileSize)};
+
+ switch (config->buildId) {
+ case BuildIdKind::Fast:
+ computeHash(buildId, buf, [](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ write64le(dest, xxHash64(arr));
+ });
+ break;
+ case BuildIdKind::Md5:
+ computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ memcpy(dest, MD5::hash(arr).data(), hashSize);
+ });
+ break;
+ case BuildIdKind::Sha1:
+ computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ memcpy(dest, SHA1::hash(arr).data(), hashSize);
+ });
+ break;
+ case BuildIdKind::Uuid:
+ if (auto ec = llvm::getRandomBytes(buildId.data(), hashSize))
+ error("entropy source failure: " + ec.message());
+ break;
+ default:
+ llvm_unreachable("unknown BuildIdKind");
+ }
+ for (Partition &part : partitions)
+ part.buildId->writeBuildId(buildId);
}
template void elf::writeResult<ELF32LE>();
diff --git a/ELF/Writer.h b/ELF/Writer.h
index 7806f824c58f..784fba9c75a6 100644
--- a/ELF/Writer.h
+++ b/ELF/Writer.h
@@ -1,9 +1,8 @@
//===- Writer.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -20,16 +19,14 @@ namespace elf {
class InputFile;
class OutputSection;
class InputSectionBase;
-template <class ELFT> class ObjFile;
-class SymbolTable;
template <class ELFT> void writeResult();
// This describes a program header entry.
// Each contains type, access flags and range of output sections that will be
// placed in it.
struct PhdrEntry {
- PhdrEntry(unsigned Type, unsigned Flags) : p_type(Type), p_flags(Flags) {}
- void add(OutputSection *Sec);
+ PhdrEntry(unsigned type, unsigned flags) : p_type(type), p_flags(flags) {}
+ void add(OutputSection *sec);
uint64_t p_paddr = 0;
uint64_t p_vaddr = 0;
@@ -40,22 +37,22 @@ struct PhdrEntry {
uint32_t p_type = 0;
uint32_t p_flags = 0;
- OutputSection *FirstSec = nullptr;
- OutputSection *LastSec = nullptr;
- bool HasLMA = false;
+ OutputSection *firstSec = nullptr;
+ OutputSection *lastSec = nullptr;
+ bool hasLMA = false;
- uint64_t LMAOffset = 0;
+ uint64_t lmaOffset = 0;
};
void addReservedSymbols();
-llvm::StringRef getOutputSectionName(const InputSectionBase *S);
+llvm::StringRef getOutputSectionName(const InputSectionBase *s);
template <class ELFT> uint32_t calcMipsEFlags();
-uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
- llvm::StringRef FileName);
+uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ llvm::StringRef fileName);
-bool isMipsN32Abi(const InputFile *F);
+bool isMipsN32Abi(const InputFile *f);
bool isMicroMips();
bool isMipsR6();
} // namespace elf
diff --git a/LICENSE.TXT b/LICENSE.TXT
index 09b8b616c227..cba22f66a9e0 100644
--- a/LICENSE.TXT
+++ b/LICENSE.TXT
@@ -1,5 +1,240 @@
==============================================================================
-lld License
+The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
+==============================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
+==============================================================================
+Software from third parties included in the LLVM Project:
+==============================================================================
+The LLVM Project contains third party software which is under different license
+terms. All such code will be identified clearly using at least one of two
+mechanisms:
+1) It will be in a separate directory tree with its own `LICENSE.txt` or
+ `LICENSE` file at the top containing the specific license and restrictions
+ which apply to that software, or
+2) It will contain specific license and restriction terms at the top of every
+ file.
+
+==============================================================================
+Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
University of Illinois/NCSA
Open Source License
@@ -41,22 +276,3 @@ CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
-
-==============================================================================
-The lld software contains code written by third parties. Such software will
-have its own individual LICENSE.TXT file in the directory in which it appears.
-This file will describe the copyrights, license, and restrictions which apply
-to that code.
-
-The disclaimer of warranty in the University of Illinois Open Source License
-applies to all code in the lld Distribution, and nothing in any of the
-other licenses gives permission to use the names of the LLVM Team or the
-University of Illinois to endorse or promote products derived from this
-Software.
-
-The following pieces of software have additional or alternate copyrights,
-licenses, and/or restrictions:
-
-Program Directory
-------- ---------
-<none yet>
diff --git a/docs/NewLLD.rst b/docs/NewLLD.rst
index 79bdf90c6ccd..d01fb53b953f 100644
--- a/docs/NewLLD.rst
+++ b/docs/NewLLD.rst
@@ -307,3 +307,11 @@ Glossary
On Unix, your program is generally not guaranteed to be safe with ICF,
although large programs happen to work correctly.
LLD works fine with ICF for example.
+
+Other Info
+----------
+
+.. toctree::
+ :maxdepth: 1
+
+ missingkeyfunction
diff --git a/docs/Partitions.rst b/docs/Partitions.rst
new file mode 100644
index 000000000000..e9113087631a
--- /dev/null
+++ b/docs/Partitions.rst
@@ -0,0 +1,116 @@
+Partitions
+==========
+
+.. warning::
+
+ This feature is currently experimental, and its interface is subject
+ to change.
+
+LLD's partitioning feature allows a program (which may be an executable
+or a shared library) to be split into multiple pieces, or partitions. A
+partitioned program consists of a main partition together with a number of
+loadable partitions. The loadable partitions depend on the main partition
+in a similar way to a regular ELF shared object dependency, but unlike a
+shared object, the main partition and the loadable partitions share a virtual
+address space at link time, and each loadable partition is assigned a fixed
+offset from the main partition. This allows the loadable partitions to refer
+to code and data in the main partition directly without the binary size and
+performance overhead of PLTs, GOTs or symbol table entries.
+
+Usage
+-----
+
+A program that uses the partitioning feature must decide which symbols are
+going to be used as the "entry points" for each partition. An entry point
+could, for example, be the equivalent of the partition's ``main`` function, or
+there could be a group of functions that expose the functionality implemented
+by the partition. The intent is that in order to use a loadable partition,
+the program will use ``dlopen``/``dlsym`` or similar functions to dynamically
+load the partition at its assigned address, look up an entry point by name
+and call it. Note, however, that the standard ``dlopen`` function does not
+allow specifying a load address. On Android, the ``android_dlopen_ext``
+function may be used together with the ``ANDROID_DLEXT_RESERVED_ADDRESS``
+flag to load a shared object at a specific address.
+
+Once the entry points have been decided, the translation unit(s)
+containing the entry points should be compiled using the Clang compiler flag
+``-fsymbol-partition=<soname>``, where ``<soname>`` is the intended soname
+of the partition. The resulting object files are passed to the linker in
+the usual way.
+
+The linker will then use these entry points to automatically split the program
+into partitions according to which sections of the program are reachable from
+which entry points, similarly to how ``--gc-sections`` removes unused parts of
+a program. Any sections that are only reachable from a loadable partition's
+entry point are assigned to that partition, while all other sections are
+assigned to the main partition, including sections only reachable from
+loadable partitions.
+
+The following diagram illustrates how sections are assigned to partitions. Each
+section is colored according to its assigned partition.
+
+.. image:: partitions.svg
+
+The result of linking a program that uses partitions is essentially an
+ELF file with all of the partitions concatenated together. This file is
+referred to as a combined output file. To extract a partition from the
+combined output file, the ``llvm-objcopy`` tool should be used together
+with the flag ``--extract-main-partition`` to extract the main partition, or
+``-extract-partition=<soname>`` to extract one of the loadable partitions.
+An example command sequence is shown below:
+
+.. code-block:: shell
+
+ # Compile the main program.
+ clang -ffunction-sections -fdata-sections -c main.c
+
+ # Compile a feature to be placed in a loadable partition.
+ # Note that this is likely to be a separate build step to the main partition.
+ clang -ffunction-sections -fdata-sections -fsymbol-partition=libfeature.so -c feature.c
+
+ # Link the combined output file.
+ clang main.o feature.o -fuse-ld=lld -shared -o libcombined.so -Wl,-soname,libmain.so -Wl,--gc-sections
+
+ # Extract the partitions.
+ llvm-objcopy libcombined.so libmain.so --extract-main-partition
+ llvm-objcopy libcombined.so libfeature.so --extract-partition=libfeature.so
+
+In order to allow a program to discover the names of its loadable partitions
+and the locations of their reserved regions, the linker creates a partition
+index, which is an array of structs with the following definition:
+
+.. code-block:: c
+
+ struct partition_index_entry {
+ int32_t name_offset;
+ int32_t addr_offset;
+ uint32_t size;
+ };
+
+The ``name_offset`` field is a relative pointer to a null-terminated string
+containing the soname of the partition, the ``addr_offset`` field is a
+relative pointer to its load address and the ``size`` field contains the
+size of the region reserved for the partition. To derive an absolute pointer
+from the relative pointer fields in this data structure, the address of the
+field should be added to the value stored in the field.
+
+The program may discover the location of the partition index using the
+linker-defined symbols ``__part_index_begin`` and ``__part_index_end``.
+
+Restrictions
+------------
+
+This feature is currently only supported in the ELF linker.
+
+The partitioning feature may not currently be used together with the
+``SECTIONS`` or ``PHDRS`` linker script features, nor may it be used with the
+``--section-start``, ``-Ttext``, ``-Tdata`` or ``-Tbss`` flags. All of these
+features assume a single set of output sections and/or program headers, which
+makes their semantics ambiguous in the presence of more than one partition.
+
+The partitioning feature may not currently be used on the MIPS architecture
+because it is unclear whether the MIPS multi-GOT ABI is compatible with
+partitions.
+
+The current implementation only supports creating up to 254 partitions due
+to implementation limitations. This limit may be relaxed in the future.
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index dc5df6795d99..76207fec11ac 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -1,19 +1,19 @@
=======================
-lld 8.0.0 Release Notes
+lld 9.0.0 Release Notes
=======================
.. contents::
:local:
.. warning::
- These are in-progress notes for the upcoming LLVM 8.0.0 release.
+ These are in-progress notes for the upcoming LLVM 9.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 8.0.0.
+This document contains the release notes for the lld linker, release 9.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/>`_.
@@ -24,47 +24,43 @@ Non-comprehensive list of changes in this release
ELF Improvements
----------------
-* lld now supports RISC-V. (`r339364
- <https://reviews.llvm.org/rL339364>`_)
+* ld.lld now has typo suggestions for flags:
+ ``$ ld.lld --call-shared`` now prints
+ ``unknown argument '--call-shared', did you mean '--call_shared'``.
-* Default image base address has changed from 65536 to 2 MiB for i386
- and 4 MiB for AArch64 to make lld-generated executables work better
- with automatic superpage promotion. FreeBSD can promote contiguous
- non-superpages to a superpage if they are aligned to the superpage
- size. (`r342746 <https://reviews.llvm.org/rL342746>`_)
+* ...
-* lld/Hexagon can now link Linux kernel and musl libc for Qualcomm
- Hexagon ISA.
+COFF Improvements
+-----------------
-* Initial MSP430 ISA support has landed.
+* Like the ELF driver, lld-link now has typo suggestions for flags.
-* The following flags have been added: ``-z interpose``, ``-z global``
+* lld-link now correctly reports duplicate symbol errors for obj files
+ that were compiled with /Gy.
-COFF Improvements
------------------
+* lld-link now correctly reports duplicate symbol errors when several res
+ input files define resources with the same type, name, and language.
+ This can be demoted to a warning using ``/force:multipleres``.
+
+* Having more than two ``/natvis:`` now works correctly; it used to not
+ work for larger binaries before.
-* PDB GUID is set to hash of PDB contents instead to a random byte
- sequence for build reproducibility.
+* Undefined symbols are now printed only in demangled form. Pass
+ ``/demangle:no`` to see raw symbol names instead.
-* The following flags have been added: ``/force:multiple``
+* The following flags have been added: ``/functionpadmin``, ``/swaprun:``,
+ ``/threads:no``
-* lld now can link against import libraries produced by GNU tools.
+* Several speed and memory usage improvements.
-* lld can create thunks for ARM, to allow linking images over 16 MB.
+* ...
MinGW Improvements
------------------
-* lld can now automatically import data variables from DLLs without the
- use of the dllimport attribute.
-
-* lld can now use existing normal MinGW sysroots with import libraries and
- CRT startup object files for GNU binutils. lld can handle most object
- files produced by GCC, and thus works as a drop-in replacement for
- ld.bfd in such environments. (There are known issues with linking crtend.o
- from GCC in setups with DWARF exceptions though, where object files are
- linked in a different order than with GNU ld, inserting a DWARF exception
- table terminator too early.)
+* lld now correctly links crtend.o as the last object file, handling
+ terminators for the sections such as .eh_frame properly, fixing
+ DWARF exception handling with libgcc and gcc's crtend.o.
MachO Improvements
------------------
@@ -74,7 +70,4 @@ MachO Improvements
WebAssembly Improvements
------------------------
-* Add initial support for creating shared libraries (-shared).
- Note: The shared library format is still under active development and may
- undergo significant changes in future versions.
- See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+* ...
diff --git a/docs/WebAssembly.rst b/docs/WebAssembly.rst
index 424c1a10c7e7..41522163bb85 100644
--- a/docs/WebAssembly.rst
+++ b/docs/WebAssembly.rst
@@ -4,21 +4,17 @@ WebAssembly lld port
The WebAssembly version of lld takes WebAssembly binaries as inputs and produces
a WebAssembly binary as its output. For the most part it tries to mimic the
behaviour of traditional ELF linkers and specifically the ELF lld port. Where
-possible that command line flags and the semantics should be the same.
+possible the command line flags and the semantics should be the same.
Object file format
------------------
-The format the input object files that lld expects is specified as part of the
-the WebAssembly tool conventions
-https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md.
-
-This is object format that the llvm will produce when run with the
-``wasm32-unknown-unknown`` target. To build llvm with WebAssembly support
-currently requires enabling the experimental backed using
-``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly``.
+The WebAssembly object file format used by LLVM and LLD is specified as part of
+the WebAssembly tool conventions on linking_.
+This is the object format that the llvm will produce when run with the
+``wasm32-unknown-unknown`` target.
Usage
-----
@@ -88,10 +84,52 @@ WebAssembly-specific options:
By default the function table is neither imported nor exported, but defined
for internal use only.
-When building shared libraries symbols are exported if they are marked
-as ``visibility=default``. When building executables only the entry point is
-exported by default. In addition any symbol included on the command line via
-``--export`` is also exported.
+Behaviour
+---------
+
+In general, where possible, the WebAssembly linker attempts to emulate the
+behaviour of a traditional ELF linker, and in particular the ELF port of lld.
+For more specific details on how this is achieved see the tool conventions on
+linking_.
+
+Function Signatures
+~~~~~~~~~~~~~~~~~~~
+
+One way in which the WebAssembly linker differs from traditional native linkers
+is that function signature checking is strict in WebAssembly. It is a
+validation error for a module to contain a call site that doesn't agree with
+the target signature. Even though this is undefined behaviour in C/C++, it is not
+uncommon to find this in real-world C/C++ programs. For example, a call site in
+one compilation unit which calls a function defined in another compilation
+unit but with too many arguments.
+
+In order not to generate such invalid modules, lld has two modes of handling such
+mismatches: it can simply error-out or it can create stub functions that will
+trap at runtime (functions that contain only an ``unreachable`` instruction)
+and use these stub functions at the otherwise invalid call sites.
+
+The default behaviour is to generate these stub function and to produce
+a warning. The ``--falal-warnings`` flag can be used to disable this behaviour
+and error out if mismatched are found.
+
+Imports and Exports
+~~~~~~~~~~~~~~~~~~~
+
+When building a shared library any symbols marked as ``visibility=default`` will
+be exported. When building an executable, only the entry point and symbols
+flagged as ``WASM_SYMBOL_EXPORTED`` are exported by default. In LLVM the
+``WASM_SYMBOL_EXPORTED`` flag is applied to any symbol in the ``llvm.used`` list
+which corresponds to ``__attribute__((used))`` in C/C++ sources.
+
+In addition, symbols can be exported via the linker command line using
+``--export``.
+
+Finally, just like with native ELF linker the ``--export-dynamic`` flag can be
+used to export symbol in the executable which are marked as
+``visibility=default``.
+
+Garbage Collection
+~~~~~~~~~~~~~~~~~~
Since WebAssembly is designed with size in mind the linker defaults to
``--gc-sections`` which means that all unused functions and data segments will
@@ -103,6 +141,18 @@ The symbols which are preserved by default are:
- Any symbol which is to be exported.
- Any symbol transitively referenced by the above.
+Weak Undefined Functions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+On native platforms, calls to weak undefined functions end up as calls to the
+null function pointer. With WebAssembly, direct calls must reference a defined
+function (with the correct signature). In order to handle this case the linker
+will generate function a stub containing only the ``unreachable`` instruction
+and use this for any direct references to an undefined weak function.
+
+For example a runtime call to a weak undefined function ``foo`` will up trapping
+on ``unreachable`` inside and linker-generated function called
+``undefined:foo``.
Missing features
----------------
@@ -112,3 +162,5 @@ Missing features
- No support for creating shared libraries. The spec for shared libraries in
WebAssembly is still in flux:
https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+
+.. _linking: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md
diff --git a/docs/conf.py b/docs/conf.py
index 62404b275450..dd65859fe121 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -48,9 +48,9 @@ copyright = u'2011-%d, LLVM Project' % date.today().year
# built documents.
#
# The short version.
-version = '8'
+version = '9'
# The full version, including alpha/beta/rc tags.
-release = '8'
+release = '9'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 97c3d1bccbda..a174f652e736 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -28,23 +28,15 @@ On Unix-like Systems
.. _libc++: http://libcxx.llvm.org/
.. _Python 2.4: http://python.org/download/
-2. Check out LLVM::
+2. Check out LLVM and subprojects (including lld)::
- $ cd path/to/llvm-project
- $ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
-
-3. Check out lld::
-
- $ cd llvm/tools
- $ svn co http://llvm.org/svn/llvm-project/lld/trunk lld
-
- * lld can also be checked out to ``path/to/llvm-project`` and built as an external
- project.
+ $ git clone https://github.com/llvm/llvm-project.git
4. Build LLVM and lld::
- $ cd path/to/llvm-build/llvm (out of source build required)
- $ cmake -G "Unix Makefiles" path/to/llvm-project/llvm
+ $ cd llvm-project
+ $ mkdir build && cd build
+ $ cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS=lld ../llvm
$ make
* If you want to build with clang and it is not the default compiler or
@@ -71,23 +63,12 @@ Using Visual Studio
.. _Visual Studio 12 (2013) or later: http://www.microsoft.com/visualstudio/11/en-us
.. _Python 2.4: http://python.org/download/
-#. Check out LLVM::
-
- $ cd path/to/llvm-project
- $ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
-
-#. Check out lld::
-
- $ cd llvm/tools
- $ svn co http://llvm.org/svn/llvm-project/lld/trunk lld
-
- * lld can also be checked out to ``path/to/llvm-project`` and built as an external
- project.
+#. Check out LLVM as above.
#. Generate Visual Studio project files::
- $ cd path/to/llvm-build/llvm (out of source build required)
- $ cmake -G "Visual Studio 11" path/to/llvm-project/llvm
+ $ cd llvm-project/build (out of source build required)
+ $ cmake -G "Visual Studio 11" -DLLVM_ENABLE_PROJECTS=lld ../llvm
#. Build
diff --git a/docs/index.rst b/docs/index.rst
index da1c894f3d83..9056d1c2de15 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -106,7 +106,7 @@ build that tree. You need `cmake` and of course a C++ compiler.
.. code-block:: console
- $ git clone https://github.com/llvm-project/llvm-project-20170507 llvm-project
+ $ git clone https://github.com/llvm/llvm-project llvm-project
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=lld -DCMAKE_INSTALL_PREFIX=/usr/local ../llvm-project/llvm
@@ -173,4 +173,6 @@ document soon.
AtomLLD
WebAssembly
windows_support
+ missingkeyfunction
+ Partitions
ReleaseNotes
diff --git a/docs/ld.lld.1 b/docs/ld.lld.1
index d1ce4a3517f4..c432aacf7f73 100644
--- a/docs/ld.lld.1
+++ b/docs/ld.lld.1
@@ -1,9 +1,10 @@
-.\" This file is distributed under the University of Illinois Open Source
-.\" License. See LICENSE.TXT for details.
+.\" 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 man page documents only lld's ELF linking support, obtained originally
.\" from FreeBSD.
-.Dd September 26, 2018
+.Dd May 12, 2019
.Dt LD.LLD 1
.Os
.Sh NAME
@@ -45,6 +46,9 @@ as a native linker as well as a cross linker.
.It Fl -allow-multiple-definition
Do not error if a symbol is defined multiple times.
The first definition will be used.
+.It Fl -allow-shlib-undefined
+Allow unresolved references in shared libraries.
+This option is enabled by default when linking a shared library.
.It Fl -apply-dynamic-relocs
Apply link-time values for dynamic relocations.
.It Fl -as-needed
@@ -178,6 +182,8 @@ Set the
field to the specified value.
.It Fl -fini Ns = Ns Ar symbol
Specify a finalizer function.
+.It Fl -force-bti
+Force enable AArch64 BTI instruction in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property.
.It Fl -format Ns = Ns Ar input-format , Fl b Ar input-format
Specify the format of the inputs following this option.
.Ar input-format
@@ -213,6 +219,15 @@ Enable identical code folding.
Enable safe identical code folding.
.It Fl -icf Ns = Ns Cm none
Disable identical code folding.
+.It Fl -ignore-data-address-equality
+Ignore address equality of data. C/C++ requires each data to have a unique
+address. This option allows lld to do unsafe optimization that breaks the
+requirement: create copies of read-only data or merge two or more read-only data
+that happen to have the same value.
+.It Fl -ignore-function-address-equality
+Ignore address equality of functions. This option allows non-PIC calls to a
+function with non-default visibility in a shared object. The function may have
+different addresses within the executable and within the shared object.
.It Fl -image-base Ns = Ns Ar value
Set the base address to
.Ar value .
@@ -241,6 +256,11 @@ Set target emulation.
.It Fl -Map Ns = Ns Ar file , Fl M Ar file
Print a link map to
.Ar file .
+.It Fl -nmagic , Fl n
+Do not page align sections, link against static libraries.
+.It Fl -no-allow-shlib-undefined
+Do not allow unresolved references in shared libraries.
+This option is enabled by default when linking an executable.
.It Fl -no-as-needed
Always set
.Dv DT_NEEDED
@@ -259,6 +279,12 @@ section.
Disable garbage collection of unused sections.
.It Fl -no-gnu-unique
Disable STB_GNU_UNIQUE symbol binding.
+.It Fl -no-merge-exidx-entries
+Disable merging .ARM.exidx entries.
+.It Fl -no-nmagic
+Page align sections.
+.It Fl -no-omagic
+Do not set the text data sections to be writable, page align sections.
.It Fl -no-rosegment
Do not put read-only non-executable sections in their own segment.
.It Fl -no-threads
@@ -267,9 +293,11 @@ Do not run the linker multi-threaded.
Report version scripts that refer undefined symbols.
.It Fl -no-undefined
Report unresolved symbols even if the linker is creating a shared library.
+.It Fl -no-warn-symbol-ordering
+Do not warn about problems with the symbol ordering file or call graph profile.
.It Fl -no-whole-archive
Restores the default behavior of loading archive members.
-.It Fl -no-pie
+.It Fl -no-pie , Fl -no-pic-executable
Do not create a position independent executable.
.It Fl -noinhibit-exec
Retain the executable output file whenever it is still usable.
@@ -305,13 +333,62 @@ is
.Cm binary ,
which produces output with no ELF header.
.It Fl -omagic , Fl N
-Set the text and data sections to be readable and writable.
+Set the text and data sections to be readable and writable, do not page align sections, link against static libraries.
.It Fl -opt-remarks-filename Ar file
Write optimization remarks in YAML format to
.Ar file .
+.It Fl -opt-remarks-passes Ar pass-regex
+Filter optimization remarks by only allowing the passes matching
+.Ar pass-regex .
.It Fl -opt-remarks-with-hotness
Include hotness information in the optimization remarks file.
-.It Fl -pie
+.It Fl -orphan-handling Ns = Ns Ar mode
+Control how orphan sections are handled. An orphan section is one not
+specifically mentioned in a linker script.
+.Ar mode
+may be:
+.Pp
+.Bl -tag -width 2n -compact
+.It Cm place
+Place orphan sections in suitable output sections.
+.It Cm warn
+Place orphan sections as for
+.Cm place
+and also report a warning.
+.It Cm error
+Place orphan sections as for
+.Cm place
+and also report an error.
+.El
+.Pp
+.Cm place
+is the default.
+.It Fl -pack-dyn-relocs Ns = Ns Ar format
+Pack dynamic relocations in the given format.
+.Ar format
+may be:
+.Pp
+.Bl -tag -width 2n -compact
+.It Cm none
+Don't pack. Dynamic relocations are encoded in SHT_REL(A).
+.It Cm android
+Pack dynamic relocations in SHT_ANDROID_REL(A).
+.It Cm relr
+Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_REL(A).
+.It Cm android+relr
+Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_ANDROID_REL(A).
+.El
+.Pp
+.Cm none
+is the default. If
+.Fl -use-android-relr-tags
+is specified, use SHT_ANDROID_RELR instead of SHT_RELR.
+.Pp
+.It Fl -pac-plt
+AArch64 only, use pointer authentication in PLT.
+.It Fl -pic-veneer
+Always generate position independent thunks.
+.It Fl -pie , Fl -pic-executable
Create a position independent executable.
.It Fl -print-gc-sections
List removed unused sections.
@@ -356,6 +433,8 @@ Set
.Dv DT_SONAME
to
.Ar value .
+.It Fl -sort-common
+This option is ignored for GNU compatibility.
.It Fl -sort-section Ns = Ns Ar value
Specifies sections sorting rule when linkerscript is used.
.It Fl -start-lib
@@ -425,11 +504,27 @@ Print the names of the input files.
Trace references to
.Ar symbol .
.It Fl -undefined Ns = Ns Ar symbol , Fl u Ar symbol
-Force
+If
.Ar symbol
-to be an undefined symbol during linking.
+is not defined after symbol resolution, and there's a static library
+that contains an object file defining the symbol, load the member
+to include the object file in the output file.
+.It Fl -undefined-glob Ns = Ns Ar pattern
+Synonym for
+.Fl -undefined ,
+except that it takes a glob pattern. In a glob pattern,
+.Cm *
+matches zero or more characters,
+.Cm ?
+matches any single character, and
+.Cm [...]
+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 -unresolved-symbols Ns = Ns Ar value
Determine how to handle unresolved symbols.
+.It Fl -use-android-relr-tags
+Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*.
.It Fl v
Display the version number and proceed with linking if object files are
specified.
@@ -477,6 +572,14 @@ Sets the
.Dv DYNAMIC
section.
Different loaders can decide how to handle this flag on their own.
+.It Cm ifunc-noplt
+Do not emit PLT entries for ifunc symbols.
+Instead, emit text relocations referencing the resolver.
+This is an experimental optimization and only suitable for standalone
+environments where text relocations do not have the usual drawbacks.
+This option must be combined with the
+.Fl z Li notext
+option.
.It Cm initfirst
Sets the
.Dv DF_1_INITFIRST
diff --git a/docs/missingkeyfunction.rst b/docs/missingkeyfunction.rst
index 410c749c3b03..db4ea11b4a4f 100644
--- a/docs/missingkeyfunction.rst
+++ b/docs/missingkeyfunction.rst
@@ -1,26 +1,27 @@
-Missing Key Method
-==================
+Missing Key Function
+====================
If your build failed with a linker error something like this::
foo.cc:28: error: undefined reference to 'vtable for C'
- the vtable symbol may be undefined because the class is missing its key function (see https://lld.llvm.org/missingkeyfunction)
+ the vtable symbol may be undefined because the class is missing its key function
+ (see https://lld.llvm.org/missingkeyfunction)
it's likely that your class C has a key function (defined by the ABI as the first
-non-pure, non-inline, virtual method), but you haven't actually defined it.
+non-pure, non-inline, virtual function), but you haven't actually defined it.
When a class has a key function, the compiler emits the vtable (and some other
things as well) only in the translation unit that defines that key function. Thus,
if you're missing the key function, you'll also be missing the vtable. If no other
-function calls your missing method, you won't see any undefined reference errors
+function calls your missing function, you won't see any undefined reference errors
for it, but you will see undefined references to the vtable symbol.
-When a class has no non-pure, non-inline, virtual methods, there is no key
-method, and the compiler is forced to emit the vtable in every translation unit
+When a class has no non-pure, non-inline, virtual functions, there is no key
+function, and the compiler is forced to emit the vtable in every translation unit
that references the class. In this case, it is emitted in a COMDAT section,
which allows the linker to eliminate all duplicate copies. This is still
wasteful in terms of object file size and link time, so it's always advisable to
-ensure there is at least one eligible method that can serve as the key function.
+ensure there is at least one eligible function that can serve as the key function.
Here are the most common mistakes that lead to this error:
@@ -36,8 +37,8 @@ Say you have a base class declared in a header file::
...
};
-Here, ``~B`` is the first non-pure, non-inline, virtual method, so it is the key
-method. If you forget to define ``B::~B`` in your source file, the compiler will
+Here, ``~B`` is the first non-pure, non-inline, virtual function, so it is the key
+function. If you forget to define ``B::~B`` in your source file, the compiler will
not emit the vtable for ``B``, and you'll get an undefined reference to "vtable
for B".
@@ -47,10 +48,10 @@ the first eligible key function and it's easy to forget to implement them. It's
also more likely that you won't have any direct references to the destructor, so
you won't see any undefined reference errors that point directly to the problem.
-The solution in this case is to implement the missing method.
+The solution in this case is to implement the missing function.
-Forgetting to declare a virtual method in an abstract class as pure
--------------------------------------------------------------------
+Forgetting to declare a virtual function in an abstract class as pure
+---------------------------------------------------------------------
Say you have an abstract base class declared in a header file::
@@ -65,18 +66,18 @@ Say you have an abstract base class declared in a header file::
};
This base class is intended to be abstract, but you forgot to mark one of the
-methods pure. Here, ``A::bar``, being non-pure, is nominated as the key function,
+functions pure. Here, ``A::bar``, being non-pure, is nominated as the key function,
and as a result, the vtable for ``A`` is not emitted, because the compiler is
waiting for a translation unit that defines ``A::bar``.
The solution in this case is to add the missing ``= 0`` to the declaration of
``A::bar``.
-Key method is defined, but the linker doesn't see it
-----------------------------------------------------
+Key function is defined, but the linker doesn't see it
+------------------------------------------------------
It's also possible that you have defined the key function somewhere, but the
-object file containing the definition of that method isn't being linked into
+object file containing the definition of that function isn't being linked into
your application.
The solution in this case is to check your dependencies to make sure that
diff --git a/docs/partitions.dot b/docs/partitions.dot
new file mode 100644
index 000000000000..81f12a2f4283
--- /dev/null
+++ b/docs/partitions.dot
@@ -0,0 +1,22 @@
+digraph G {
+ part_main [label="Main partition",shape=plaintext];
+ part1 [label="Loadable partition 1",shape=plaintext];
+ part2 [label="Loadable partition 2",shape=plaintext];
+ main [style=filled,fillcolor=lightblue];
+ f1 [style=filled,fillcolor=lightsalmon];
+ f2 [style=filled,fillcolor=palegreen];
+ f3 [style=filled,fillcolor=lightblue];
+ f4 [style=filled,fillcolor=lightsalmon];
+ f5 [style=filled,fillcolor=lightblue];
+ f6 [style=filled,fillcolor=palegreen];
+ part_main -> main;
+ main -> f3;
+ part1 -> f1;
+ f1 -> f3;
+ f1 -> f4;
+ f1 -> f5;
+ part2 -> f2;
+ f2 -> f3;
+ f2 -> f5;
+ f2 -> f6;
+}
diff --git a/docs/partitions.svg b/docs/partitions.svg
new file mode 100644
index 000000000000..39cd96933446
--- /dev/null
+++ b/docs/partitions.svg
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.38.0 (20140413.2041)
+ -->
+<!-- Title: G Pages: 1 -->
+<svg width="393pt" height="188pt"
+ viewBox="0.00 0.00 393.00 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)">
+<title>G</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-184 389,-184 389,4 -4,4"/>
+<!-- part_main -->
+<g id="node1" class="node"><title>part_main</title>
+<text text-anchor="middle" x="47.5" y="-158.3" font-family="Times,serif" font-size="14.00">Main partition</text>
+</g>
+<!-- main -->
+<g id="node4" class="node"><title>main</title>
+<ellipse fill="lightblue" stroke="black" cx="75.5" cy="-90" rx="28.6953" ry="18"/>
+<text text-anchor="middle" x="75.5" y="-86.3" font-family="Times,serif" font-size="14.00">main</text>
+</g>
+<!-- part_main&#45;&gt;main -->
+<g id="edge1" class="edge"><title>part_main&#45;&gt;main</title>
+<path fill="none" stroke="black" d="M54.4214,-143.697C57.6218,-135.696 61.492,-126.02 65.0381,-117.155"/>
+<polygon fill="black" stroke="black" points="68.3868,-118.207 68.8511,-107.622 61.8874,-115.607 68.3868,-118.207"/>
+</g>
+<!-- part1 -->
+<g id="node2" class="node"><title>part1</title>
+<text text-anchor="middle" x="176.5" y="-158.3" font-family="Times,serif" font-size="14.00">Loadable partition 1</text>
+</g>
+<!-- f1 -->
+<g id="node5" class="node"><title>f1</title>
+<ellipse fill="lightsalmon" stroke="black" cx="176.5" cy="-90" rx="27" ry="18"/>
+<text text-anchor="middle" x="176.5" y="-86.3" font-family="Times,serif" font-size="14.00">f1</text>
+</g>
+<!-- part1&#45;&gt;f1 -->
+<g id="edge3" class="edge"><title>part1&#45;&gt;f1</title>
+<path fill="none" stroke="black" d="M176.5,-143.697C176.5,-135.983 176.5,-126.712 176.5,-118.112"/>
+<polygon fill="black" stroke="black" points="180,-118.104 176.5,-108.104 173,-118.104 180,-118.104"/>
+</g>
+<!-- part2 -->
+<g id="node3" class="node"><title>part2</title>
+<text text-anchor="middle" x="321.5" y="-158.3" font-family="Times,serif" font-size="14.00">Loadable partition 2</text>
+</g>
+<!-- f2 -->
+<g id="node6" class="node"><title>f2</title>
+<ellipse fill="palegreen" stroke="black" cx="284.5" cy="-90" rx="27" ry="18"/>
+<text text-anchor="middle" x="284.5" y="-86.3" font-family="Times,serif" font-size="14.00">f2</text>
+</g>
+<!-- part2&#45;&gt;f2 -->
+<g id="edge7" class="edge"><title>part2&#45;&gt;f2</title>
+<path fill="none" stroke="black" d="M312.354,-143.697C307.97,-135.403 302.636,-125.311 297.813,-116.187"/>
+<polygon fill="black" stroke="black" points="300.801,-114.35 293.034,-107.145 294.612,-117.621 300.801,-114.35"/>
+</g>
+<!-- f3 -->
+<g id="node7" class="node"><title>f3</title>
+<ellipse fill="lightblue" stroke="black" cx="104.5" cy="-18" rx="27" ry="18"/>
+<text text-anchor="middle" x="104.5" y="-14.3" font-family="Times,serif" font-size="14.00">f3</text>
+</g>
+<!-- main&#45;&gt;f3 -->
+<g id="edge2" class="edge"><title>main&#45;&gt;f3</title>
+<path fill="none" stroke="black" d="M82.3726,-72.411C85.7675,-64.2164 89.9422,-54.1395 93.7473,-44.9548"/>
+<polygon fill="black" stroke="black" points="97.0828,-46.0481 97.6767,-35.4699 90.6158,-43.3689 97.0828,-46.0481"/>
+</g>
+<!-- f1&#45;&gt;f3 -->
+<g id="edge4" class="edge"><title>f1&#45;&gt;f3</title>
+<path fill="none" stroke="black" d="M161.93,-74.8345C151.75,-64.9376 137.976,-51.5462 126.469,-40.3591"/>
+<polygon fill="black" stroke="black" points="128.905,-37.8461 119.296,-33.3847 124.026,-42.865 128.905,-37.8461"/>
+</g>
+<!-- f4 -->
+<g id="node8" class="node"><title>f4</title>
+<ellipse fill="lightsalmon" stroke="black" cx="176.5" cy="-18" rx="27" ry="18"/>
+<text text-anchor="middle" x="176.5" y="-14.3" font-family="Times,serif" font-size="14.00">f4</text>
+</g>
+<!-- f1&#45;&gt;f4 -->
+<g id="edge5" class="edge"><title>f1&#45;&gt;f4</title>
+<path fill="none" stroke="black" d="M176.5,-71.6966C176.5,-63.9827 176.5,-54.7125 176.5,-46.1124"/>
+<polygon fill="black" stroke="black" points="180,-46.1043 176.5,-36.1043 173,-46.1044 180,-46.1043"/>
+</g>
+<!-- f5 -->
+<g id="node9" class="node"><title>f5</title>
+<ellipse fill="lightblue" stroke="black" cx="248.5" cy="-18" rx="27" ry="18"/>
+<text text-anchor="middle" x="248.5" y="-14.3" font-family="Times,serif" font-size="14.00">f5</text>
+</g>
+<!-- f1&#45;&gt;f5 -->
+<g id="edge6" class="edge"><title>f1&#45;&gt;f5</title>
+<path fill="none" stroke="black" d="M191.07,-74.8345C201.25,-64.9376 215.024,-51.5462 226.531,-40.3591"/>
+<polygon fill="black" stroke="black" points="228.974,-42.865 233.704,-33.3847 224.095,-37.8461 228.974,-42.865"/>
+</g>
+<!-- f2&#45;&gt;f3 -->
+<g id="edge8" class="edge"><title>f2&#45;&gt;f3</title>
+<path fill="none" stroke="black" d="M260.806,-81.0022C232.063,-71.1346 182.266,-53.5073 140.5,-36 138.683,-35.2385 136.825,-34.4358 134.957,-33.6106"/>
+<polygon fill="black" stroke="black" points="136.231,-30.3452 125.68,-29.3829 133.328,-36.7149 136.231,-30.3452"/>
+</g>
+<!-- f2&#45;&gt;f5 -->
+<g id="edge9" class="edge"><title>f2&#45;&gt;f5</title>
+<path fill="none" stroke="black" d="M276.15,-72.7646C271.788,-64.2831 266.353,-53.7144 261.459,-44.1974"/>
+<polygon fill="black" stroke="black" points="264.49,-42.4395 256.804,-35.1473 258.265,-45.6409 264.49,-42.4395"/>
+</g>
+<!-- f6 -->
+<g id="node10" class="node"><title>f6</title>
+<ellipse fill="palegreen" stroke="black" cx="320.5" cy="-18" rx="27" ry="18"/>
+<text text-anchor="middle" x="320.5" y="-14.3" font-family="Times,serif" font-size="14.00">f6</text>
+</g>
+<!-- f2&#45;&gt;f6 -->
+<g id="edge10" class="edge"><title>f2&#45;&gt;f6</title>
+<path fill="none" stroke="black" d="M292.85,-72.7646C297.212,-64.2831 302.647,-53.7144 307.541,-44.1974"/>
+<polygon fill="black" stroke="black" points="310.735,-45.6409 312.196,-35.1473 304.51,-42.4395 310.735,-45.6409"/>
+</g>
+</g>
+</svg>
diff --git a/docs/sphinx_intro.rst b/docs/sphinx_intro.rst
index 6bb9816b5ab4..b671cdc3df64 100644
--- a/docs/sphinx_intro.rst
+++ b/docs/sphinx_intro.rst
@@ -43,8 +43,8 @@ to install it using:
Use your distribution's standard package management tool to install it,
i.e., ``apt-get install easy_install`` or ``yum install easy_install``.
- Mac OS X
- All modern Mac OS X systems come with ``easy_install`` as part of the base
+ macOS
+ All modern macOS systems come with ``easy_install`` as part of the base
system.
Windows
diff --git a/include/lld/Common/Args.h b/include/lld/Common/Args.h
index 769d4840cf06..b3c8686e57e2 100644
--- a/include/lld/Common/Args.h
+++ b/include/lld/Common/Args.h
@@ -1,9 +1,8 @@
//===- Args.h ---------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -11,6 +10,7 @@
#define LLD_ARGS_H
#include "lld/Common/LLVM.h"
+#include "llvm/Support/CodeGen.h"
#include "llvm/Support/MemoryBuffer.h"
#include <vector>
@@ -22,15 +22,20 @@ class InputArgList;
namespace lld {
namespace args {
-int getInteger(llvm::opt::InputArgList &Args, unsigned Key, int Default);
-std::vector<StringRef> getStrings(llvm::opt::InputArgList &Args, int Id);
-uint64_t getZOptionValue(llvm::opt::InputArgList &Args, int Id, StringRef Key,
+llvm::CodeGenOpt::Level getCGOptLevel(int optLevelLTO);
+
+int64_t getInteger(llvm::opt::InputArgList &args, unsigned key,
+ int64_t Default);
+
+std::vector<StringRef> getStrings(llvm::opt::InputArgList &args, int id);
+
+uint64_t getZOptionValue(llvm::opt::InputArgList &args, int id, StringRef key,
uint64_t Default);
-std::vector<StringRef> getLines(MemoryBufferRef MB);
+std::vector<StringRef> getLines(MemoryBufferRef mb);
-StringRef getFilenameWithoutExe(StringRef Path);
+StringRef getFilenameWithoutExe(StringRef path);
} // namespace args
} // namespace lld
diff --git a/include/lld/Common/Driver.h b/include/lld/Common/Driver.h
index f6d92933af62..e5595952ad22 100644
--- a/include/lld/Common/Driver.h
+++ b/include/lld/Common/Driver.h
@@ -1,9 +1,8 @@
//===- lld/Common/Driver.h - Linker Driver Emulator -----------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -15,28 +14,28 @@
namespace lld {
namespace coff {
-bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
- llvm::raw_ostream &Diag = llvm::errs());
+bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
+ llvm::raw_ostream &diag = llvm::errs());
}
namespace mingw {
-bool link(llvm::ArrayRef<const char *> Args,
- llvm::raw_ostream &Diag = llvm::errs());
+bool link(llvm::ArrayRef<const char *> args,
+ llvm::raw_ostream &diag = llvm::errs());
}
namespace elf {
-bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
- llvm::raw_ostream &Diag = llvm::errs());
+bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
+ llvm::raw_ostream &diag = llvm::errs());
}
namespace mach_o {
-bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
- llvm::raw_ostream &Diag = llvm::errs());
+bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
+ llvm::raw_ostream &diag = llvm::errs());
}
namespace wasm {
-bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
- llvm::raw_ostream &Diag = llvm::errs());
+bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
+ llvm::raw_ostream &diag = llvm::errs());
}
}
diff --git a/include/lld/Common/ErrorHandler.h b/include/lld/Common/ErrorHandler.h
index c169f7b50de8..7126a7bf410a 100644
--- a/include/lld/Common/ErrorHandler.h
+++ b/include/lld/Common/ErrorHandler.h
@@ -1,9 +1,8 @@
//===- ErrorHandler.h -------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -83,74 +82,75 @@ namespace lld {
class ErrorHandler {
public:
- uint64_t ErrorCount = 0;
- uint64_t ErrorLimit = 20;
- StringRef ErrorLimitExceededMsg = "too many errors emitted, stopping now";
- StringRef LogName = "lld";
- llvm::raw_ostream *ErrorOS = &llvm::errs();
- bool ColorDiagnostics = llvm::errs().has_colors();
- bool ExitEarly = true;
- bool FatalWarnings = false;
- bool Verbose = false;
-
- void error(const Twine &Msg);
- LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
- void log(const Twine &Msg);
- void message(const Twine &Msg);
- void warn(const Twine &Msg);
-
- std::unique_ptr<llvm::FileOutputBuffer> OutputBuffer;
+ uint64_t errorCount = 0;
+ uint64_t errorLimit = 20;
+ StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
+ StringRef logName = "lld";
+ llvm::raw_ostream *errorOS = &llvm::errs();
+ bool colorDiagnostics = llvm::errs().has_colors();
+ bool exitEarly = true;
+ bool fatalWarnings = false;
+ bool verbose = false;
+ bool vsDiagnostics = false;
+
+ void error(const Twine &msg);
+ LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg);
+ void log(const Twine &msg);
+ void message(const Twine &msg);
+ void warn(const Twine &msg);
+
+ std::unique_ptr<llvm::FileOutputBuffer> outputBuffer;
private:
- void print(StringRef S, raw_ostream::Colors C);
+ void printHeader(StringRef s, raw_ostream::Colors c, const Twine &msg);
};
/// Returns the default error handler.
ErrorHandler &errorHandler();
-inline void error(const Twine &Msg) { errorHandler().error(Msg); }
-inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg) {
- errorHandler().fatal(Msg);
+inline void error(const Twine &msg) { errorHandler().error(msg); }
+inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) {
+ errorHandler().fatal(msg);
}
-inline void log(const Twine &Msg) { errorHandler().log(Msg); }
-inline void message(const Twine &Msg) { errorHandler().message(Msg); }
-inline void warn(const Twine &Msg) { errorHandler().warn(Msg); }
-inline uint64_t errorCount() { return errorHandler().ErrorCount; }
+inline void log(const Twine &msg) { errorHandler().log(msg); }
+inline void message(const Twine &msg) { errorHandler().message(msg); }
+inline void warn(const Twine &msg) { errorHandler().warn(msg); }
+inline uint64_t errorCount() { return errorHandler().errorCount; }
-LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
+LLVM_ATTRIBUTE_NORETURN void exitLld(int val);
-void diagnosticHandler(const llvm::DiagnosticInfo &DI);
-void checkError(Error E);
+void diagnosticHandler(const llvm::DiagnosticInfo &di);
+void checkError(Error e);
// check functions are convenient functions to strip errors
// from error-or-value objects.
-template <class T> T check(ErrorOr<T> E) {
- if (auto EC = E.getError())
- fatal(EC.message());
- return std::move(*E);
+template <class T> T check(ErrorOr<T> e) {
+ if (auto ec = e.getError())
+ fatal(ec.message());
+ return std::move(*e);
}
-template <class T> T check(Expected<T> E) {
- if (!E)
- fatal(llvm::toString(E.takeError()));
- return std::move(*E);
+template <class T> T check(Expected<T> e) {
+ if (!e)
+ fatal(llvm::toString(e.takeError()));
+ return std::move(*e);
}
template <class T>
-T check2(ErrorOr<T> E, llvm::function_ref<std::string()> Prefix) {
- if (auto EC = E.getError())
- fatal(Prefix() + ": " + EC.message());
- return std::move(*E);
+T check2(ErrorOr<T> e, llvm::function_ref<std::string()> prefix) {
+ if (auto ec = e.getError())
+ fatal(prefix() + ": " + ec.message());
+ return std::move(*e);
}
template <class T>
-T check2(Expected<T> E, llvm::function_ref<std::string()> Prefix) {
- if (!E)
- fatal(Prefix() + ": " + toString(E.takeError()));
- return std::move(*E);
+T check2(Expected<T> e, llvm::function_ref<std::string()> prefix) {
+ if (!e)
+ fatal(prefix() + ": " + toString(e.takeError()));
+ return std::move(*e);
}
-inline std::string toString(const Twine &S) { return S.str(); }
+inline std::string toString(const Twine &s) { return s.str(); }
// To evaluate the second argument lazily, we use C macro.
#define CHECK(E, S) check2((E), [&] { return toString(S); })
diff --git a/include/lld/Common/Filesystem.h b/include/lld/Common/Filesystem.h
new file mode 100644
index 000000000000..63a0f554a06c
--- /dev/null
+++ b/include/lld/Common/Filesystem.h
@@ -0,0 +1,20 @@
+//===- Filesystem.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_FILESYSTEM_H
+#define LLD_FILESYSTEM_H
+
+#include "lld/Common/LLVM.h"
+#include <system_error>
+
+namespace lld {
+void unlinkAsync(StringRef path);
+std::error_code tryCreateFile(StringRef path);
+} // namespace lld
+
+#endif
diff --git a/include/lld/Common/LLVM.h b/include/lld/Common/LLVM.h
index 95a2aa903957..f7ed1d793ca7 100644
--- a/include/lld/Common/LLVM.h
+++ b/include/lld/Common/LLVM.h
@@ -1,9 +1,8 @@
//===--- LLVM.h - Import various common LLVM datatypes ----------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -30,6 +29,7 @@ class Twine;
class MemoryBuffer;
class MemoryBufferRef;
template <typename T> class ArrayRef;
+template <typename T> class MutableArrayRef;
template <unsigned InternalLen> class SmallString;
template <typename T, unsigned N> class SmallVector;
template <typename T> class ErrorOr;
@@ -63,6 +63,7 @@ using llvm::isa;
// ADT's.
using llvm::ArrayRef;
+using llvm::MutableArrayRef;
using llvm::Error;
using llvm::ErrorOr;
using llvm::Expected;
diff --git a/include/lld/Common/Memory.h b/include/lld/Common/Memory.h
index 699f7c1654cd..41d8f15d7b34 100644
--- a/include/lld/Common/Memory.h
+++ b/include/lld/Common/Memory.h
@@ -1,9 +1,8 @@
//===- Memory.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -29,30 +28,30 @@
namespace lld {
// Use this arena if your object doesn't have a destructor.
-extern llvm::BumpPtrAllocator BAlloc;
-extern llvm::StringSaver Saver;
+extern llvm::BumpPtrAllocator bAlloc;
+extern llvm::StringSaver saver;
void freeArena();
// These two classes are hack to keep track of all
// SpecificBumpPtrAllocator instances.
struct SpecificAllocBase {
- SpecificAllocBase() { Instances.push_back(this); }
+ SpecificAllocBase() { instances.push_back(this); }
virtual ~SpecificAllocBase() = default;
virtual void reset() = 0;
- static std::vector<SpecificAllocBase *> Instances;
+ static std::vector<SpecificAllocBase *> instances;
};
template <class T> struct SpecificAlloc : public SpecificAllocBase {
- void reset() override { Alloc.DestroyAll(); }
- llvm::SpecificBumpPtrAllocator<T> Alloc;
+ void reset() override { alloc.DestroyAll(); }
+ llvm::SpecificBumpPtrAllocator<T> 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)...);
+template <typename T, typename... U> T *make(U &&... args) {
+ static SpecificAlloc<T> alloc;
+ return new (alloc.alloc.Allocate()) T(std::forward<U>(args)...);
}
} // namespace lld
diff --git a/include/lld/Common/Reproduce.h b/include/lld/Common/Reproduce.h
index 0f425de269c7..734d9e4011d1 100644
--- a/include/lld/Common/Reproduce.h
+++ b/include/lld/Common/Reproduce.h
@@ -1,9 +1,8 @@
//===- Reproduce.h - Utilities for creating reproducers ---------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -23,17 +22,13 @@ namespace lld {
// Makes a given pathname an absolute path first, and then remove
// beginning /. For example, "../foo.o" is converted to "home/john/foo.o",
// assuming that the current directory is "/home/john/bar".
-std::string relativeToRoot(StringRef Path);
+std::string relativeToRoot(StringRef path);
// Quote a given string if it contains a space character.
-std::string quote(StringRef S);
-
-// Rewrite the given path if a file exists with that pathname, otherwise
-// returns the original path.
-std::string rewritePath(StringRef S);
+std::string quote(StringRef s);
// Returns the string form of the given argument.
-std::string toString(const llvm::opt::Arg &Arg);
+std::string toString(const llvm::opt::Arg &arg);
}
#endif
diff --git a/include/lld/Common/Strings.h b/include/lld/Common/Strings.h
index 566030e43aa6..ded22dd769be 100644
--- a/include/lld/Common/Strings.h
+++ b/include/lld/Common/Strings.h
@@ -1,9 +1,8 @@
//===- Strings.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -20,25 +19,25 @@
namespace lld {
// Returns a demangled C++ symbol name. If Name is not a mangled
// name, it returns Optional::None.
-llvm::Optional<std::string> demangleItanium(llvm::StringRef Name);
-llvm::Optional<std::string> demangleMSVC(llvm::StringRef S);
+llvm::Optional<std::string> demangleItanium(llvm::StringRef name);
+llvm::Optional<std::string> demangleMSVC(llvm::StringRef s);
-std::vector<uint8_t> parseHex(llvm::StringRef S);
-bool isValidCIdentifier(llvm::StringRef S);
+std::vector<uint8_t> parseHex(llvm::StringRef s);
+bool isValidCIdentifier(llvm::StringRef s);
// Write the contents of the a buffer to a file
-void saveBuffer(llvm::StringRef Buffer, const llvm::Twine &Path);
+void saveBuffer(llvm::StringRef buffer, const llvm::Twine &path);
// This class represents multiple glob patterns.
class StringMatcher {
public:
StringMatcher() = default;
- explicit StringMatcher(llvm::ArrayRef<llvm::StringRef> Pat);
+ explicit StringMatcher(llvm::ArrayRef<llvm::StringRef> pat);
- bool match(llvm::StringRef S) const;
+ bool match(llvm::StringRef s) const;
private:
- std::vector<llvm::GlobPattern> Patterns;
+ std::vector<llvm::GlobPattern> patterns;
};
} // namespace lld
diff --git a/include/lld/Common/TargetOptionsCommandFlags.h b/include/lld/Common/TargetOptionsCommandFlags.h
index 2eaecb72759e..9345e616f9a9 100644
--- a/include/lld/Common/TargetOptionsCommandFlags.h
+++ b/include/lld/Common/TargetOptionsCommandFlags.h
@@ -1,9 +1,8 @@
//===-- TargetOptionsCommandFlags.h ----------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -16,8 +15,8 @@
#include "llvm/Target/TargetOptions.h"
namespace lld {
-llvm::TargetOptions InitTargetOptionsFromCodeGenFlags();
-llvm::Optional<llvm::CodeModel::Model> GetCodeModelFromCMModel();
-std::string GetCPUStr();
-std::vector<std::string> GetMAttrs();
+llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
+llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
+std::string getCPUStr();
+std::vector<std::string> getMAttrs();
}
diff --git a/include/lld/Common/Threads.h b/include/lld/Common/Threads.h
index 1425abd12922..7834130fdf72 100644
--- a/include/lld/Common/Threads.h
+++ b/include/lld/Common/Threads.h
@@ -1,9 +1,8 @@
//===- Threads.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -64,21 +63,28 @@
namespace lld {
-extern bool ThreadsEnabled;
+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);
+}
-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);
+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(llvm::parallel::seq, std::begin(Range), std::end(Range), Fn);
+ for_each_n(llvm::parallel::seq, begin, end, 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);
+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
- for_each_n(llvm::parallel::seq, Begin, End, Fn);
+ sort(llvm::parallel::seq, std::begin(range), std::end(range), fn);
}
} // namespace lld
diff --git a/include/lld/Common/Timer.h b/include/lld/Common/Timer.h
index 6654af626919..4a298b04a30b 100644
--- a/include/lld/Common/Timer.h
+++ b/include/lld/Common/Timer.h
@@ -1,9 +1,8 @@
//===- Timer.h ----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -22,18 +21,18 @@ namespace lld {
class Timer;
struct ScopedTimer {
- explicit ScopedTimer(Timer &T);
+ explicit ScopedTimer(Timer &t);
~ScopedTimer();
void stop();
- Timer *T = nullptr;
+ Timer *t = nullptr;
};
class Timer {
public:
- Timer(llvm::StringRef Name, Timer &Parent);
+ Timer(llvm::StringRef name, Timer &parent);
static Timer &root();
@@ -44,14 +43,14 @@ public:
double millis() const;
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::vector<Timer *> Children;
- std::string Name;
- Timer *Parent;
+ 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::vector<Timer *> children;
+ std::string name;
+ Timer *parent;
};
} // namespace lld
diff --git a/include/lld/Common/Version.h b/include/lld/Common/Version.h
index 23a10ed6dbf3..9571aa2743e5 100644
--- a/include/lld/Common/Version.h
+++ b/include/lld/Common/Version.h
@@ -1,9 +1,8 @@
//===- lld/Common/Version.h - LLD Version Number ----------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/include/lld/Core/AbsoluteAtom.h b/include/lld/Core/AbsoluteAtom.h
index ed25297cea81..5214b418f3d5 100644
--- a/include/lld/Core/AbsoluteAtom.h
+++ b/include/lld/Core/AbsoluteAtom.h
@@ -1,9 +1,8 @@
//===- Core/AbsoluteAtom.h - An absolute Atom -----------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/ArchiveLibraryFile.h b/include/lld/Core/ArchiveLibraryFile.h
index 2c736e7d6c61..0abef40447b9 100644
--- a/include/lld/Core/ArchiveLibraryFile.h
+++ b/include/lld/Core/ArchiveLibraryFile.h
@@ -1,9 +1,8 @@
//===- Core/ArchiveLibraryFile.h - Models static library ------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Atom.h b/include/lld/Core/Atom.h
index 149c3d5ee2c5..4d3d27a2af57 100644
--- a/include/lld/Core/Atom.h
+++ b/include/lld/Core/Atom.h
@@ -1,9 +1,8 @@
//===- Core/Atom.h - A node in linking graph --------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/DefinedAtom.h b/include/lld/Core/DefinedAtom.h
index ba10b45411f1..4b1de7ffec11 100644
--- a/include/lld/Core/DefinedAtom.h
+++ b/include/lld/Core/DefinedAtom.h
@@ -1,9 +1,8 @@
//===- Core/DefinedAtom.h - An Atom with content --------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Error.h b/include/lld/Core/Error.h
index 36a36724987a..c18fe96d268c 100644
--- a/include/lld/Core/Error.h
+++ b/include/lld/Core/Error.h
@@ -1,9 +1,8 @@
//===- Error.h - system_error extensions for lld ----------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/include/lld/Core/File.h b/include/lld/Core/File.h
index 54f533576a4b..492f35989f16 100644
--- a/include/lld/Core/File.h
+++ b/include/lld/Core/File.h
@@ -1,9 +1,8 @@
//===- Core/File.h - A Container of Atoms ---------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Instrumentation.h b/include/lld/Core/Instrumentation.h
index 939d64557587..602a37aff57c 100644
--- a/include/lld/Core/Instrumentation.h
+++ b/include/lld/Core/Instrumentation.h
@@ -1,9 +1,8 @@
//===- include/Core/Instrumentation.h - Instrumentation API ---------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/include/lld/Core/LinkingContext.h b/include/lld/Core/LinkingContext.h
index 52ab1a2480e8..e090ff990231 100644
--- a/include/lld/Core/LinkingContext.h
+++ b/include/lld/Core/LinkingContext.h
@@ -1,9 +1,8 @@
//===- lld/Core/LinkingContext.h - Linker Target Info Interface -*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Node.h b/include/lld/Core/Node.h
index c30482409e7a..a224793d7824 100644
--- a/include/lld/Core/Node.h
+++ b/include/lld/Core/Node.h
@@ -1,9 +1,8 @@
//===- lld/Core/Node.h - Input file class -----------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/include/lld/Core/Pass.h b/include/lld/Core/Pass.h
index bfe3f9b10e0c..57d5a40e082a 100644
--- a/include/lld/Core/Pass.h
+++ b/include/lld/Core/Pass.h
@@ -1,9 +1,8 @@
//===------ Core/Pass.h - Base class for linker passes ----------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/PassManager.h b/include/lld/Core/PassManager.h
index f2ef10f406f2..02499d09caee 100644
--- a/include/lld/Core/PassManager.h
+++ b/include/lld/Core/PassManager.h
@@ -1,9 +1,8 @@
//===- lld/Core/PassManager.h - Manage linker passes ----------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Reader.h b/include/lld/Core/Reader.h
index 6cf6282ff39c..a2d7912d7413 100644
--- a/include/lld/Core/Reader.h
+++ b/include/lld/Core/Reader.h
@@ -1,9 +1,8 @@
//===- lld/Core/Reader.h - Abstract File Format Reading Interface ---------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Reference.h b/include/lld/Core/Reference.h
index 1d3003c84616..191e0f07ec38 100644
--- a/include/lld/Core/Reference.h
+++ b/include/lld/Core/Reference.h
@@ -1,9 +1,8 @@
//===- Core/References.h - A Reference to Another Atom ----------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Resolver.h b/include/lld/Core/Resolver.h
index 5157c9fddc1a..9ab219509459 100644
--- a/include/lld/Core/Resolver.h
+++ b/include/lld/Core/Resolver.h
@@ -1,9 +1,8 @@
//===- Core/Resolver.h - Resolves Atom References -------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/SharedLibraryAtom.h b/include/lld/Core/SharedLibraryAtom.h
index 7fec7a3e3d29..171d154bc055 100644
--- a/include/lld/Core/SharedLibraryAtom.h
+++ b/include/lld/Core/SharedLibraryAtom.h
@@ -1,9 +1,8 @@
//===- Core/SharedLibraryAtom.h - A Shared Library Atom -------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/SharedLibraryFile.h b/include/lld/Core/SharedLibraryFile.h
index 53bf967b0236..846d1f22f17a 100644
--- a/include/lld/Core/SharedLibraryFile.h
+++ b/include/lld/Core/SharedLibraryFile.h
@@ -1,9 +1,8 @@
//===- Core/SharedLibraryFile.h - Models shared libraries as Atoms --------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Simple.h b/include/lld/Core/Simple.h
index feeed6ae473b..f211beb9953f 100644
--- a/include/lld/Core/Simple.h
+++ b/include/lld/Core/Simple.h
@@ -1,9 +1,8 @@
//===- lld/Core/Simple.h - Simple implementations of Atom and File --------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/include/lld/Core/SymbolTable.h b/include/lld/Core/SymbolTable.h
index 156c56eafbf7..c7502a45d7c3 100644
--- a/include/lld/Core/SymbolTable.h
+++ b/include/lld/Core/SymbolTable.h
@@ -1,9 +1,8 @@
//===- Core/SymbolTable.h - Main Symbol Table -----------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/UndefinedAtom.h b/include/lld/Core/UndefinedAtom.h
index f45d6ecda6b0..a40db36519ec 100644
--- a/include/lld/Core/UndefinedAtom.h
+++ b/include/lld/Core/UndefinedAtom.h
@@ -1,9 +1,8 @@
//===- Core/UndefinedAtom.h - An Undefined Atom ---------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/Core/Writer.h b/include/lld/Core/Writer.h
index 1cdfabefebd7..0de5fca32bce 100644
--- a/include/lld/Core/Writer.h
+++ b/include/lld/Core/Writer.h
@@ -1,9 +1,8 @@
//===- lld/Core/Writer.h - Abstract File Format Interface -----------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/ReaderWriter/MachOLinkingContext.h b/include/lld/ReaderWriter/MachOLinkingContext.h
index fde65880c3e3..f48ad77053e5 100644
--- a/include/lld/ReaderWriter/MachOLinkingContext.h
+++ b/include/lld/ReaderWriter/MachOLinkingContext.h
@@ -1,9 +1,8 @@
//===- lld/ReaderWriter/MachOLinkingContext.h -----------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/include/lld/ReaderWriter/YamlContext.h b/include/lld/ReaderWriter/YamlContext.h
index b97d21f68e55..dc133e3627de 100644
--- a/include/lld/ReaderWriter/YamlContext.h
+++ b/include/lld/ReaderWriter/YamlContext.h
@@ -1,9 +1,8 @@
//===- lld/ReaderWriter/YamlContext.h - object used in YAML I/O context ---===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/DefinedAtom.cpp b/lib/Core/DefinedAtom.cpp
index 177cae7fcbf0..3c1eece16841 100644
--- a/lib/Core/DefinedAtom.cpp
+++ b/lib/Core/DefinedAtom.cpp
@@ -1,9 +1,8 @@
//===- DefinedAtom.cpp ------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/Error.cpp b/lib/Core/Error.cpp
index 6fc76f7ca3d0..f138a81efaab 100644
--- a/lib/Core/Error.cpp
+++ b/lib/Core/Error.cpp
@@ -1,9 +1,8 @@
//===- Error.cpp - system_error extensions for lld --------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/File.cpp b/lib/Core/File.cpp
index 30ded091a92a..ce33923c136e 100644
--- a/lib/Core/File.cpp
+++ b/lib/Core/File.cpp
@@ -1,9 +1,8 @@
//===- Core/File.cpp - A Container of Atoms -------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/LinkingContext.cpp b/lib/Core/LinkingContext.cpp
index 0f225c322122..911ae606678d 100644
--- a/lib/Core/LinkingContext.cpp
+++ b/lib/Core/LinkingContext.cpp
@@ -1,9 +1,8 @@
//===- lib/Core/LinkingContext.cpp - Linker Context Object Interface ------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/Reader.cpp b/lib/Core/Reader.cpp
index 5d8bbbbfe4d7..3592d87ce627 100644
--- a/lib/Core/Reader.cpp
+++ b/lib/Core/Reader.cpp
@@ -1,9 +1,8 @@
//===- lib/Core/Reader.cpp ------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/Resolver.cpp b/lib/Core/Resolver.cpp
index 9c51c6cdb19c..7e6d95f8223f 100644
--- a/lib/Core/Resolver.cpp
+++ b/lib/Core/Resolver.cpp
@@ -1,9 +1,8 @@
//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/SymbolTable.cpp b/lib/Core/SymbolTable.cpp
index 51ae8d17181d..55cc27c38a62 100644
--- a/lib/Core/SymbolTable.cpp
+++ b/lib/Core/SymbolTable.cpp
@@ -1,9 +1,8 @@
//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Core/Writer.cpp b/lib/Core/Writer.cpp
index 51f95bc5053a..12788b187e11 100644
--- a/lib/Core/Writer.cpp
+++ b/lib/Core/Writer.cpp
@@ -1,9 +1,8 @@
//===- lib/Core/Writer.cpp ------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp
index bbac230df453..8646d86c08e4 100644
--- a/lib/Driver/DarwinLdDriver.cpp
+++ b/lib/Driver/DarwinLdDriver.cpp
@@ -1,9 +1,8 @@
//===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
@@ -334,8 +333,8 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
Twine(unknownArg->getAsString(parsedArgs)));
}
- errorHandler().Verbose = parsedArgs.hasArg(OPT_v);
- errorHandler().ErrorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20);
+ errorHandler().verbose = parsedArgs.hasArg(OPT_v);
+ errorHandler().errorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20);
// Figure out output kind ( -dylib, -r, -bundle, -preload, or -static )
llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE;
@@ -638,7 +637,7 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
// Now that we've constructed the final set of search paths, print out those
// search paths in verbose mode.
- if (errorHandler().Verbose) {
+ if (errorHandler().verbose) {
message("Library search paths:");
for (auto path : ctx.searchDirs()) {
message(" " + path);
@@ -1146,13 +1145,13 @@ static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
/// This is where the link is actually performed.
bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
raw_ostream &Error) {
- errorHandler().LogName = args::getFilenameWithoutExe(args[0]);
- errorHandler().ErrorLimitExceededMsg =
+ errorHandler().logName = args::getFilenameWithoutExe(args[0]);
+ errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now (use "
"'-error-limit 0' to see all errors)";
- errorHandler().ErrorOS = &Error;
- errorHandler().ExitEarly = CanExitEarly;
- errorHandler().ColorDiagnostics = Error.has_colors();
+ errorHandler().errorOS = &Error;
+ errorHandler().exitEarly = CanExitEarly;
+ errorHandler().colorDiagnostics = Error.has_colors();
MachOLinkingContext ctx;
if (!parse(args, ctx))
@@ -1197,9 +1196,9 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
if (auto ec = pm.runOnFile(*merged)) {
// FIXME: This should be passed to logAllUnhandledErrors but it needs
// to be passed a Twine instead of a string.
- *errorHandler().ErrorOS << "Failed to run passes on file '"
+ *errorHandler().errorOS << "Failed to run passes on file '"
<< ctx.outputPath() << "': ";
- logAllUnhandledErrors(std::move(ec), *errorHandler().ErrorOS,
+ logAllUnhandledErrors(std::move(ec), *errorHandler().errorOS,
std::string());
return false;
}
@@ -1211,9 +1210,9 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
if (auto ec = ctx.writeFile(*merged)) {
// FIXME: This should be passed to logAllUnhandledErrors but it needs
// to be passed a Twine instead of a string.
- *errorHandler().ErrorOS << "Failed to write file '" << ctx.outputPath()
+ *errorHandler().errorOS << "Failed to write file '" << ctx.outputPath()
<< "': ";
- logAllUnhandledErrors(std::move(ec), *errorHandler().ErrorOS,
+ logAllUnhandledErrors(std::move(ec), *errorHandler().errorOS,
std::string());
return false;
}
diff --git a/lib/ReaderWriter/FileArchive.cpp b/lib/ReaderWriter/FileArchive.cpp
index 2f52d9d34312..b09bf34dc47c 100644
--- a/lib/ReaderWriter/FileArchive.cpp
+++ b/lib/ReaderWriter/FileArchive.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ArchHandler.cpp b/lib/ReaderWriter/MachO/ArchHandler.cpp
index cb20907b3e30..c101f3b157bb 100644
--- a/lib/ReaderWriter/MachO/ArchHandler.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler.cpp
@@ -1,9 +1,8 @@
//===- lib/FileFormat/MachO/ArchHandler.cpp -------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ArchHandler.h b/lib/ReaderWriter/MachO/ArchHandler.h
index 80840b561701..83646c09b1a8 100644
--- a/lib/ReaderWriter/MachO/ArchHandler.h
+++ b/lib/ReaderWriter/MachO/ArchHandler.h
@@ -1,9 +1,8 @@
//===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
index 2f663c660f5c..06c98ac06fd1 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
@@ -1,9 +1,8 @@
//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
index b9c815c5a320..a424edf4985a 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
@@ -1,9 +1,8 @@
//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
index a2c68092724d..6ea8e8c42e80 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
@@ -1,9 +1,8 @@
//===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
index fba3d530e484..316b5bbc6f4f 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
@@ -1,9 +1,8 @@
//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/Atoms.h b/lib/ReaderWriter/MachO/Atoms.h
index 573efca9f6f9..b8bca1959cfb 100644
--- a/lib/ReaderWriter/MachO/Atoms.h
+++ b/lib/ReaderWriter/MachO/Atoms.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/Atoms.h ---------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
index fa0aaa103eeb..de5adb088799 100644
--- a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
+++ b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
@@ -388,12 +387,9 @@ private:
// Gather the personality functions now, so that they're in deterministic
// order (derived from the DefinedAtom order).
- if (unwindEntry.personalityFunction) {
- auto pFunc = std::find(personalities.begin(), personalities.end(),
- unwindEntry.personalityFunction);
- if (pFunc == personalities.end())
- personalities.push_back(unwindEntry.personalityFunction);
- }
+ if (unwindEntry.personalityFunction &&
+ !llvm::count(personalities, unwindEntry.personalityFunction))
+ personalities.push_back(unwindEntry.personalityFunction);
}
}
@@ -552,8 +548,7 @@ private:
}
}
- auto personality = std::find(personalities.begin(), personalities.end(),
- entry.personalityFunction);
+ auto personality = llvm::find(personalities, entry.personalityFunction);
uint32_t personalityIdx = personality == personalities.end()
? 0
: personality - personalities.begin() + 1;
diff --git a/lib/ReaderWriter/MachO/DebugInfo.h b/lib/ReaderWriter/MachO/DebugInfo.h
index 28e41bf4263c..959e10f9a073 100644
--- a/lib/ReaderWriter/MachO/DebugInfo.h
+++ b/lib/ReaderWriter/MachO/DebugInfo.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ExecutableAtoms.h b/lib/ReaderWriter/MachO/ExecutableAtoms.h
index ab14e6d8c3e7..ce94be457026 100644
--- a/lib/ReaderWriter/MachO/ExecutableAtoms.h
+++ b/lib/ReaderWriter/MachO/ExecutableAtoms.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/ExecutableAtoms.h ---------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/File.h b/lib/ReaderWriter/MachO/File.h
index 2bdd6342b477..1cc1c4109dce 100644
--- a/lib/ReaderWriter/MachO/File.h
+++ b/lib/ReaderWriter/MachO/File.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/FlatNamespaceFile.h b/lib/ReaderWriter/MachO/FlatNamespaceFile.h
index 7ccd4f19f834..1885effef49f 100644
--- a/lib/ReaderWriter/MachO/FlatNamespaceFile.h
+++ b/lib/ReaderWriter/MachO/FlatNamespaceFile.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/FlatNamespaceFile.h -------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp
index 49e6f88d4aa4..bc66d49eafb9 100644
--- a/lib/ReaderWriter/MachO/GOTPass.cpp
+++ b/lib/ReaderWriter/MachO/GOTPass.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/ReaderWriter/MachO/LayoutPass.cpp b/lib/ReaderWriter/MachO/LayoutPass.cpp
index 9058e4f562e2..2718dfcf743f 100644
--- a/lib/ReaderWriter/MachO/LayoutPass.cpp
+++ b/lib/ReaderWriter/MachO/LayoutPass.cpp
@@ -1,9 +1,8 @@
//===-- ReaderWriter/MachO/LayoutPass.cpp - Layout atoms ------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/LayoutPass.h b/lib/ReaderWriter/MachO/LayoutPass.h
index c18777eded0a..904e16b7fb0e 100644
--- a/lib/ReaderWriter/MachO/LayoutPass.h
+++ b/lib/ReaderWriter/MachO/LayoutPass.h
@@ -1,9 +1,8 @@
//===------ lib/ReaderWriter/MachO/LayoutPass.h - Handles Layout of atoms -===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
index 61583963ddd7..38456024c9f8 100644
--- a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
+++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachOLinkingContext.cpp ---------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -768,8 +767,7 @@ void MachOLinkingContext::registerDylib(MachODylibFile *dylib,
bool upward) const {
std::lock_guard<std::mutex> lock(_dylibsMutex);
- if (std::find(_allDylibs.begin(),
- _allDylibs.end(), dylib) == _allDylibs.end())
+ if (!llvm::count(_allDylibs, dylib))
_allDylibs.push_back(dylib);
_pathToDylibMap[dylib->installName()] = dylib;
// If path is different than install name, register path too.
@@ -1016,11 +1014,10 @@ static bool isLibrary(const std::unique_ptr<Node> &elem) {
// new undefines from libraries.
void MachOLinkingContext::finalizeInputFiles() {
std::vector<std::unique_ptr<Node>> &elements = getNodes();
- std::stable_sort(elements.begin(), elements.end(),
- [](const std::unique_ptr<Node> &a,
- const std::unique_ptr<Node> &b) {
- return !isLibrary(a) && isLibrary(b);
- });
+ llvm::stable_sort(elements, [](const std::unique_ptr<Node> &a,
+ const std::unique_ptr<Node> &b) {
+ return !isLibrary(a) && isLibrary(b);
+ });
size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
elements.push_back(llvm::make_unique<GroupEnd>(numLibs));
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lib/ReaderWriter/MachO/MachONormalizedFile.h
index 7eeb8adbd84f..3ef2949addab 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFile.h
+++ b/lib/ReaderWriter/MachO/MachONormalizedFile.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFile.h -----------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -288,7 +287,8 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb,
/// Takes in-memory normalized view and writes a mach-o object file.
llvm::Error writeBinary(const NormalizedFile &file, StringRef path);
-size_t headerAndLoadCommandsSize(const NormalizedFile &file);
+size_t headerAndLoadCommandsSize(const NormalizedFile &file,
+ bool includeFunctionStarts);
/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index 7c2e833c090f..38b365374f36 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp ---------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
index ee9e174b82e0..aeb04ef4508a 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h ------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
index 7ef0237e8c36..da27c7cadf96 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp ---------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -109,7 +108,7 @@ private:
class MachOFileLayout {
public:
/// All layout computation is done in the constructor.
- MachOFileLayout(const NormalizedFile &file);
+ MachOFileLayout(const NormalizedFile &file, bool alwaysIncludeFunctionStarts);
/// Returns the final file size as computed in the constructor.
size_t size() const;
@@ -123,7 +122,8 @@ public:
llvm::Error writeBinary(StringRef path);
private:
- uint32_t loadCommandsSize(uint32_t &count);
+ uint32_t loadCommandsSize(uint32_t &count,
+ bool alwaysIncludeFunctionStarts);
void buildFileOffsets();
void writeMachHeader();
llvm::Error writeLoadCommands();
@@ -232,8 +232,9 @@ private:
ByteBuffer _exportTrie;
};
-size_t headerAndLoadCommandsSize(const NormalizedFile &file) {
- MachOFileLayout layout(file);
+size_t headerAndLoadCommandsSize(const NormalizedFile &file,
+ bool includeFunctionStarts) {
+ MachOFileLayout layout(file, includeFunctionStarts);
return layout.headerAndLoadCommandsSize();
}
@@ -250,7 +251,8 @@ size_t MachOFileLayout::headerAndLoadCommandsSize() const {
return _endOfLoadCommands;
}
-MachOFileLayout::MachOFileLayout(const NormalizedFile &file)
+MachOFileLayout::MachOFileLayout(const NormalizedFile &file,
+ bool alwaysIncludeFunctionStarts)
: _file(file),
_is64(MachOLinkingContext::is64Bit(file.arch)),
_swap(!MachOLinkingContext::isHostEndian(file.arch)),
@@ -271,7 +273,7 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file)
_endOfLoadCommands += sizeof(version_min_command);
_countOfLoadCommands++;
}
- if (!_file.functionStarts.empty()) {
+ if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
_endOfLoadCommands += sizeof(linkedit_data_command);
_countOfLoadCommands++;
}
@@ -326,7 +328,8 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file)
} else {
// Final linked images have one load command per segment.
_endOfLoadCommands = _startOfLoadCommands
- + loadCommandsSize(_countOfLoadCommands);
+ + loadCommandsSize(_countOfLoadCommands,
+ alwaysIncludeFunctionStarts);
// Assign section file offsets.
buildFileOffsets();
@@ -375,7 +378,8 @@ MachOFileLayout::MachOFileLayout(const NormalizedFile &file)
}
}
-uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) {
+uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count,
+ bool alwaysIncludeFunctionStarts) {
uint32_t size = 0;
count = 0;
@@ -445,7 +449,7 @@ uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) {
}
// Add LC_FUNCTION_STARTS if needed
- if (!_file.functionStarts.empty()) {
+ if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
size += sizeof(linkedit_data_command);
++count;
}
@@ -1007,6 +1011,7 @@ llvm::Error MachOFileLayout::writeLoadCommands() {
lc += sizeof(linkedit_data_command);
}
}
+ assert(lc == &_buffer[_endOfLoadCommands]);
return llvm::Error::success();
}
@@ -1018,6 +1023,7 @@ void MachOFileLayout::writeSectionContent() {
if (s.content.empty())
continue;
uint32_t offset = _sectInfo[&s].fileOffset;
+ assert(offset >= _endOfLoadCommands);
uint8_t *p = &_buffer[offset];
memcpy(p, &s.content[0], s.content.size());
p += s.content.size();
@@ -1543,7 +1549,7 @@ llvm::Error MachOFileLayout::writeBinary(StringRef path) {
/// Takes in-memory normalized view and writes a mach-o object file.
llvm::Error writeBinary(const NormalizedFile &file, StringRef path) {
- MachOFileLayout layout(file);
+ MachOFileLayout layout(file, false);
return layout.writeBinary(path);
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index e93ca86c3164..1a4603be77c0 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp ------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -588,7 +587,8 @@ void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg,
void Util::assignAddressesToSections(const NormalizedFile &file) {
// NOTE!: Keep this in sync with organizeSections.
- size_t hlcSize = headerAndLoadCommandsSize(file);
+ size_t hlcSize = headerAndLoadCommandsSize(file,
+ _ctx.generateFunctionStartsLoadCommand());
uint64_t address = 0;
for (SegmentInfo *seg : _segmentInfos) {
if (seg->name.equals("__PAGEZERO")) {
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 473de894894e..879f07fb4760 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp --------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
index 92a646dab5e0..7f53faaeaea3 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp -----------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/MachOPasses.h b/lib/ReaderWriter/MachO/MachOPasses.h
index cd01d4aa2c93..93cd3e4df281 100644
--- a/lib/ReaderWriter/MachO/MachOPasses.h
+++ b/lib/ReaderWriter/MachO/MachOPasses.h
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/MachOPasses.h -------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ObjCPass.cpp b/lib/ReaderWriter/MachO/ObjCPass.cpp
index 23c71e0f5ecd..df121f0e1d5d 100644
--- a/lib/ReaderWriter/MachO/ObjCPass.cpp
+++ b/lib/ReaderWriter/MachO/ObjCPass.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/ObjCPass.cpp -------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/ReaderWriter/MachO/SectCreateFile.h b/lib/ReaderWriter/MachO/SectCreateFile.h
index 49e65f63151d..7bb98e16695c 100644
--- a/lib/ReaderWriter/MachO/SectCreateFile.h
+++ b/lib/ReaderWriter/MachO/SectCreateFile.h
@@ -1,9 +1,8 @@
//===---- lib/ReaderWriter/MachO/SectCreateFile.h ---------------*- c++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp
index 8a2d2e910cad..b0775ad5fc26 100644
--- a/lib/ReaderWriter/MachO/ShimPass.cpp
+++ b/lib/ReaderWriter/MachO/ShimPass.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/ReaderWriter/MachO/StubsPass.cpp b/lib/ReaderWriter/MachO/StubsPass.cpp
index 04c586df336c..fbbd8b2c7584 100644
--- a/lib/ReaderWriter/MachO/StubsPass.cpp
+++ b/lib/ReaderWriter/MachO/StubsPass.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/StubsPass.cpp ---------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -271,9 +270,8 @@ public:
mergedFile.addAtom(*helperCacheNLPAtom);
// Add reference to dyld_stub_binder in libSystem.dylib
- auto I = std::find_if(
- mergedFile.sharedLibrary().begin(), mergedFile.sharedLibrary().end(),
- [&](const SharedLibraryAtom *atom) {
+ auto I = llvm::find_if(
+ mergedFile.sharedLibrary(), [&](const SharedLibraryAtom *atom) {
return atom->name().equals(_stubInfo.binderSymbolName);
});
assert(I != mergedFile.sharedLibrary().end() &&
diff --git a/lib/ReaderWriter/MachO/TLVPass.cpp b/lib/ReaderWriter/MachO/TLVPass.cpp
index e362e507ebf2..89b655e1f888 100644
--- a/lib/ReaderWriter/MachO/TLVPass.cpp
+++ b/lib/ReaderWriter/MachO/TLVPass.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/ReaderWriter/MachO/WriterMachO.cpp b/lib/ReaderWriter/MachO/WriterMachO.cpp
index c457e7b55a43..60e0e9dd9a81 100644
--- a/lib/ReaderWriter/MachO/WriterMachO.cpp
+++ b/lib/ReaderWriter/MachO/WriterMachO.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/MachO/WriterMachO.cpp -----------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
index 59548684e677..5feff2a0514a 100644
--- a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
+++ b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
@@ -1,9 +1,8 @@
//===- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp -------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/tools/lld/CMakeLists.txt b/tools/lld/CMakeLists.txt
index d8829493fc22..a15e296e31df 100644
--- a/tools/lld/CMakeLists.txt
+++ b/tools/lld/CMakeLists.txt
@@ -8,6 +8,7 @@ add_lld_tool(lld
target_link_libraries(lld
PRIVATE
+ lldCommon
lldCOFF
lldDriver
lldELF
diff --git a/tools/lld/lld.cpp b/tools/lld/lld.cpp
index c749d458a992..37ef260495e9 100644
--- a/tools/lld/lld.cpp
+++ b/tools/lld/lld.cpp
@@ -1,9 +1,8 @@
//===- tools/lld/lld.cpp - Linker Driver Dispatcher -----------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -27,9 +26,13 @@
//===----------------------------------------------------------------------===//
#include "lld/Common/Driver.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include <cstdlib>
@@ -46,13 +49,13 @@ enum Flavor {
Wasm, // -flavor wasm
};
-LLVM_ATTRIBUTE_NORETURN static void die(const Twine &S) {
- errs() << S << "\n";
+LLVM_ATTRIBUTE_NORETURN static void die(const Twine &s) {
+ errs() << s << "\n";
exit(1);
}
-static Flavor getFlavor(StringRef S) {
- return StringSwitch<Flavor>(S)
+static Flavor getFlavor(StringRef s) {
+ return StringSwitch<Flavor>(s)
.CasesLower("ld", "ld.lld", "gnu", Gnu)
.CasesLower("wasm", "ld-wasm", Wasm)
.CaseLower("link", WinLink)
@@ -60,55 +63,73 @@ static Flavor getFlavor(StringRef S) {
.Default(Invalid);
}
-static bool isPETarget(const std::vector<const char *> &V) {
- for (auto It = V.begin(); It + 1 != V.end(); ++It) {
- if (StringRef(*It) != "-m")
+static cl::TokenizerCallback getDefaultQuotingStyle() {
+ if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32)
+ return cl::TokenizeWindowsCommandLine;
+ return cl::TokenizeGNUCommandLine;
+}
+
+static bool isPETargetName(StringRef s) {
+ return s == "i386pe" || s == "i386pep" || s == "thumb2pe" || s == "arm64pe";
+}
+
+static bool isPETarget(std::vector<const char *> &v) {
+ for (auto it = v.begin(); it + 1 != v.end(); ++it) {
+ if (StringRef(*it) != "-m")
+ continue;
+ return isPETargetName(*(it + 1));
+ }
+ // Expand response files (arguments in the form of @<filename>)
+ // to allow detecting the -m argument from arguments in them.
+ SmallVector<const char *, 256> expandedArgs(v.data(), v.data() + v.size());
+ cl::ExpandResponseFiles(saver, getDefaultQuotingStyle(), expandedArgs);
+ for (auto it = expandedArgs.begin(); it + 1 != expandedArgs.end(); ++it) {
+ if (StringRef(*it) != "-m")
continue;
- StringRef S = *(It + 1);
- return S == "i386pe" || S == "i386pep" || S == "thumb2pe" || S == "arm64pe";
+ return isPETargetName(*(it + 1));
}
return false;
}
-static Flavor parseProgname(StringRef Progname) {
+static Flavor parseProgname(StringRef progname) {
#if __APPLE__
// Use Darwin driver for "ld" on Darwin.
- if (Progname == "ld")
+ if (progname == "ld")
return Darwin;
#endif
#if LLVM_ON_UNIX
// Use GNU driver for "ld" on other Unix-like system.
- if (Progname == "ld")
+ if (progname == "ld")
return Gnu;
#endif
// Progname may be something like "lld-gnu". Parse it.
- SmallVector<StringRef, 3> V;
- Progname.split(V, "-");
- for (StringRef S : V)
- if (Flavor F = getFlavor(S))
- return F;
+ SmallVector<StringRef, 3> v;
+ progname.split(v, "-");
+ for (StringRef s : v)
+ if (Flavor f = getFlavor(s))
+ return f;
return Invalid;
}
-static Flavor parseFlavor(std::vector<const char *> &V) {
+static Flavor parseFlavor(std::vector<const char *> &v) {
// Parse -flavor option.
- if (V.size() > 1 && V[1] == StringRef("-flavor")) {
- if (V.size() <= 2)
+ if (v.size() > 1 && v[1] == StringRef("-flavor")) {
+ if (v.size() <= 2)
die("missing arg value for '-flavor'");
- Flavor F = getFlavor(V[2]);
- if (F == Invalid)
- die("Unknown flavor: " + StringRef(V[2]));
- V.erase(V.begin() + 1, V.begin() + 3);
- return F;
+ Flavor f = getFlavor(v[2]);
+ if (f == Invalid)
+ die("Unknown flavor: " + StringRef(v[2]));
+ v.erase(v.begin() + 1, v.begin() + 3);
+ return f;
}
// Deduct the flavor from argv[0].
- StringRef Arg0 = path::filename(V[0]);
- if (Arg0.endswith_lower(".exe"))
- Arg0 = Arg0.drop_back(4);
- return parseProgname(Arg0);
+ StringRef arg0 = path::filename(v[0]);
+ if (arg0.endswith_lower(".exe"))
+ arg0 = arg0.drop_back(4);
+ return parseProgname(arg0);
}
// If this function returns true, lld calls _exit() so that it quickly
@@ -121,21 +142,21 @@ static bool canExitEarly() { return StringRef(getenv("LLD_IN_TEST")) != "1"; }
/// Universal linker main(). This linker emulates the gnu, darwin, or
/// windows linker based on the argv[0] or -flavor option.
-int main(int Argc, const char **Argv) {
- InitLLVM X(Argc, Argv);
+int main(int argc, const char **argv) {
+ InitLLVM x(argc, argv);
- std::vector<const char *> Args(Argv, Argv + Argc);
- switch (parseFlavor(Args)) {
+ std::vector<const char *> args(argv, argv + argc);
+ switch (parseFlavor(args)) {
case Gnu:
- if (isPETarget(Args))
- return !mingw::link(Args);
- return !elf::link(Args, canExitEarly());
+ if (isPETarget(args))
+ return !mingw::link(args);
+ return !elf::link(args, canExitEarly());
case WinLink:
- return !coff::link(Args, canExitEarly());
+ return !coff::link(args, canExitEarly());
case Darwin:
- return !mach_o::link(Args, canExitEarly());
+ return !mach_o::link(args, canExitEarly());
case Wasm:
- return !wasm::link(Args, canExitEarly());
+ return !wasm::link(args, canExitEarly());
default:
die("lld is a generic driver.\n"
"Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld"