aboutsummaryrefslogtreecommitdiffstats
path: root/ELF
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-07-28 11:08:33 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-07-28 11:08:33 +0000
commit20d35e67e67f106f617c939725101223211659f0 (patch)
tree64eb963cbf5ba58765e0a6b64a440965d66a7a4d /ELF
parentae1a339de31cf4065777531959a11e55a2e5fa00 (diff)
downloadsrc-20d35e67e67f106f617c939725101223211659f0.tar.gz
src-20d35e67e67f106f617c939725101223211659f0.zip
Vendor import of lld trunk r338150:vendor/lld/lld-trunk-r338150
Notes
Notes: svn path=/vendor/lld/dist/; revision=336821 svn path=/vendor/lld/lld-trunk-r338150/; revision=336822; tag=vendor/lld/lld-trunk-r338150
Diffstat (limited to 'ELF')
-rw-r--r--ELF/AArch64ErrataFix.cpp17
-rw-r--r--ELF/AArch64ErrataFix.h1
-rw-r--r--ELF/Arch/AArch64.cpp57
-rw-r--r--ELF/Arch/AMDGPU.cpp2
-rw-r--r--ELF/Arch/ARM.cpp30
-rw-r--r--ELF/Arch/Hexagon.cpp97
-rw-r--r--ELF/Arch/Mips.cpp175
-rw-r--r--ELF/Arch/MipsArchTree.cpp37
-rw-r--r--ELF/Arch/PPC.cpp7
-rw-r--r--ELF/Arch/PPC64.cpp423
-rw-r--r--ELF/Arch/SPARCV9.cpp12
-rw-r--r--ELF/Arch/X86.cpp168
-rw-r--r--ELF/Arch/X86_64.cpp215
-rw-r--r--ELF/CMakeLists.txt4
-rw-r--r--ELF/CallGraphSort.cpp249
-rw-r--r--ELF/CallGraphSort.h23
-rw-r--r--ELF/Config.h55
-rw-r--r--ELF/Driver.cpp590
-rw-r--r--ELF/Driver.h6
-rw-r--r--ELF/DriverUtils.cpp55
-rw-r--r--ELF/EhFrame.cpp8
-rw-r--r--ELF/Filesystem.cpp2
-rw-r--r--ELF/GdbIndex.cpp2
-rw-r--r--ELF/GdbIndex.h1
-rw-r--r--ELF/ICF.cpp116
-rw-r--r--ELF/ICF.h4
-rw-r--r--ELF/InputFiles.cpp608
-rw-r--r--ELF/InputFiles.h83
-rw-r--r--ELF/InputSection.cpp477
-rw-r--r--ELF/InputSection.h65
-rw-r--r--ELF/LTO.cpp214
-rw-r--r--ELF/LTO.h6
-rw-r--r--ELF/LinkerScript.cpp413
-rw-r--r--ELF/LinkerScript.h63
-rw-r--r--ELF/MapFile.cpp213
-rw-r--r--ELF/MapFile.h1
-rw-r--r--ELF/MarkLive.cpp38
-rw-r--r--ELF/MarkLive.h21
-rw-r--r--ELF/Options.td520
-rw-r--r--ELF/OutputSections.cpp90
-rw-r--r--ELF/OutputSections.h19
-rw-r--r--ELF/Relocations.cpp812
-rw-r--r--ELF/Relocations.h21
-rw-r--r--ELF/ScriptLexer.cpp10
-rw-r--r--ELF/ScriptParser.cpp304
-rw-r--r--ELF/Strings.cpp62
-rw-r--r--ELF/Strings.h75
-rw-r--r--ELF/SymbolTable.cpp300
-rw-r--r--ELF/SymbolTable.h11
-rw-r--r--ELF/Symbols.cpp95
-rw-r--r--ELF/Symbols.h159
-rw-r--r--ELF/SyntheticSections.cpp1475
-rw-r--r--ELF/SyntheticSections.h335
-rw-r--r--ELF/Target.cpp28
-rw-r--r--ELF/Target.h113
-rw-r--r--ELF/Thunks.cpp390
-rw-r--r--ELF/Thunks.h20
-rw-r--r--ELF/Writer.cpp754
-rw-r--r--ELF/Writer.h5
59 files changed, 6782 insertions, 3374 deletions
diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp
index 9c0d536dea71..7551919cf86f 100644
--- a/ELF/AArch64ErrataFix.cpp
+++ b/ELF/AArch64ErrataFix.cpp
@@ -34,12 +34,11 @@
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Relocations.h"
-#include "Strings.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
-
+#include "lld/Common/Strings.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -47,6 +46,7 @@
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support;
using namespace llvm::support::endian;
using namespace lld;
@@ -341,7 +341,7 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
// patch or 0 if no patch required.
static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
uint64_t Limit) {
- uint64_t ISAddr = IS->getParent()->Addr + IS->OutSecOff;
+ uint64_t ISAddr = IS->getVA(0);
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
@@ -357,7 +357,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
uint64_t PatchOff = 0;
const uint8_t *Buf = IS->Data.begin();
- const uint32_t *InstBuf = reinterpret_cast<const uint32_t *>(Buf + Off);
+ const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off);
uint32_t Instr1 = *InstBuf++;
uint32_t Instr2 = *InstBuf++;
uint32_t Instr3 = *InstBuf++;
@@ -405,7 +405,7 @@ lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off)
}
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
- return Patchee->getParent()->Addr + Patchee->OutSecOff + PatcheeOffset;
+ return Patchee->getVA(PatcheeOffset);
}
void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
@@ -554,9 +554,8 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26)
return;
- if (Config->Verbose)
- message("detected cortex-a53-843419 erratum sequence starting at " +
- utohexstr(AdrpAddr) + " in unpatched output.");
+ log("detected cortex-a53-843419 erratum sequence starting at " +
+ utohexstr(AdrpAddr) + " in unpatched output.");
auto *PS = make<Patch843419Section>(IS, PatcheeOffset);
Patches.push_back(PS);
@@ -602,7 +601,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
(DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
while (Off < Limit) {
- uint64_t StartAddr = IS->getParent()->Addr + IS->OutSecOff + Off;
+ uint64_t StartAddr = IS->getVA(Off);
if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
implementPatch(StartAddr, PatcheeOffset, IS, Patches);
}
diff --git a/ELF/AArch64ErrataFix.h b/ELF/AArch64ErrataFix.h
index 6c100f25d8af..edd154d4cab3 100644
--- a/ELF/AArch64ErrataFix.h
+++ b/ELF/AArch64ErrataFix.h
@@ -11,7 +11,6 @@
#define LLD_ELF_AARCH64ERRATAFIX_H
#include "lld/Common/LLVM.h"
-
#include <map>
#include <vector>
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index 99e9879a6989..c7b3c0801de2 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -34,7 +34,7 @@ public:
AArch64();
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
- bool isPicRel(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,
@@ -89,6 +89,11 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
return R_TLSDESC_CALL;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
+ case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
return R_TLS;
case R_AARCH64_CALL26:
case R_AARCH64_CONDBR19:
@@ -144,8 +149,10 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const {
}
}
-bool AArch64::isPicRel(RelType Type) const {
- return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
+RelType AArch64::getDynRel(RelType Type) const {
+ if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64)
+ return Type;
+ return R_AARCH64_NONE;
}
void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
@@ -240,12 +247,12 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt<16>(Loc, Val, Type);
+ checkIntUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt<32>(Loc, Val, Type);
+ checkIntUInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_AARCH64_ABS64:
@@ -260,11 +267,11 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
- checkInt<33>(Loc, Val, Type);
+ checkInt(Loc, Val, 33, Type);
write32AArch64Addr(Loc, Val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
write32AArch64Addr(Loc, Val);
break;
case R_AARCH64_JUMP26:
@@ -278,38 +285,40 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
- checkInt<28>(Loc, Val, Type);
+ checkInt(Loc, Val, 28, Type);
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
- checkAlignment<4>(Loc, Val, Type);
- checkInt<21>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ checkInt(Loc, Val, 21, Type);
or32le(Loc, (Val & 0x1FFFFC) << 3);
break;
- case R_AARCH64_LD64_GOT_LO12_NC:
- case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
- case R_AARCH64_TLSDESC_LD64_LO12:
- checkAlignment<8>(Loc, Val, Type);
- or32le(Loc, (Val & 0xFF8) << 7);
- break;
case R_AARCH64_LDST8_ABS_LO12_NC:
+ case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
or32AArch64Imm(Loc, getBits(Val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
- checkAlignment<2>(Loc, Val, Type);
+ case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ checkAlignment(Loc, Val, 2, Type);
or32AArch64Imm(Loc, getBits(Val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
- checkAlignment<4>(Loc, Val, Type);
+ case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ checkAlignment(Loc, Val, 4, Type);
or32AArch64Imm(Loc, getBits(Val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
- checkAlignment<8>(Loc, Val, Type);
+ 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));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
- checkAlignment<16>(Loc, Val, Type);
+ case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
+ checkAlignment(Loc, Val, 16, Type);
or32AArch64Imm(Loc, getBits(Val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0_NC:
@@ -325,11 +334,11 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
or32le(Loc, (Val & 0xFFFF000000000000) >> 43);
break;
case R_AARCH64_TSTBR14:
- checkInt<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
or32le(Loc, (Val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkInt<24>(Loc, Val, Type);
+ checkInt(Loc, Val, 24, Type);
or32AArch64Imm(Loc, Val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
@@ -353,7 +362,7 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// movk x0, #0x10
// nop
// nop
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
switch (Type) {
case R_AARCH64_TLSDESC_ADD_LO12:
@@ -403,7 +412,7 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp
index 505e0e6ad480..48b27f23510c 100644
--- a/ELF/Arch/AMDGPU.cpp
+++ b/ELF/Arch/AMDGPU.cpp
@@ -66,6 +66,7 @@ void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, Val);
break;
case R_AMDGPU_ABS64:
+ case R_AMDGPU_REL64:
write64le(Loc, Val);
break;
case R_AMDGPU_GOTPCREL32_HI:
@@ -86,6 +87,7 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
case R_AMDGPU_REL32:
case R_AMDGPU_REL32_LO:
case R_AMDGPU_REL32_HI:
+ case R_AMDGPU_REL64:
return R_PC;
case R_AMDGPU_GOTPCREL:
case R_AMDGPU_GOTPCREL32_LO:
diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp
index b9f551e4b3be..d99be9be7c36 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -29,7 +29,6 @@ public:
uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
- bool isPicRel(RelType Type) 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;
@@ -55,6 +54,7 @@ ARM::ARM() {
TlsGotRel = R_ARM_TLS_TPOFF32;
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
TlsOffsetRel = R_ARM_TLS_DTPOFF32;
+ GotBaseSymInGotPlt = false;
GotEntrySize = 4;
GotPltEntrySize = 4;
PltEntrySize = 16;
@@ -161,18 +161,10 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
}
}
-bool ARM::isPicRel(RelType Type) const {
- return (Type == R_ARM_TARGET1 && !Config->Target1Rel) ||
- (Type == R_ARM_ABS32);
-}
-
RelType ARM::getDynRel(RelType Type) const {
- if (Type == R_ARM_TARGET1 && !Config->Target1Rel)
+ if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel))
return R_ARM_ABS32;
- if (Type == R_ARM_ABS32)
- return Type;
- // Keep it going with a dummy value so that we can find more reloc errors.
- return R_ARM_ABS32;
+ return R_ARM_NONE;
}
void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
@@ -392,7 +384,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, 1);
break;
case R_ARM_PREL31:
- checkInt<31>(Loc, Val, Type);
+ checkInt(Loc, Val, 31, Type);
write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
break;
case R_ARM_CALL:
@@ -401,7 +393,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
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<26>(Loc, Val, Type);
+ checkInt(Loc, Val, 26, Type);
write32le(Loc, 0xfa000000 | // opcode
((Val & 2) << 23) | // H
((Val >> 2) & 0x00ffffff)); // imm24
@@ -416,16 +408,16 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- checkInt<26>(Loc, Val, Type);
+ checkInt(Loc, Val, 26, Type);
write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt<12>(Loc, Val, Type);
+ 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<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
write16le(Loc,
(read16le(Loc) & 0xfbc0) | // opcode cond
((Val >> 10) & 0x0400) | // S
@@ -451,7 +443,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
// FIXME: Use of I1 and I2 require v6T2ops
- checkInt<25>(Loc, Val, Type);
+ checkInt(Loc, Val, 25, Type);
write16le(Loc,
0xf000 | // opcode
((Val >> 14) & 0x0400) | // S
@@ -469,14 +461,14 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
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
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write16le(Loc,
0xf2c0 | // opcode
((Val >> 17) & 0x0400) | // i
diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp
new file mode 100644
index 000000000000..2d5c23fd5ad6
--- /dev/null
+++ b/ELF/Arch/Hexagon.cpp
@@ -0,0 +1,97 @@
+//===-- Hexagon.cpp -------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::support::endian;
+using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+class Hexagon final : public TargetInfo {
+public:
+ 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;
+};
+} // namespace
+
+// Support V60 only at the moment.
+uint32_t Hexagon::calcEFlags() const { return 0x60; }
+
+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;
+ }
+ }
+ return Result;
+}
+
+RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
+ const uint8_t *Loc) const {
+ switch (Type) {
+ case R_HEX_B15_PCREL:
+ case R_HEX_B15_PCREL_X:
+ case R_HEX_B22_PCREL:
+ case R_HEX_B22_PCREL_X:
+ case R_HEX_B32_PCREL_X:
+ return R_PC;
+ default:
+ return R_ABS;
+ }
+}
+
+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) {
+ case R_HEX_NONE:
+ break;
+ case R_HEX_B15_PCREL:
+ or32le(Loc, applyMask(0x00df20fe, Val >> 2));
+ break;
+ case R_HEX_B15_PCREL_X:
+ or32le(Loc, applyMask(0x00df20fe, Val & 0x3f));
+ break;
+ case R_HEX_B22_PCREL:
+ or32le(Loc, applyMask(0x1ff3ffe, Val >> 2));
+ break;
+ case R_HEX_B22_PCREL_X:
+ or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f));
+ break;
+ case R_HEX_B32_PCREL_X:
+ or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
+ break;
+ default:
+ error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ break;
+ }
+}
+
+TargetInfo *elf::getHexagonTargetInfo() {
+ static Hexagon Target;
+ return &Target;
+}
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index 495e2567006f..dc70401c0b0e 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -32,7 +32,6 @@ public:
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
- bool isPicRel(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;
@@ -50,6 +49,7 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
DefaultMaxPageSize = 65536;
GotEntrySize = sizeof(typename ELFT::uint);
GotPltEntrySize = sizeof(typename ELFT::uint);
+ GotBaseSymInGotPlt = false;
PltEntrySize = 16;
PltHeaderSize = 32;
CopyRel = R_MIPS_COPY;
@@ -101,8 +101,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_HIGHEST:
case R_MICROMIPS_HI16:
case R_MICROMIPS_LO16:
- case R_MICROMIPS_HIGHER:
- case R_MICROMIPS_HIGHEST:
// R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate
// offset between start of function and 'gp' value which by default
// equal to the start of .got section. In that case we consider these
@@ -124,8 +122,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_TLS_TPREL_LO16:
case R_MIPS_TLS_TPREL32:
case R_MIPS_TLS_TPREL64:
- case R_MICROMIPS_GOT_OFST:
- case R_MICROMIPS_SUB:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_TPREL_HI16:
@@ -155,7 +151,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_GOT_DISP:
case R_MIPS_TLS_GOTTPREL:
case R_MICROMIPS_CALL16:
- case R_MICROMIPS_GOT_DISP:
case R_MICROMIPS_TLS_GOTTPREL:
return R_MIPS_GOT_OFF;
case R_MIPS_CALL_HI16:
@@ -168,7 +163,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MICROMIPS_GOT_LO16:
return R_MIPS_GOT_OFF32;
case R_MIPS_GOT_PAGE:
- case R_MICROMIPS_GOT_PAGE:
return R_MIPS_GOT_LOCAL_PAGE;
case R_MIPS_TLS_GD:
case R_MICROMIPS_TLS_GD:
@@ -183,12 +177,10 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
}
}
-template <class ELFT> bool MIPS<ELFT>::isPicRel(RelType Type) const {
- return Type == R_MIPS_32 || Type == R_MIPS_64;
-}
-
template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
- return RelativeRel;
+ if (Type == R_MIPS_32 || Type == R_MIPS_64)
+ return RelativeRel;
+ return R_MIPS_NONE;
}
template <class ELFT>
@@ -213,8 +205,8 @@ template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) {
}
template <endianness E>
-static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
- uint8_t Shift) {
+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);
@@ -222,14 +214,14 @@ static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
}
template <endianness E>
-static void writeMicroRelocation32(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;
if (E == support::little)
std::swap(Words[0], Words[1]);
- writeRelocation<E>(Loc, V, BitsSize, Shift);
+ writeValue<E>(Loc, V, BitsSize, Shift);
if (E == support::little)
std::swap(Words[0], Words[1]);
@@ -296,13 +288,14 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
}
- write32<E>(Buf + 24, 0x0320f809); // jalr $25
+ 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 = InX::GotPlt->getVA();
- writeRelocation<E>(Buf, GotPlt + 0x8000, 16, 16);
- writeRelocation<E>(Buf + 4, GotPlt, 16, 0);
- writeRelocation<E>(Buf + 8, GotPlt, 16, 0);
+ writeValue<E>(Buf, GotPlt + 0x8000, 16, 16);
+ writeValue<E>(Buf + 4, GotPlt, 16, 0);
+ writeValue<E>(Buf + 8, GotPlt, 16, 0);
}
template <class ELFT>
@@ -330,13 +323,16 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
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, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25
+ write32<E>(Buf + 8, JrInst); // jr $25 / jr.hb $25
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
- writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
- writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0);
- writeRelocation<E>(Buf + 12, GotPltEntryAddr, 16, 0);
+ writeValue<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
+ writeValue<E>(Buf + 4, GotPltEntryAddr, 16, 0);
+ writeValue<E>(Buf + 12, GotPltEntryAddr, 16, 0);
}
template <class ELFT>
@@ -455,9 +451,6 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
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);
- if (Type2 == R_MICROMIPS_SUB &&
- (Type3 == R_MICROMIPS_HI16 || Type3 == R_MICROMIPS_LO16))
- return std::make_pair(Type3, -Val);
error(getErrorLocation(Loc) + "unsupported relocations combination " +
Twine(Type));
return std::make_pair(Type & 0xff, Val);
@@ -467,6 +460,9 @@ 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);
+
// 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 ||
@@ -481,9 +477,6 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
Val -= 0x7000;
}
- if (ELFT::Is64Bits || Config->MipsN32Abi)
- std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
-
switch (Type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
@@ -497,25 +490,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write64<E>(Loc, Val);
break;
case R_MIPS_26:
- writeRelocation<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) {
- writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
+ writeValue<E>(Loc, Val + 0x8000, 16, 16);
} else {
- checkInt<16>(Loc, Val, Type);
- writeRelocation<E>(Loc, Val, 16, 0);
+ checkInt(Loc, Val, 16, Type);
+ writeValue<E>(Loc, Val, 16, 0);
}
break;
case R_MICROMIPS_GOT16:
if (Config->Relocatable) {
- writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
+ writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
} else {
- checkInt<16>(Loc, Val, Type);
- writeMicroRelocation32<E>(Loc, Val, 16, 0);
+ checkInt(Loc, Val, 16, Type);
+ writeShuffleValue<E>(Loc, Val, 16, 0);
}
break;
case R_MIPS_CALL16:
@@ -525,7 +518,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<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
@@ -534,28 +527,25 @@ 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:
- writeRelocation<E>(Loc, Val, 16, 0);
+ writeValue<E>(Loc, Val, 16, 0);
break;
- case R_MICROMIPS_GOT_DISP:
- case R_MICROMIPS_GOT_PAGE:
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
- checkInt<16>(Loc, Val, Type);
- writeMicroRelocation32<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:
- case R_MICROMIPS_GOT_OFST:
case R_MICROMIPS_LO16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_TPREL_LO16:
- writeMicroRelocation32<E>(Loc, Val, 16, 0);
+ writeShuffleValue<E>(Loc, Val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
- checkInt<7>(Loc, Val, Type);
- writeMicroRelocation32<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:
@@ -563,86 +553,80 @@ 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:
- writeRelocation<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:
- writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
+ writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
break;
case R_MIPS_HIGHER:
- writeRelocation<E>(Loc, Val + 0x80008000, 16, 32);
+ writeValue<E>(Loc, Val + 0x80008000, 16, 32);
break;
case R_MIPS_HIGHEST:
- writeRelocation<E>(Loc, Val + 0x800080008000, 16, 48);
- break;
- case R_MICROMIPS_HIGHER:
- writeMicroRelocation32<E>(Loc, Val + 0x80008000, 16, 32);
- break;
- case R_MICROMIPS_HIGHEST:
- writeMicroRelocation32<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<4>(Loc, Val, Type);
- checkInt<18>(Loc, Val, Type);
- writeRelocation<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<4>(Loc, Val, Type);
- checkInt<21>(Loc, Val, Type);
- writeRelocation<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<4>(Loc, Val, Type);
- checkInt<23>(Loc, Val, Type);
- writeRelocation<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<4>(Loc, Val, Type);
- checkInt<28>(Loc, Val, Type);
- writeRelocation<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:
- writeRelocation<E>(Loc, Val, 32, 0);
+ writeValue<E>(Loc, Val, 32, 0);
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
- checkInt<27>(Loc, Val, Type);
- writeMicroRelocation32<E>(Loc, Val, 26, 1);
+ checkInt(Loc, Val, 27, Type);
+ writeShuffleValue<E>(Loc, Val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
- checkInt<8>(Loc, Val, Type);
+ checkInt(Loc, Val, 8, Type);
writeMicroRelocation16<E>(Loc, Val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
- checkInt<11>(Loc, Val, Type);
+ checkInt(Loc, Val, 11, Type);
writeMicroRelocation16<E>(Loc, Val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
- checkInt<17>(Loc, Val, Type);
- writeMicroRelocation32<E>(Loc, Val, 16, 1);
+ checkInt(Loc, Val, 17, Type);
+ writeShuffleValue<E>(Loc, Val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
- checkInt<21>(Loc, Val, Type);
- writeMicroRelocation32<E>(Loc, Val, 18, 3);
+ checkInt(Loc, Val, 21, Type);
+ writeShuffleValue<E>(Loc, Val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
- checkInt<21>(Loc, Val, Type);
- writeMicroRelocation32<E>(Loc, Val, 19, 2);
+ checkInt(Loc, Val, 21, Type);
+ writeShuffleValue<E>(Loc, Val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
- checkInt<22>(Loc, Val, Type);
- writeMicroRelocation32<E>(Loc, Val, 21, 1);
+ checkInt(Loc, Val, 22, Type);
+ writeShuffleValue<E>(Loc, Val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
- checkInt<25>(Loc, Val, Type);
- writeMicroRelocation32<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));
@@ -651,19 +635,26 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const {
return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST ||
- Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_GOT_OFST;
+ Type == R_MICROMIPS_LO16;
}
// Return true if the symbol is a PIC function.
template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) {
- typedef typename ELFT::Ehdr Elf_Ehdr;
- if (!Sym->Section || !Sym->isFunc())
+ if (!Sym->isFunc())
+ return false;
+
+ if (Sym->StOther & STO_MIPS_PIC)
+ return true;
+
+ if (!Sym->Section)
+ return false;
+
+ ObjFile<ELFT> *File =
+ cast<InputSectionBase>(Sym->Section)->template getFile<ELFT>();
+ if (!File)
return false;
- auto *Sec = cast<InputSectionBase>(Sym->Section);
- const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
- return (Sym->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
- (Hdr->e_flags & EF_MIPS_PIC);
+ return File->getObj().getHeader()->e_flags & EF_MIPS_PIC;
}
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
diff --git a/ELF/Arch/MipsArchTree.cpp b/ELF/Arch/MipsArchTree.cpp
index 754a47001579..98ceac3075e0 100644
--- a/ELF/Arch/MipsArchTree.cpp
+++ b/ELF/Arch/MipsArchTree.cpp
@@ -65,25 +65,30 @@ static StringRef getNanName(bool IsNan2008) {
static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; }
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;
- for (const FileFlags &F : Files.slice(1)) {
+ 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("target ABI '" + getAbiName(ABI) + "' is incompatible with '" +
- getAbiName(ABI2) + "': " + toString(F.File));
+ 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("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" +
- getNanName(Nan2) + ": " + toString(F.File));
+ 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("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" +
- getFpName(Fp2) + ": " + toString(F.File));
+ error(toString(F.File) + ": -mfp" + getFpName(Fp2) +
+ " is incompatible with target -mfp" + getFpName(Fp));
}
}
@@ -102,11 +107,13 @@ static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
for (const FileFlags &F : Files.slice(1)) {
bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
if (IsPic && !IsPic2)
- warn("linking abicalls code " + toString(Files[0].File) +
- " with non-abicalls file: " + toString(F.File));
+ warn(toString(F.File) +
+ ": linking non-abicalls code with abicalls code " +
+ toString(Files[0].File));
if (!IsPic && IsPic2)
- warn("linking non-abicalls code " + toString(Files[0].File) +
- " with abicalls file: " + toString(F.File));
+ warn(toString(F.File) +
+ ": linking abicalls code with non-abicalls code " +
+ toString(Files[0].File));
}
// Compute the result PIC/non-PIC flag.
@@ -326,7 +333,7 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) {
case Mips::Val_GNU_MIPS_ABI_FP_SOFT:
return "-msoft-float";
case Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
- return "-mips32r2 -mfp64 (old)";
+ return "-mgp32 -mfp64 (old)";
case Mips::Val_GNU_MIPS_ABI_FP_XX:
return "-mfpxx";
case Mips::Val_GNU_MIPS_ABI_FP_64:
@@ -343,9 +350,9 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
if (compareMipsFpAbi(NewFlag, OldFlag) >= 0)
return NewFlag;
if (compareMipsFpAbi(OldFlag, NewFlag) < 0)
- error("target floating point ABI '" + getMipsFpAbiName(OldFlag) +
- "' is incompatible with '" + getMipsFpAbiName(NewFlag) +
- "': " + FileName);
+ error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) +
+ "' is incompatible with target floating point ABI '" +
+ getMipsFpAbiName(OldFlag) + "'");
return OldFlag;
}
diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp
index 6af0df331df6..20cae0e59cf4 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -21,13 +21,18 @@ using namespace lld::elf;
namespace {
class PPC final : public TargetInfo {
public:
- PPC() { GotBaseSymOff = 0x8000; }
+ 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;
};
} // namespace
+PPC::PPC() {
+ GotBaseSymOff = 0x8000;
+ GotBaseSymInGotPlt = false;
+}
+
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp
index ac4021b5918d..fa3bf6c62a0d 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -14,12 +14,14 @@
#include "llvm/Support/Endian.h"
using namespace llvm;
+using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
static uint64_t PPC64TocOffset = 0x8000;
+static uint64_t DynamicThreadPointerOffset = 0x8000;
uint64_t elf::getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
@@ -39,11 +41,21 @@ namespace {
class PPC64 final : public TargetInfo {
public:
PPC64();
+ 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;
+ 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;
};
} // namespace
@@ -51,21 +63,35 @@ 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 applyPPCLo(uint64_t V) { return V; }
-static uint16_t applyPPCHi(uint64_t V) { return V >> 16; }
-static uint16_t applyPPCHa(uint64_t V) { return (V + 0x8000) >> 16; }
-static uint16_t applyPPCHigher(uint64_t V) { return V >> 32; }
-static uint16_t applyPPCHighera(uint64_t V) { return (V + 0x8000) >> 32; }
-static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; }
-static uint16_t applyPPCHighesta(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; }
PPC64::PPC64() {
- PltRel = GotRel = R_PPC64_GLOB_DAT;
+ GotRel = R_PPC64_GLOB_DAT;
+ PltRel = R_PPC64_JMP_SLOT;
RelativeRel = R_PPC64_RELATIVE;
+ IRelativeRel = R_PPC64_IRELATIVE;
GotEntrySize = 8;
+ PltEntrySize = 4;
GotPltEntrySize = 8;
- PltEntrySize = 32;
- PltHeaderSize = 0;
+ GotBaseSymInGotPlt = false;
+ GotBaseSymOff = 0x8000;
+ GotHeaderEntriesNum = 1;
+ GotPltHeaderEntriesNum = 2;
+ PltHeaderSize = 60;
+ NeedsThunks = true;
+ TcbSize = 8;
+ TlsTpOffset = 0x7000;
+
+ TlsModuleIndexRel = R_PPC64_DTPMOD64;
+ TlsOffsetRel = R_PPC64_DTPREL64;
+
+ TlsGotRel = R_PPC64_TPREL64;
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
@@ -80,6 +106,110 @@ PPC64::PPC64() {
// And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers
// use 0x10000000 as the starting address.
DefaultImageBase = 0x10000000;
+
+ TrapInstr =
+ (Config->IsLE == sys::IsLittleEndianHost) ? 0x7fe00008 : 0x0800e07f;
+}
+
+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));
+ }
+ return 2;
+}
+
+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
+ // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x
+ // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x
+ // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x
+ // R_PPC64_REL24 __tls_get_addr
+ // nop None None
+
+ // Relaxing to local exec entails converting:
+ // addis r3, r2, x@got@tlsgd@ha into nop
+ // addi r3, r3, x@got@tlsgd@l into addis r3, r13, x@tprel@ha
+ // bl __tls_get_addr(x@tlsgd) into nop
+ // nop into addi r3, r3, x@tprel@l
+
+ uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
+
+ switch (Type) {
+ case R_PPC64_GOT_TLSGD16_HA:
+ write32(Loc - EndianOffset, 0x60000000); // nop
+ break;
+ case R_PPC64_GOT_TLSGD16_LO:
+ write32(Loc - EndianOffset, 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
+ relocateOne(Loc + 4 + EndianOffset, 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 {
+ // 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
+ // addis r3, r2, x@got@tlsld@ha R_PPC64_GOT_TLSLD16_HA x
+ // addi r3, r3, x@got@tlsld@l R_PPC64_GOT_TLSLD16_LO x
+ // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSLD x
+ // R_PPC64_REL24 __tls_get_addr
+ // nop None None
+
+ // Relaxing to local exec entails converting:
+ // addis r3, r2, x@got@tlsld@ha into nop
+ // addi r3, r3, x@got@tlsld@l into addis r3, r13, 0
+ // bl __tls_get_addr(x@tlsgd) into nop
+ // nop into addi r3, r3, 4096
+
+ uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
+ switch (Type) {
+ case R_PPC64_GOT_TLSLD16_HA:
+ write32(Loc - EndianOffset, 0x60000000); // nop
+ break;
+ case R_PPC64_GOT_TLSLD16_LO:
+ write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13, 0
+ break;
+ case R_PPC64_TLSLD:
+ write32(Loc, 0x60000000); // nop
+ write32(Loc + 4, 0x38631000); // addi r3, r3, 4096
+ break;
+ case R_PPC64_DTPREL16:
+ case R_PPC64_DTPREL16_HA:
+ case R_PPC64_DTPREL16_HI:
+ 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);
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
+ }
}
RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
@@ -95,48 +225,162 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
case R_PPC64_TOC:
return R_PPC_TOC;
case R_PPC64_REL24:
- return R_PPC_PLT_OPD;
+ return R_PPC_CALL_PLT;
+ case R_PPC64_REL16_LO:
+ case R_PPC64_REL16_HA:
+ case R_PPC64_REL32:
+ case R_PPC64_REL64:
+ return R_PC;
+ case R_PPC64_GOT_TLSGD16:
+ case R_PPC64_GOT_TLSGD16_HA:
+ case R_PPC64_GOT_TLSGD16_HI:
+ case R_PPC64_GOT_TLSGD16_LO:
+ return R_TLSGD_GOT;
+ case R_PPC64_GOT_TLSLD16:
+ case R_PPC64_GOT_TLSLD16_HA:
+ case R_PPC64_GOT_TLSLD16_HI:
+ case R_PPC64_GOT_TLSLD16_LO:
+ return R_TLSLD_GOT;
+ case R_PPC64_GOT_TPREL16_HA:
+ case R_PPC64_GOT_TPREL16_LO_DS:
+ case R_PPC64_GOT_TPREL16_DS:
+ case R_PPC64_GOT_TPREL16_HI:
+ return R_GOT_OFF;
+ 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:
+ return R_TLSLD_GOT_OFF;
+ case R_PPC64_TPREL16:
+ case R_PPC64_TPREL16_HA:
+ case R_PPC64_TPREL16_LO:
+ case R_PPC64_TPREL16_HI:
+ case R_PPC64_TPREL16_DS:
+ case R_PPC64_TPREL16_LO_DS:
+ case R_PPC64_TPREL16_HIGHER:
+ case R_PPC64_TPREL16_HIGHERA:
+ case R_PPC64_TPREL16_HIGHEST:
+ case R_PPC64_TPREL16_HIGHESTA:
+ return R_TLS;
+ case R_PPC64_DTPREL16:
+ case R_PPC64_DTPREL16_DS:
+ case R_PPC64_DTPREL16_HA:
+ case R_PPC64_DTPREL16_HI:
+ case R_PPC64_DTPREL16_HIGHER:
+ case R_PPC64_DTPREL16_HIGHERA:
+ case R_PPC64_DTPREL16_HIGHEST:
+ case R_PPC64_DTPREL16_HIGHESTA:
+ case R_PPC64_DTPREL16_LO:
+ case R_PPC64_DTPREL16_LO_DS:
+ case R_PPC64_DTPREL64:
+ return R_ABS;
+ case R_PPC64_TLSGD:
+ return R_TLSDESC_CALL;
+ case R_PPC64_TLSLD:
+ return R_TLSLD_HINT;
+ case R_PPC64_TLS:
+ return R_HINT;
default:
return R_ABS;
}
}
+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
+
+ // 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 = InX::GotPlt->getVA() - (InX::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 {
- uint64_t Off = GotPltEntryAddr - getPPC64TocBase();
-
- // FIXME: What we should do, in theory, is get the offset of the function
- // descriptor in the .opd section, and use that as the offset from %r2 (the
- // TOC-base pointer). Instead, we have the GOT-entry offset, and that will
- // be a pointer to the function descriptor in the .opd section. Using
- // this scheme is simpler, but requires an extra indirection per PLT dispatch.
-
- write32be(Buf, 0xf8410028); // std %r2, 40(%r1)
- write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
- write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
- write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
- write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
- write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
- write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
- write32be(Buf + 28, 0x4e800420); // bctr
+ int32_t Offset = PltHeaderSize + Index * PltEntrySize;
+ // bl __glink_PLTresolve
+ write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
}
static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
- uint64_t V = Val - PPC64TocOffset;
+ // Relocations relative to the toc-base need to be adjusted by the Toc offset.
+ uint64_t TocBiasedVal = Val - PPC64TocOffset;
+ // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset.
+ uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset;
+
switch (Type) {
+ // TOC biased relocation.
+ case R_PPC64_GOT_TLSGD16:
+ case R_PPC64_GOT_TLSLD16:
case R_PPC64_TOC16:
- return {R_PPC64_ADDR16, V};
+ return {R_PPC64_ADDR16, TocBiasedVal};
case R_PPC64_TOC16_DS:
- return {R_PPC64_ADDR16_DS, V};
+ case R_PPC64_GOT_TPREL16_DS:
+ case R_PPC64_GOT_DTPREL16_DS:
+ return {R_PPC64_ADDR16_DS, TocBiasedVal};
+ 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, V};
+ return {R_PPC64_ADDR16_HA, TocBiasedVal};
+ 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, V};
+ return {R_PPC64_ADDR16_HI, TocBiasedVal};
+ case R_PPC64_GOT_TLSGD16_LO:
+ case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TOC16_LO:
- return {R_PPC64_ADDR16_LO, V};
+ return {R_PPC64_ADDR16_LO, TocBiasedVal};
case R_PPC64_TOC16_LO_DS:
- return {R_PPC64_ADDR16_LO_DS, V};
+ case R_PPC64_GOT_TPREL16_LO_DS:
+ case R_PPC64_GOT_DTPREL16_LO_DS:
+ return {R_PPC64_ADDR16_LO_DS, TocBiasedVal};
+
+ // Dynamic Thread pointer biased relocation types.
+ case R_PPC64_DTPREL16:
+ return {R_PPC64_ADDR16, DTPBiasedVal};
+ case R_PPC64_DTPREL16_DS:
+ return {R_PPC64_ADDR16_DS, DTPBiasedVal};
+ case R_PPC64_DTPREL16_HA:
+ return {R_PPC64_ADDR16_HA, DTPBiasedVal};
+ case R_PPC64_DTPREL16_HI:
+ return {R_PPC64_ADDR16_HI, DTPBiasedVal};
+ case R_PPC64_DTPREL16_HIGHER:
+ return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal};
+ case R_PPC64_DTPREL16_HIGHERA:
+ return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal};
+ case R_PPC64_DTPREL16_HIGHEST:
+ return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal};
+ case R_PPC64_DTPREL16_HIGHESTA:
+ return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal};
+ case R_PPC64_DTPREL16_LO:
+ return {R_PPC64_ADDR16_LO, DTPBiasedVal};
+ case R_PPC64_DTPREL16_LO_DS:
+ return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal};
+ case R_PPC64_DTPREL64:
+ return {R_PPC64_ADDR64, DTPBiasedVal};
+
default:
return {Type, Val};
}
@@ -149,68 +393,139 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_PPC64_ADDR14: {
- checkAlignment<4>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
// Preserve the AA/LK bits in the branch instruction
uint8_t AALK = Loc[3];
- write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc));
+ write16(Loc + 2, (AALK & 3) | (Val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- checkInt<16>(Loc, Val, Type);
- write16be(Loc, Val);
+ case R_PPC64_TPREL16:
+ checkInt(Loc, Val, 16, Type);
+ write16(Loc, Val);
break;
case R_PPC64_ADDR16_DS:
- checkInt<16>(Loc, Val, Type);
- write16be(Loc, (read16be(Loc) & 3) | (Val & ~3));
+ case R_PPC64_TPREL16_DS:
+ checkInt(Loc, Val, 16, Type);
+ write16(Loc, (read16(Loc) & 3) | (Val & ~3));
break;
case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
- write16be(Loc, applyPPCHa(Val));
+ case R_PPC64_TPREL16_HA:
+ write16(Loc, ha(Val));
break;
case R_PPC64_ADDR16_HI:
case R_PPC64_REL16_HI:
- write16be(Loc, applyPPCHi(Val));
+ case R_PPC64_TPREL16_HI:
+ write16(Loc, hi(Val));
break;
case R_PPC64_ADDR16_HIGHER:
- write16be(Loc, applyPPCHigher(Val));
+ case R_PPC64_TPREL16_HIGHER:
+ write16(Loc, higher(Val));
break;
case R_PPC64_ADDR16_HIGHERA:
- write16be(Loc, applyPPCHighera(Val));
+ case R_PPC64_TPREL16_HIGHERA:
+ write16(Loc, highera(Val));
break;
case R_PPC64_ADDR16_HIGHEST:
- write16be(Loc, applyPPCHighest(Val));
+ case R_PPC64_TPREL16_HIGHEST:
+ write16(Loc, highest(Val));
break;
case R_PPC64_ADDR16_HIGHESTA:
- write16be(Loc, applyPPCHighesta(Val));
+ case R_PPC64_TPREL16_HIGHESTA:
+ write16(Loc, highesta(Val));
break;
case R_PPC64_ADDR16_LO:
- write16be(Loc, applyPPCLo(Val));
+ case R_PPC64_REL16_LO:
+ case R_PPC64_TPREL16_LO:
+ write16(Loc, lo(Val));
break;
case R_PPC64_ADDR16_LO_DS:
- case R_PPC64_REL16_LO:
- write16be(Loc, (read16be(Loc) & 3) | (applyPPCLo(Val) & ~3));
+ case R_PPC64_TPREL16_LO_DS:
+ write16(Loc, (read16(Loc) & 3) | (lo(Val) & ~3));
break;
case R_PPC64_ADDR32:
case R_PPC64_REL32:
- checkInt<32>(Loc, Val, Type);
- write32be(Loc, Val);
+ checkInt(Loc, Val, 32, Type);
+ write32(Loc, Val);
break;
case R_PPC64_ADDR64:
case R_PPC64_REL64:
case R_PPC64_TOC:
- write64be(Loc, Val);
+ write64(Loc, Val);
break;
case R_PPC64_REL24: {
uint32_t Mask = 0x03FFFFFC;
- checkInt<24>(Loc, Val, Type);
- write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask));
+ checkInt(Loc, Val, 24, Type);
+ write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
break;
}
+ case R_PPC64_DTPREL64:
+ write64(Loc, Val - DynamicThreadPointerOffset);
+ break;
default:
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
+bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &S) const {
+ // If a function is in the plt it needs to be called through
+ // a call stub.
+ return Type == R_PPC64_REL24 && S.isInPlt();
+}
+
+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)
+ return R_RELAX_TLS_LD_TO_LE_ABS;
+ return Expr;
+}
+
+// Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement.
+// The general dynamic code sequence for a global `x` uses 4 instructions.
+// Instruction Relocation Symbol
+// addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x
+// addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x
+// bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x
+// R_PPC64_REL24 __tls_get_addr
+// nop None None
+//
+// Relaxing to initial-exec entails:
+// 1) Convert the addis/addi pair that builds the address of the tls_index
+// struct for 'x' to an addis/ld pair that loads an offset from a got-entry.
+// 2) Convert the call to __tls_get_addr to a nop.
+// 3) Convert the nop following the call to an add of the loaded offset to the
+// 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) {
+ 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);
+ return;
+ 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 EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
+ uint32_t InputRegister = (read32(Loc - EndianOffset) & (0x1f << 16));
+ write32(Loc - EndianOffset, 0xE8600000 | InputRegister);
+ 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
+ return;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
+ }
+}
+
TargetInfo *elf::getPPC64TargetInfo() {
static PPC64 Target;
return &Target;
diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp
index d9d6e1390407..36f5c836930e 100644
--- a/ELF/Arch/SPARCV9.cpp
+++ b/ELF/Arch/SPARCV9.cpp
@@ -77,23 +77,23 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
write32be(Loc, Val);
break;
case R_SPARC_DISP32:
// V-disp32
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32be(Loc, Val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
- checkUInt<22>(Loc, Val, Type);
+ checkUInt(Loc, Val, 22, Type);
write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff));
break;
case R_SPARC_GOT22:
@@ -103,7 +103,7 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_SPARC_WDISP19:
// V-disp19
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
@@ -137,7 +137,7 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t Off = PltHeaderSize + Index * PltEntrySize;
+ uint64_t Off = getPltEntryOffset(Index);
relocateOne(Buf, R_SPARC_22, Off);
relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
}
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index 10517bef14f3..19a0b6017f1a 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -21,7 +21,7 @@ using namespace lld;
using namespace lld::elf;
namespace {
-class X86 final : public TargetInfo {
+class X86 : public TargetInfo {
public:
X86();
RelExpr getRelExpr(RelType Type, const Symbol &S,
@@ -46,7 +46,6 @@ public:
} // namespace
X86::X86() {
- GotBaseSymOff = -1;
CopyRel = R_386_COPY;
GotRel = R_386_GLOB_DAT;
PltRel = R_386_JUMP_SLOT;
@@ -74,9 +73,9 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
case R_386_TLS_LDO_32:
return R_ABS;
case R_386_TLS_GD:
- return R_TLSGD;
+ return R_TLSGD_GOT_FROM_END;
case R_386_TLS_LDM:
- return R_TLSLD;
+ return R_TLSLD_GOT_FROM_END;
case R_386_PLT32:
return R_PLT_PC;
case R_386_PC8:
@@ -224,7 +223,7 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
}
write32le(Buf + 7, RelOff);
- write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
+ write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
}
int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
@@ -256,15 +255,15 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// 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.
- checkUInt<8>(Loc, Val, Type);
+ checkIntUInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_386_PC8:
- checkInt<8>(Loc, Val, Type);
+ checkInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_386_16:
- checkUInt<16>(Loc, Val, Type);
+ checkIntUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_386_PC16:
@@ -278,7 +277,7 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// current location subtracted from it.
// We just check that Val fits in 17 bits. This misses some cases, but
// should have no false positives.
- checkInt<17>(Loc, Val, Type);
+ checkInt(Loc, Val, 17, Type);
write16le(Loc, Val);
break;
case R_386_32:
@@ -301,7 +300,7 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
default:
@@ -399,7 +398,152 @@ void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
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;
+};
+
+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;
+};
+} // namespace
+
+RetpolinePic::RetpolinePic() {
+ PltHeaderSize = 48;
+ PltEntrySize = 32;
+}
+
+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)
+ 0x50, // 6: pushl %eax
+ 0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax
+ 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next
+ 0xf3, 0x90, // 12: loop: pause
+ 0x0f, 0xae, 0xe8, // 14: lfence
+ 0xeb, 0xf9, // 17: jmp loop
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
+ 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
+ 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
+ 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
+ 0x89, 0xc8, // 2b: mov %ecx, %eax
+ 0x59, // 2d: pop %ecx
+ 0xc3, // 2e: ret
+ 0xcc, // 2f: int3; padding
+ };
+ memcpy(Buf, Insn, sizeof(Insn));
+
+ uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
+ uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
+ write32le(Buf + 2, GotPlt + 4);
+ write32le(Buf + 9, GotPlt + 8);
+}
+
+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
+ 0xe9, 0, 0, 0, 0, // jmp plt+0x12
+ 0x68, 0, 0, 0, 0, // pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // jmp plt+0
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
+ };
+ memcpy(Buf, Insn, sizeof(Insn));
+
+ uint32_t Ebx = InX::Got->getVA() + InX::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);
+}
+
+RetpolineNoPic::RetpolineNoPic() {
+ PltHeaderSize = 48;
+ PltEntrySize = 32;
+}
+
+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[] = {
+ 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
+ 0x50, // 6: pushl %eax
+ 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
+ 0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next
+ 0xf3, 0x90, // 11: loop: pause
+ 0x0f, 0xae, 0xe8, // 13: lfence
+ 0xeb, 0xf9, // 16: jmp loop
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3
+ 0xcc, 0xcc, 0xcc, // 1f: int3; .align 16
+ 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
+ 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
+ 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
+ 0x89, 0xc8, // 2b: mov %ecx, %eax
+ 0x59, // 2d: pop %ecx
+ 0xc3, // 2e: ret
+ 0xcc, // 2f: int3; padding
+ };
+ memcpy(Buf, Insn, sizeof(Insn));
+
+ uint32_t GotPlt = InX::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[] = {
+ 0x50, // 0: pushl %eax
+ 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
+ 0xe8, 0, 0, 0, 0, // 6: call plt+0x20
+ 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11
+ 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // 15: jmp plt+0
+ 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);
+}
+
TargetInfo *elf::getX86TargetInfo() {
- static X86 Target;
- return &Target;
+ if (Config->ZRetpolineplt) {
+ if (Config->Pic) {
+ static RetpolinePic T;
+ return &T;
+ }
+ static RetpolineNoPic T;
+ return &T;
+ }
+
+ static X86 T;
+ return &T;
}
diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp
index c977d9247d92..d4bdb3730c58 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -23,12 +23,12 @@ using namespace lld;
using namespace lld::elf;
namespace {
-template <class ELFT> class X86_64 final : public TargetInfo {
+template <class ELFT> class X86_64 : public TargetInfo {
public:
X86_64();
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
- bool isPicRel(RelType Type) 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;
@@ -43,6 +43,8 @@ public:
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) const override;
private:
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
@@ -51,7 +53,6 @@ private:
} // namespace
template <class ELFT> X86_64<ELFT>::X86_64() {
- GotBaseSymOff = -1;
CopyRel = R_X86_64_COPY;
GotRel = R_X86_64_GLOB_DAT;
PltRel = R_X86_64_JUMP_SLOT;
@@ -106,6 +107,11 @@ RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
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;
+ case R_X86_64_GOTPC32:
+ case R_X86_64_GOTPC64:
+ return R_GOTONLY_PC_FROM_END;
case R_X86_64_NONE:
return R_NONE;
default:
@@ -124,7 +130,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
template <class ELFT>
void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
// See comments in X86::writeGotPlt.
- write32le(Buf, S.getPltVA() + 6);
+ write64le(Buf, S.getPltVA() + 6);
}
template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
@@ -153,12 +159,14 @@ void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
write32le(Buf + 7, Index);
- write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
+ write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
}
-template <class ELFT> bool X86_64<ELFT>::isPicRel(RelType Type) const {
- return Type != R_X86_64_PC32 && Type != R_X86_64_32 &&
- Type != R_X86_64_TPOFF32;
+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;
+ return R_X86_64_NONE;
}
template <class ELFT>
@@ -285,20 +293,21 @@ template <class ELFT>
void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_X86_64_8:
- checkUInt<8>(Loc, Val, Type);
+ checkUInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_X86_64_16:
- checkUInt<16>(Loc, Val, Type);
+ checkUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_X86_64_32:
- checkUInt<32>(Loc, Val, Type);
+ 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_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
@@ -309,7 +318,7 @@ 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<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_X86_64_64:
@@ -318,6 +327,8 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
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);
break;
default:
@@ -460,12 +471,180 @@ void X86_64<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
write32le(Loc - 1, Val + 1);
}
-TargetInfo *elf::getX32TargetInfo() {
- static X86_64<ELF32LE> Target;
- return &Target;
+// 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) const {
+ // Replace "cmp %fs:0x70,%rsp" and subsequent branch
+ // with "stc, nopl 0x0(%rax,%rax,1)"
+ if (Loc + 8 < End && memcmp(Loc, "\x64\x48\x3b\x24\x25", 4) == 0) {
+ memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
+ return true;
+ }
+
+ // Adjust "lea -0x200(%rsp),%r10" to lea "-0x4200(%rsp),%r10"
+ if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x94\x24\x00\xfe\xff", 7) == 0) {
+ memcpy(Loc, "\x4c\x8d\x94\x24\x00\xbe\xff", 7);
+ return true;
+ }
+
+ // Adjust "lea -0x200(%rsp),%r11" to lea "-0x4200(%rsp),%r11"
+ if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x9c\x24\x00\xfe\xff", 7) == 0) {
+ memcpy(Loc, "\x4c\x8d\x9c\x24\x00\xbe\xff", 7);
+ return true;
+ }
+ return false;
+}
+
+template <>
+bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
+ uint8_t *End) 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
+// entries, we use a CALL followed by MOV and RET to do the same thing as an
+// indirect jump. That instruction sequence is so-called "retpoline".
+//
+// We have two types of retpoline PLTs as a size optimization. If `-z now`
+// 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> {
+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;
+};
+
+template <class ELFT> class RetpolineZNow : public X86_64<ELFT> {
+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;
+};
+} // namespace
+
+template <class ELFT> Retpoline<ELFT>::Retpoline() {
+ TargetInfo::PltHeaderSize = 48;
+ TargetInfo::PltEntrySize = 32;
}
-TargetInfo *elf::getX86_64TargetInfo() {
- static X86_64<ELF64LE> Target;
- return &Target;
+template <class ELFT>
+void Retpoline<ELFT>::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[] = {
+ 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
+ 0xf3, 0x90, // 12: loop: pause
+ 0x0f, 0xae, 0xe8, // 14: lfence
+ 0xeb, 0xf9, // 17: jmp loop
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
+ 0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp)
+ 0xc3, // 24: ret
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding
+ 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding
+ };
+ memcpy(Buf, Insn, sizeof(Insn));
+
+ uint64_t GotPlt = InX::GotPlt->getVA();
+ uint64_t Plt = InX::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[] = {
+ 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
+ 0x68, 0, 0, 0, 0, // 11: pushq <relocation index>
+ 0xe9, 0, 0, 0, 0, // 16: jmp plt+0
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
+ };
+ memcpy(Buf, Insn, sizeof(Insn));
+
+ uint64_t Off = TargetInfo::getPltEntryOffset(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);
+}
+
+template <class ELFT> RetpolineZNow<ELFT>::RetpolineZNow() {
+ TargetInfo::PltHeaderSize = 32;
+ TargetInfo::PltEntrySize = 16;
}
+
+template <class ELFT>
+void RetpolineZNow<ELFT>::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
+ 0xeb, 0xf9, // a: jmp loop
+ 0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16
+ 0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp)
+ 0xc3, // 14: ret
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
+ 0xcc, // 1f: int3; padding
+ };
+ 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[] = {
+ 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));
+
+ write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
+ write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12);
+}
+
+template <class ELFT> static TargetInfo *getTargetInfo() {
+ if (Config->ZRetpolineplt) {
+ if (Config->ZNow) {
+ static RetpolineZNow<ELFT> T;
+ return &T;
+ }
+ static Retpoline<ELFT> T;
+ return &T;
+ }
+
+ static X86_64<ELFT> T;
+ return &T;
+}
+
+TargetInfo *elf::getX32TargetInfo() { return getTargetInfo<ELF32LE>(); }
+TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo<ELF64LE>(); }
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index 7ec837841315..fb2f53a72025 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -12,6 +12,7 @@ add_lld_library(lldELF
Arch/AMDGPU.cpp
Arch/ARM.cpp
Arch/AVR.cpp
+ Arch/Hexagon.cpp
Arch/Mips.cpp
Arch/MipsArchTree.cpp
Arch/PPC.cpp
@@ -19,6 +20,7 @@ add_lld_library(lldELF
Arch/SPARCV9.cpp
Arch/X86.cpp
Arch/X86_64.cpp
+ CallGraphSort.cpp
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
@@ -35,7 +37,6 @@ add_lld_library(lldELF
Relocations.cpp
ScriptLexer.cpp
ScriptParser.cpp
- Strings.cpp
SymbolTable.cpp
Symbols.cpp
SyntheticSections.cpp
@@ -46,6 +47,7 @@ add_lld_library(lldELF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
BinaryFormat
+ BitWriter
Core
DebugInfoDWARF
LTO
diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp
new file mode 100644
index 000000000000..33ac159a6e26
--- /dev/null
+++ b/ELF/CallGraphSort.cpp
@@ -0,0 +1,249 @@
+//===- CallGraphSort.cpp --------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Implementation of Call-Chain Clustering from: Optimizing Function Placement
+/// for Large-Scale Data-Center Applications
+/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf
+///
+/// The goal of this algorithm is to improve runtime performance of the final
+/// executable by arranging code sections such that page table and i-cache
+/// misses are minimized.
+///
+/// Definitions:
+/// * Cluster
+/// * An ordered list of input sections which are layed out as a unit. At the
+/// beginning of the algorithm each input section has its own cluster and
+/// the weight of the cluster is the sum of the weight of all incomming
+/// edges.
+/// * Call-Chain Clustering (C³) Heuristic
+/// * Defines when and how clusters are combined. Pick the highest weighted
+/// input section then add it to its most likely predecessor if it wouldn't
+/// penalize it too much.
+/// * Density
+/// * The weight of the cluster divided by the size of the cluster. This is a
+/// proxy for the ammount of execution time spent per byte of the cluster.
+///
+/// It does so given a call graph profile by the following:
+/// * Build a weighted call graph from the call graph profile
+/// * Sort input sections by weight
+/// * For each input section starting with the highest weight
+/// * Find its most likely predecessor cluster
+/// * Check if the combined cluster would be too large, or would have too low
+/// a density.
+/// * If not, then combine the clusters.
+/// * Sort non-empty clusters by density
+///
+//===----------------------------------------------------------------------===//
+
+#include "CallGraphSort.h"
+#include "OutputSections.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+struct Edge {
+ int From;
+ uint64_t Weight;
+};
+
+struct Cluster {
+ Cluster(int Sec, size_t S) {
+ Sections.push_back(Sec);
+ Size = S;
+ }
+
+ double getDensity() const {
+ if (Size == 0)
+ return 0;
+ return double(Weight) / double(Size);
+ }
+
+ std::vector<int> Sections;
+ size_t Size = 0;
+ uint64_t Weight = 0;
+ uint64_t InitialWeight = 0;
+ std::vector<Edge> Preds;
+};
+
+class CallGraphSort {
+public:
+ CallGraphSort();
+
+ DenseMap<const InputSectionBase *, int> run();
+
+private:
+ std::vector<Cluster> Clusters;
+ std::vector<const InputSectionBase *> Sections;
+
+ void groupClusters();
+};
+
+// Maximum ammount the combined cluster density can be worse than the original
+// cluster to consider merging.
+constexpr int MAX_DENSITY_DEGRADATION = 8;
+
+// Maximum cluster size in bytes.
+constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
+} // end anonymous namespace
+
+// Take the edge list in Config->CallGraphProfile, resolve symbol names to
+// Symbols, and generate a graph between InputSections with the provided
+// weights.
+CallGraphSort::CallGraphSort() {
+ llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
+ 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());
+ }
+ return Res.first->second;
+ };
+
+ // Create the graph.
+ for (const auto &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
+ // containing input sections that can't actually be placed adjacently in the
+ // 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())
+ continue;
+
+ int From = GetOrCreateNode(FromSB);
+ int To = GetOrCreateNode(ToSB);
+
+ Clusters[To].Weight += Weight;
+
+ if (From == To)
+ continue;
+
+ // Add an edge
+ Clusters[To].Preds.push_back({From, 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);
+ if (NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION)
+ return true;
+ return false;
+}
+
+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());
+
+ for (int SI = 0, SE = Clusters.size(); SI != SE; ++SI) {
+ SortedSecs[SI] = SI;
+ SecToCluster[SI] = &Clusters[SI];
+ }
+
+ std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) {
+ return Clusters[B].getDensity() < Clusters[A].getDensity();
+ });
+
+ 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];
+
+ int BestPred = -1;
+ uint64_t BestWeight = 0;
+
+ for (Edge &E : C.Preds) {
+ if (BestPred == -1 || E.Weight > BestWeight) {
+ BestPred = E.From;
+ BestWeight = E.Weight;
+ }
+ }
+
+ // don't consider merging if the edge is unlikely.
+ if (BestWeight * 10 <= C.InitialWeight)
+ continue;
+
+ Cluster *PredC = SecToCluster[BestPred];
+ if (PredC == &C)
+ continue;
+
+ if (C.Size + PredC->Size > MAX_CLUSTER_SIZE)
+ continue;
+
+ 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;
+
+ 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();
+ });
+
+ // Sort by density.
+ std::stable_sort(Clusters.begin(), Clusters.end(),
+ [](const Cluster &A, const Cluster &B) {
+ return A.getDensity() > B.getDensity();
+ });
+}
+
+DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
+ groupClusters();
+
+ // Generate order.
+ llvm::DenseMap<const InputSectionBase *, int> OrderMap;
+ ssize_t CurOrder = 1;
+
+ for (const Cluster &C : Clusters)
+ for (int SecIndex : C.Sections)
+ OrderMap[Sections[SecIndex]] = CurOrder++;
+
+ return OrderMap;
+}
+
+// Sort sections by the profile data provided by -callgraph-profile-file
+//
+// This first builds a call graph based on the profile data then merges sections
+// according to the C³ huristic. All clusters are then sorted by a density
+// metric to further improve locality.
+DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
+ return CallGraphSort().run();
+}
diff --git a/ELF/CallGraphSort.h b/ELF/CallGraphSort.h
new file mode 100644
index 000000000000..3f96dc88f435
--- /dev/null
+++ b/ELF/CallGraphSort.h
@@ -0,0 +1,23 @@
+//===- CallGraphSort.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_CALL_GRAPH_SORT_H
+#define LLD_ELF_CALL_GRAPH_SORT_H
+
+#include "llvm/ADT/DenseMap.h"
+
+namespace lld {
+namespace elf {
+class InputSectionBase;
+
+llvm::DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder();
+} // namespace elf
+} // namespace lld
+
+#endif
diff --git a/ELF/Config.h b/ELF/Config.h
index 74c325cb7cb1..ec804c5296bc 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -10,6 +10,7 @@
#ifndef LLD_ELF_CONFIG_H
#define LLD_ELF_CONFIG_H
+#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
@@ -17,13 +18,13 @@
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
-
#include <vector>
namespace lld {
namespace elf {
class InputFile;
+class InputSectionBase;
enum ELFKind {
ELFNoneKind,
@@ -39,6 +40,9 @@ enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid };
// For --discard-{all,locals,none}.
enum class DiscardPolicy { Default, All, Locals, None };
+// For --icf={none,safe,all}.
+enum class ICFLevel { None, Safe, All };
+
// For --strip-{all,debug}.
enum class StripPolicy { None, All, Debug };
@@ -79,21 +83,27 @@ struct Configuration {
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> Argv;
std::vector<llvm::StringRef> AuxiliaryList;
std::vector<llvm::StringRef> FilterList;
std::vector<llvm::StringRef> SearchPaths;
@@ -103,15 +113,20 @@ struct Configuration {
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 = false;
+ bool AndroidPackDynRelocs;
bool ARMHasBlx = false;
bool ARMHasMovtMovw = false;
bool ARMJ1J2BranchEncoding = false;
bool AsNeeded = false;
bool Bsymbolic;
bool BsymbolicFunctions;
+ bool CheckSections;
bool CompressDebugSections;
+ bool Cref;
bool DefineCommon;
bool Demangle = true;
bool DisableVerify;
@@ -123,14 +138,15 @@ struct Configuration {
bool GcSections;
bool GdbIndex;
bool GnuHash = false;
+ bool GnuUnique;
bool HasDynamicList = false;
bool HasDynSymTab;
- bool ICF;
- bool ICFData;
+ bool IgnoreDataAddressEquality;
+ bool IgnoreFunctionAddressEquality;
+ bool LTODebugPassManager;
+ bool LTONewPassManager;
bool MergeArmExidx;
bool MipsN32Abi = false;
- bool NoGnuUnique;
- bool NoUndefinedVersion;
bool NoinhibitExec;
bool Nostdlib;
bool OFormatBinary;
@@ -138,7 +154,9 @@ struct Configuration {
bool OptRemarksWithHotness;
bool Pie;
bool PrintGcSections;
+ bool PrintIcfSections;
bool Relocatable;
+ bool RelrPackDynRelocs;
bool SaveTemps;
bool SingleRoRx;
bool Shared;
@@ -146,12 +164,21 @@ struct Configuration {
bool SysvHash = false;
bool Target1Rel;
bool Trace;
- bool Verbose;
+ bool ThinLTOEmitImportsFiles;
+ bool ThinLTOIndexOnly;
+ bool UndefinedVersion;
+ bool UseAndroidRelrTags = false;
+ bool WarnBackrefs;
bool WarnCommon;
bool WarnMissingEntry;
+ bool WarnSymbolOrdering;
+ bool WriteAddends;
bool ZCombreloc;
+ bool ZCopyreloc;
bool ZExecstack;
- bool ZNocopyreloc;
+ bool ZHazardplt;
+ bool ZInitfirst;
+ bool ZKeepTextSectionPrefix;
bool ZNodelete;
bool ZNodlopen;
bool ZNow;
@@ -159,9 +186,10 @@ struct Configuration {
bool ZRelro;
bool ZRodynamic;
bool ZText;
- bool ExitEarly;
+ bool ZRetpolineplt;
bool ZWxneeded;
DiscardPolicy Discard;
+ ICFLevel ICF;
OrphanHandlingPolicy OrphanHandling;
SortSectionPolicy SortSection;
StripPolicy Strip;
@@ -173,6 +201,7 @@ struct Configuration {
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;
@@ -238,6 +267,12 @@ struct Configuration {
// The only instance of Configuration struct.
extern Configuration *Config;
+static inline void errorOrWarn(const Twine &Msg) {
+ if (!Config->NoinhibitExec)
+ error(Msg);
+ else
+ warn(Msg);
+}
} // namespace elf
} // namespace lld
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index cc76fea2ad5e..1fc552f011b6 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -30,9 +30,9 @@
#include "InputFiles.h"
#include "InputSection.h"
#include "LinkerScript.h"
+#include "MarkLive.h"
#include "OutputSections.h"
#include "ScriptParser.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
@@ -42,12 +42,16 @@
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
+#include "lld/Common/TargetOptionsCommandFlags.h"
#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/TargetSelect.h"
@@ -66,16 +70,18 @@ using namespace lld::elf;
Configuration *elf::Config;
LinkerDriver *elf::Driver;
-static void setConfigs();
+static void setConfigs(opt::InputArgList &Args);
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
- errorHandler().LogName = Args[0];
+ errorHandler().LogName = sys::path::filename(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();
+
InputSections.clear();
OutputSections.clear();
Tar = nullptr;
@@ -88,14 +94,14 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
Driver = make<LinkerDriver>();
Script = make<LinkerScript>();
Symtab = make<SymbolTable>();
- Config->Argv = {Args.begin(), Args.end()};
+ Config->ProgName = Args[0];
- Driver->main(Args, CanExitEarly);
+ 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 (Config->ExitEarly)
+ if (CanExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
@@ -113,7 +119,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
std::pair<ELFKind, uint16_t> Ret =
StringSwitch<std::pair<ELFKind, uint16_t>>(S)
- .Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64})
+ .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec",
+ {ELF64LEKind, EM_AARCH64})
.Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM})
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
@@ -122,6 +129,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
+ .Case("elf64lppc", {ELF64LEKind, EM_PPC64})
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
.Case("elf_i386", {ELF32LEKind, EM_386})
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
@@ -228,11 +236,15 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
Files.push_back(
createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path));
return;
- default:
+ case file_magic::bitcode:
+ case file_magic::elf_relocatable:
if (InLib)
Files.push_back(make<LazyObjFile>(MBRef, "", 0));
else
Files.push_back(createObjectFile(MBRef));
+ break;
+ default:
+ error(Path + ": unknown file type");
}
}
@@ -248,18 +260,11 @@ void LinkerDriver::addLibrary(StringRef Name) {
// LTO calls LLVM functions to compile bitcode files to native code.
// Technically this can be delayed until we read bitcode files, but
// we don't bother to do lazily because the initialization is fast.
-static void initLLVM(opt::InputArgList &Args) {
+static void initLLVM() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
-
- // Parse and evaluate -mllvm options.
- std::vector<const char *> V;
- V.push_back("lld (LLVM option parsing)");
- for (auto *Arg : Args.filtered(OPT_mllvm))
- V.push_back(Arg->getValue());
- cl::ParseCommandLineOptions(V.size(), V.data());
}
// Some command line options or some combinations of them are not allowed.
@@ -290,7 +295,9 @@ static void checkOptions(opt::InputArgList &Args) {
error("-r and -shared may not be used together");
if (Config->GcSections)
error("-r and --gc-sections may not be used together");
- if (Config->ICF)
+ if (Config->GdbIndex)
+ error("-r and --gdb-index may not be used together");
+ if (Config->ICF != ICFLevel::None)
error("-r and --icf may not be used together");
if (Config->Pie)
error("-r and -pie may not be used together");
@@ -310,7 +317,37 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
return false;
}
-void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
+static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2,
+ bool Default) {
+ for (auto *Arg : Args.filtered_reverse(OPT_z)) {
+ if (K1 == Arg->getValue())
+ return true;
+ if (K2 == Arg->getValue())
+ return false;
+ }
+ return Default;
+}
+
+static bool isKnown(StringRef S) {
+ return S == "combreloc" || S == "copyreloc" || S == "defs" ||
+ S == "execstack" || S == "hazardplt" || S == "initfirst" ||
+ S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" ||
+ S == "nocombreloc" || S == "nocopyreloc" || 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=");
+}
+
+// Report an error for an unknown -z option.
+static void checkZOptions(opt::InputArgList &Args) {
+ for (auto *Arg : Args.filtered(OPT_z))
+ if (!isKnown(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));
@@ -319,7 +356,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
// Handle -help
if (Args.hasArg(OPT_help)) {
- printHelp(ArgsArr[0]);
+ printHelp();
return;
}
@@ -348,9 +385,6 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
if (Args.hasArg(OPT_version))
return;
- Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown);
- errorHandler().ExitEarly = Config->ExitEarly;
-
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.
@@ -368,10 +402,14 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
}
readConfigs(Args);
- initLLVM(Args);
+ checkZOptions(Args);
+ initLLVM();
createFiles(Args);
+ if (errorCount())
+ return;
+
inferMachineType();
- setConfigs();
+ setConfigs(Args);
checkOptions(Args);
if (errorCount())
return;
@@ -482,6 +520,15 @@ static StringRef getDynamicLinker(opt::InputArgList &Args) {
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)
+ return ICFLevel::None;
+ if (Arg->getOption().getID() == OPT_icf_safe)
+ return ICFLevel::Safe;
+ return ICFLevel::All;
+}
+
static StripPolicy getStrip(opt::InputArgList &Args) {
if (Args.hasArg(OPT_relocatable))
return StripPolicy::None;
@@ -556,6 +603,8 @@ getBuildId(opt::InputArgList &Args) {
return {BuildIdKind::Fast, {}};
StringRef S = Arg->getValue();
+ if (S == "fast")
+ return {BuildIdKind::Fast, {}};
if (S == "md5")
return {BuildIdKind::Md5, {}};
if (S == "sha1" || S == "tree")
@@ -570,6 +619,57 @@ getBuildId(opt::InputArgList &Args) {
return {BuildIdKind::None, {}};
}
+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")
+ return {false, true};
+ if (S == "android+relr")
+ return {true, true};
+
+ if (S != "none")
+ error("unknown -pack-dyn-relocs format: " + S);
+ return {false, false};
+}
+
+static void readCallGraph(MemoryBufferRef MB) {
+ // Build a map from symbol name to section
+ DenseMap<StringRef, const Symbol *> SymbolNameToSymbol;
+ for (InputFile *File : ObjectFiles)
+ for (Symbol *Sym : File->getSymbols())
+ SymbolNameToSymbol[Sym->getName()] = Sym;
+
+ for (StringRef L : args::getLines(MB)) {
+ SmallVector<StringRef, 3> Fields;
+ L.split(Fields, ' ');
+ uint64_t Count;
+ if (Fields.size() != 3 || !to_integer(Fields[2], Count))
+ fatal(MB.getBufferIdentifier() + ": parse error");
+ const Symbol *FromSym = SymbolNameToSymbol.lookup(Fields[0]);
+ const Symbol *ToSym = SymbolNameToSymbol.lookup(Fields[1]);
+ if (Config->WarnSymbolOrdering) {
+ if (!FromSym)
+ warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[0]);
+ if (!ToSym)
+ warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[1]);
+ }
+ if (!FromSym || !ToSym || Count == 0)
+ continue;
+ warnUnorderableSymbol(FromSym);
+ warnUnorderableSymbol(ToSym);
+ const Defined *FromSymD = dyn_cast<Defined>(FromSym);
+ const Defined *ToSymD = dyn_cast<Defined>(ToSym);
+ if (!FromSymD || !ToSymD)
+ continue;
+ const auto *FromSB = dyn_cast_or_null<InputSectionBase>(FromSymD->Section);
+ const auto *ToSB = dyn_cast_or_null<InputSectionBase>(ToSymD->Section);
+ if (!FromSB || !ToSB)
+ continue;
+ Config->CallGraphProfile[std::make_pair(FromSB, ToSB)] += Count;
+ }
+}
+
static bool getCompressDebugSections(opt::InputArgList &Args) {
StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none");
if (S == "none")
@@ -581,54 +681,98 @@ static bool getCompressDebugSections(opt::InputArgList &Args) {
return true;
}
-static int parseInt(StringRef S, opt::Arg *Arg) {
- int V = 0;
- if (!to_integer(S, V, 10))
- error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
- return V;
+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;
+}
+
+// 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);
+
+ return Names.takeVector();
+}
+
+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))
+ return;
+ 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.hasArg(OPT_allow_multiple_definition) || hasZOption(Args, "muldefs");
+ 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->EmitRelocs = Args.hasArg(OPT_emit_relocs);
- Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
+ Config->EnableNewDtags =
+ Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
Config->Entry = Args.getLastArgValue(OPT_entry);
Config->ExportDynamic =
Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
- errorHandler().FatalWarnings =
- Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, 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 = Args.hasFlag(OPT_icf_all, OPT_icf_none, false);
- Config->ICFData = Args.hasArg(OPT_icf_data);
+ 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->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
+ 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->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
Config->OFormatBinary = isOutputFormatBinary(Args);
@@ -638,7 +782,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
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_nopie, false);
+ 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);
@@ -658,46 +804,58 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
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);
- ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
+ 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->Verbose = Args.hasArg(OPT_verbose);
- errorHandler().Verbose = Config->Verbose;
- Config->WarnCommon = Args.hasArg(OPT_warn_common);
- Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
- Config->ZExecstack = hasZOption(Args, "execstack");
- Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc");
+ 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->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->ZHazardplt = hasZOption(Args, "hazardplt");
+ Config->ZInitfirst = hasZOption(Args, "initfirst");
+ Config->ZKeepTextSectionPrefix = getZFlag(
+ Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
Config->ZNodelete = hasZOption(Args, "nodelete");
Config->ZNodlopen = hasZOption(Args, "nodlopen");
- Config->ZNow = hasZOption(Args, "now");
+ Config->ZNow = getZFlag(Args, "now", "lazy", false);
Config->ZOrigin = hasZOption(Args, "origin");
- Config->ZRelro = !hasZOption(Args, "norelro");
+ 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 = !hasZOption(Args, "notext");
+ Config->ZText = getZFlag(Args, "text", "notext", true);
Config->ZWxneeded = hasZOption(Args, "wxneeded");
- // Parse LTO plugin-related options for compatibility with gold.
- for (auto *Arg : Args.filtered(OPT_plugin_opt, OPT_plugin_opt_eq)) {
- StringRef S = Arg->getValue();
- if (S == "disable-verify")
- Config->DisableVerify = true;
- else if (S == "save-temps")
- Config->SaveTemps = true;
- else if (S.startswith("O"))
- Config->LTOO = parseInt(S.substr(1), Arg);
- else if (S.startswith("lto-partitions="))
- Config->LTOPartitions = parseInt(S.substr(15), Arg);
- else if (S.startswith("jobs="))
- Config->ThinLTOJobs = parseInt(S.substr(5), Arg);
- else if (!S.startswith("/") && !S.startswith("-fresolution=") &&
- !S.startswith("-pass-through=") && !S.startswith("mcpu=") &&
- !S.startswith("thinlto") && S != "-function-sections" &&
- S != "-data-sections")
- error(Arg->getSpelling() + ": unknown option: " + S);
- }
+ // Parse LTO options.
+ if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq))
+ parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())),
+ Arg->getSpelling());
+
+ for (auto *Arg : Args.filtered(OPT_plugin_opt))
+ parseClangOption(Arg->getValue(), Arg->getSpelling());
+
+ // Parse -mllvm options.
+ 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));
@@ -740,17 +898,12 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
- if (auto *Arg = Args.getLastArg(OPT_pack_dyn_relocs_eq)) {
- StringRef S = Arg->getValue();
- if (S == "android")
- Config->AndroidPackDynRelocs = true;
- else if (S != "none")
- error("unknown -pack-dyn-relocs format: " + S);
- }
+ 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 = args::getLines(*Buffer);
+ Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer);
// If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others.
@@ -778,32 +931,67 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
{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_version_script))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- readVersionScript(*Buffer);
+ 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());
+ }
}
// Some Config members do not directly correspond to any particular
// 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() {
+static void setConfigs(opt::InputArgList &Args) {
ELFKind Kind = Config->EKind;
uint16_t Machine = Config->EMachine;
- // There is an ILP32 ABI for x86-64, although it's not very popular.
- // It is called the x32 ABI.
- bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
-
Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
Config->Is64 = (Kind == ELF64LEKind || Kind == ELF64BEKind);
Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind);
Config->Endianness =
Config->IsLE ? support::endianness::little : support::endianness::big;
Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS);
- Config->IsRela = Config->Is64 || IsX32 || Config->MipsN32Abi;
Config->Pic = Config->Pie || Config->Shared;
Config->Wordsize = Config->Is64 ? 8 : 4;
+
+ // There is an ILP32 ABI for x86-64, although it's not very popular.
+ // It is called the x32 ABI.
+ bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
+
+ // ELF defines two different ways to store relocation addends as shown below:
+ //
+ // Rel: Addends are stored to the location where relocations are applied.
+ // Rela: Addends are stored as part of relocation entry.
+ //
+ // In other words, Rela makes it easy to read addends at the price of extra
+ // 4 or 8 byte for each relocation entry. We don't know why ELF defined two
+ // different mechanisms in the first place, but this is how the spec is
+ // defined.
+ //
+ // 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 =
+ (Config->Is64 || IsX32 || Machine == EM_PPC) && Machine != EM_MIPS;
+
+ // 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,
+ OPT_no_apply_dynamic_relocs, false) ||
+ !Config->IsRela;
}
// Returns a value of "-format" option.
@@ -818,6 +1006,10 @@ static bool getBinaryOption(StringRef S) {
}
void LinkerDriver::createFiles(opt::InputArgList &Args) {
+ // For --{push,pop}-state.
+ 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()) {
case OPT_library:
@@ -826,8 +1018,15 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
case OPT_INPUT:
addFile(Arg->getValue(), /*WithLOption=*/false);
break;
+ case OPT_defsym: {
+ StringRef From;
+ StringRef To;
+ std::tie(From, To) = StringRef(Arg->getValue()).split('=');
+ readDefsym(From, MemoryBufferRef(To, "-defsym"));
+ break;
+ }
case OPT_script:
- if (Optional<std::string> Path = searchLinkerScript(Arg->getValue())) {
+ if (Optional<std::string> Path = searchScript(Arg->getValue())) {
if (Optional<MemoryBufferRef> MB = readFile(*Path))
readLinkerScript(*MB);
break;
@@ -855,11 +1054,48 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
case OPT_no_whole_archive:
InWholeArchive = false;
break;
+ case OPT_just_symbols:
+ if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) {
+ Files.push_back(createObjectFile(*MB));
+ Files.back()->JustSymbols = true;
+ }
+ break;
+ case OPT_start_group:
+ if (InputFile::IsInGroup)
+ error("nested --start-group");
+ InputFile::IsInGroup = true;
+ break;
+ case OPT_end_group:
+ if (!InputFile::IsInGroup)
+ error("stray --end-group");
+ InputFile::IsInGroup = false;
+ ++InputFile::NextGroupId;
+ break;
case OPT_start_lib:
+ if (InLib)
+ error("nested --start-lib");
+ if (InputFile::IsInGroup)
+ error("may not nest --start-lib in --start-group");
InLib = true;
+ InputFile::IsInGroup = true;
break;
case OPT_end_lib:
+ if (!InLib)
+ error("stray --end-lib");
InLib = false;
+ InputFile::IsInGroup = false;
+ ++InputFile::NextGroupId;
+ break;
+ case OPT_push_state:
+ Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive);
+ break;
+ case OPT_pop_state:
+ if (Stack.empty()) {
+ error("unbalanced --push-state/--pop-state");
+ break;
+ }
+ std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back();
+ Stack.pop_back();
break;
}
}
@@ -932,14 +1168,6 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
return Ret;
}
-static Optional<StringRef> getArchiveName(InputFile *File) {
- if (isa<ArchiveFile>(File))
- return File->getName();
- if (!File->ArchiveName.empty())
- return File->ArchiveName;
- return None;
-}
-
// Handles the -exclude-libs option. If a static library file is specified
// by the -exclude-libs option, all public symbols from the archive become
// private unless otherwise specified by version scripts or something.
@@ -947,16 +1175,132 @@ static Optional<StringRef> getArchiveName(InputFile *File) {
//
// This is not a popular option, but some programs such as bionic libc use it.
template <class ELFT>
-static void excludeLibs(opt::InputArgList &Args, ArrayRef<InputFile *> Files) {
+static void excludeLibs(opt::InputArgList &Args) {
DenseSet<StringRef> Libs = getExcludeLibs(Args);
bool All = Libs.count("ALL");
- for (InputFile *File : Files)
- if (Optional<StringRef> Archive = getArchiveName(File))
- if (All || Libs.count(path::filename(*Archive)))
+ auto Visit = [&](InputFile *File) {
+ if (!File->ArchiveName.empty())
+ if (All || Libs.count(path::filename(File->ArchiveName)))
for (Symbol *Sym : File->getSymbols())
- if (!Sym->isLocal())
+ if (!Sym->isLocal() && Sym->File == File)
Sym->VersionId = VER_NDX_LOCAL;
+ };
+
+ for (InputFile *File : ObjectFiles)
+ 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)
+ 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;
+
+ if (Sym->isLazy())
+ Symtab->fetchLazy<ELFT>(Sym);
+}
+
+template <class ELFT> static bool shouldDemote(Symbol &Sym) {
+ // 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.
+ if (auto *S = dyn_cast<SharedSymbol>(&Sym))
+ return !S->getFile<ELFT>().IsNeeded;
+
+ // We are done processing archives, so lazy symbols that were used but not
+ // found can be converted to undefined. We could also just delete the other
+ // lazy symbols, but that seems to be more work than it is worth.
+ return Sym.isLazy() && Sym.IsUsedInRegularObj;
+}
+
+// Some files, such as .so or files between -{start,end}-lib may be removed
+// after their symbols are added to the symbol table. If that happens, we
+// need to remove symbols that refer files that no longer exist, so that
+// they won't appear in the symbol table of the output file.
+//
+// We remove symbols by demoting them to undefined symbol.
+template <class ELFT> static void demoteSymbols() {
+ for (Symbol *Sym : Symtab->getSymbols()) {
+ if (shouldDemote<ELFT>(*Sym)) {
+ bool Used = Sym->Used;
+ replaceSymbol<Undefined>(Sym, nullptr, Sym->getName(), Sym->Binding,
+ Sym->StOther, Sym->Type);
+ Sym->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)
+ // 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;
+}
+
+// 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");
+ continue;
+ }
+ 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)
+ 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);
+
+ // 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;
+ }
+ } 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);
+ }
+ }
}
// Do actual linking. Note that when this function is called,
@@ -1007,14 +1351,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
for (InputFile *F : Files)
Symtab->addFile<ELFT>(F);
- // Process -defsym option.
- for (auto *Arg : Args.filtered(OPT_defsym)) {
- StringRef From;
- StringRef To;
- std::tie(From, To) = StringRef(Arg->getValue()).split('=');
- readDefsym(From, MemoryBufferRef(To, "-defsym"));
- }
-
// Now that we have every file, we can decide if we will need a
// dynamic symbol table.
// We need one if we were asked to export dynamic symbols or if we are
@@ -1031,23 +1367,29 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Handle the `--undefined <sym>` options.
for (StringRef S : Config->Undefined)
- Symtab->fetchIfLazy<ELFT>(S);
+ handleUndefined<ELFT>(S);
// If an entry symbol is in a static archive, pull out that file now
// to complete the symbol table. After this, no new names except a
// few linker-synthesized ones will be added to the symbol table.
- Symtab->fetchIfLazy<ELFT>(Config->Entry);
+ handleUndefined<ELFT>(Config->Entry);
// Return if there were name resolution errors.
if (errorCount())
return;
- // Handle undefined symbols in DSOs.
- Symtab->scanShlibUndefined<ELFT>();
+ // Now when we read all script files, we want to finalize order of linker
+ // script commands, which can be not yet final because of INSERT commands.
+ Script->processInsertCommands();
+
+ // We want to declare linker script's symbols early,
+ // so that we can version them.
+ // They also might be exported if referenced by DSOs.
+ Script->declareSymbols();
// Handle the -exclude-libs option.
if (Args.hasArg(OPT_exclude_libs))
- excludeLibs<ELFT>(Args, Files);
+ excludeLibs<ELFT>(Args);
// Create ElfHeader early. We need a dummy section in
// addReservedSymbols to mark the created symbols as not absolute.
@@ -1059,16 +1401,29 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
addReservedSymbols();
// Apply version scripts.
- Symtab->scanVersionScript();
+ //
+ // 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();
// Create wrapped symbols for -wrap option.
for (auto *Arg : Args.filtered(OPT_wrap))
Symtab->addSymbolWrap<ELFT>(Arg->getValue());
+ // Do link-time optimization if given files are LLVM bitcode files.
+ // This compiles bitcode files into real object files.
Symtab->addCombinedLTOObject<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)
+ return;
+
// Apply symbol renames for -wrap.
Symtab->applySymbolWrap();
@@ -1115,11 +1470,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
// and identical code folding.
- markLive<ELFT>();
decompressSections();
+ splitSections<ELFT>();
+ markLive<ELFT>();
+ demoteSymbols<ELFT>();
mergeSections();
- if (Config->ICF)
+ if (Config->ICF != ICFLevel::None) {
+ findKeepUniqueSections<ELFT>(Args);
doIcf<ELFT>();
+ }
+
+ // Read the callgraph now that we know what was gced or icfed
+ if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
+ if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
+ readCallGraph(*Buffer);
// Write the result to the file.
writeResult<ELFT>();
diff --git a/ELF/Driver.h b/ELF/Driver.h
index 351d7926de71..99e194d9b66c 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -26,7 +26,7 @@ extern class LinkerDriver *Driver;
class LinkerDriver {
public:
- void main(ArrayRef<const char *> Args, bool CanExitEarly);
+ void main(ArrayRef<const char *> Args);
void addFile(StringRef Path, bool WithLOption);
void addLibrary(StringRef Name);
@@ -63,11 +63,11 @@ enum {
#undef OPTION
};
-void printHelp(const char *Argv0);
+void printHelp();
std::string createResponseFile(const llvm::opt::InputArgList &Args);
llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
-llvm::Optional<std::string> searchLinkerScript(StringRef Path);
+llvm::Optional<std::string> searchScript(StringRef Path);
llvm::Optional<std::string> searchLibrary(StringRef Path);
} // namespace elf
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index 2f7c9228851a..698e06edfe63 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -29,6 +29,7 @@
using namespace llvm;
using namespace llvm::sys;
+using namespace llvm::opt;
using namespace lld;
using namespace lld::elf;
@@ -58,18 +59,18 @@ static void handleColorDiagnostics(opt::InputArgList &Args) {
OPT_no_color_diagnostics);
if (!Arg)
return;
- else if (Arg->getOption().getID() == OPT_color_diagnostics)
+ if (Arg->getOption().getID() == OPT_color_diagnostics) {
errorHandler().ColorDiagnostics = true;
- else if (Arg->getOption().getID() == OPT_no_color_diagnostics)
+ } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
errorHandler().ColorDiagnostics = false;
- else {
+ } 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);
+ error("unknown option: --color-diagnostics=" + S);
}
}
@@ -87,6 +88,29 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
return cl::TokenizeGNUCommandLine;
}
+// Gold LTO plugin takes a `--plugin-opt foo=bar` option as an alias for
+// `--plugin-opt=foo=bar`. We want to handle `--plugin-opt=foo=` as an
+// option name and `bar` as a value. Unfortunately, OptParser cannot
+// handle an option with a space in it.
+//
+// In this function, we concatenate command line arguments so that
+// `--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;
+ } else {
+ V.push_back(Args[I]);
+ }
+ }
+ Args = std::move(V);
+}
+
// Parses a given list of options.
opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
// Make InputArgList from string vectors.
@@ -102,6 +126,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
// 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);
@@ -113,9 +138,9 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
return Args;
}
-void elf::printHelp(const char *Argv0) {
- ELFOptTable().PrintHelp(outs(), Argv0, "lld", false /*ShowHidden*/,
- true /*ShowAllAliases*/);
+void elf::printHelp() {
+ ELFOptTable().PrintHelp(outs(), Config->ProgName.data(), "lld",
+ false /*ShowHidden*/, true /*ShowAllAliases*/);
outs() << "\n";
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
@@ -123,13 +148,7 @@ void elf::printHelp(const char *Argv0) {
// 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".
- // Here, we print out all the targets that we support.
- outs() << Argv0 << ": supported targets: "
- << "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmips "
- << "elf32-ntradlittlemips elf32-powerpc elf32-tradbigmips "
- << "elf32-tradlittlemips elf32-x86-64 "
- << "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmips "
- << "elf64-tradlittlemips elf64-x86-64\n";
+ outs() << Config->ProgName << ": supported targets: elf\n";
}
// Reconstructs command line arguments so that so that you can re-run
@@ -208,10 +227,10 @@ Optional<std::string> elf::searchLibrary(StringRef Name) {
return None;
}
-// If a linker script doesn't exist in the current directory, we also look for
-// the script in the '-L' search paths. This matches the behaviour of both '-T'
-// and linker script INPUT() directives in ld.bfd.
-Optional<std::string> elf::searchLinkerScript(StringRef Name) {
+// If a linker/version script doesn't exist in the current directory, we also
+// look for the script in the '-L' search paths. This matches the behaviour of
+// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
+Optional<std::string> 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 62abc3973e7e..20b32c0a96e6 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -20,18 +20,16 @@
#include "Config.h"
#include "InputSection.h"
#include "Relocations.h"
-#include "Strings.h"
-
+#include "Target.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Strings.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::dwarf;
using namespace llvm::object;
-using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
@@ -73,7 +71,7 @@ size_t EhReader::readEhRecordSize() {
// 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(), Config->Endianness);
+ uint64_t V = read32(D.data());
if (V == UINT32_MAX)
failOn(D.data(), "CIE/FDE too large");
uint64_t Size = V + 4;
diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp
index 8d0b5d8a2f1c..5cf240eeca56 100644
--- a/ELF/Filesystem.cpp
+++ b/ELF/Filesystem.cpp
@@ -44,7 +44,7 @@ using namespace lld::elf;
// The calling thread returns almost immediately.
void elf::unlinkAsync(StringRef Path) {
// Removing a file is async on windows.
-#if defined(LLVM_ON_WIN32)
+#if defined(_WIN32)
sys::fs::remove(Path);
#else
if (!ThreadsEnabled || !sys::fs::exists(Path) ||
diff --git a/ELF/GdbIndex.cpp b/ELF/GdbIndex.cpp
index d27b57f95938..85449a200647 100644
--- a/ELF/GdbIndex.cpp
+++ b/ELF/GdbIndex.cpp
@@ -34,7 +34,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
.Case(".debug_ranges", &RangeSection)
.Case(".debug_line", &LineSection)
.Default(nullptr)) {
- Sec->maybeUncompress();
+ Sec->maybeDecompress();
M->Data = toStringRef(Sec->Data);
M->Sec = Sec;
continue;
diff --git a/ELF/GdbIndex.h b/ELF/GdbIndex.h
index 41ae9d793c11..eba1ba22f879 100644
--- a/ELF/GdbIndex.h
+++ b/ELF/GdbIndex.h
@@ -49,7 +49,6 @@ public:
return LineSection;
}
StringRef getFileName() const override { return ""; }
- StringRef getCUIndexSection() const override { return ""; }
StringRef getAbbrevSection() const override { return AbbrevSection; }
StringRef getStringSection() const override { return StrSection; }
StringRef getGnuPubNamesSection() const override {
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index b1e12e0590d5..ba413b132658 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -77,6 +77,8 @@
#include "Config.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Writer.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -112,9 +114,9 @@ private:
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);
+ llvm::function_ref<void(size_t, size_t)> Fn);
- void forEachClass(std::function<void(size_t, size_t)> Fn);
+ void forEachClass(llvm::function_ref<void(size_t, size_t)> Fn);
std::vector<InputSection *> Sections;
@@ -161,15 +163,38 @@ template <class ELFT> static uint32_t getHash(InputSection *S) {
// Returns true if section S is subject of ICF.
static bool isEligible(InputSection *S) {
- // Don't merge read only data sections unless --icf-data was passed.
- if (!(S->Flags & SHF_EXECINSTR) && !Config->ICFData)
+ if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC))
return false;
- // .init and .fini contains instructions that must be executed to
- // initialize and finalize the process. They cannot and should not
- // be merged.
- return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
- S->Name != ".init" && S->Name != ".fini";
+ // 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."))
+ 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)
+ 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))
+ 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")
+ 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))
+ return false;
+
+ return true;
}
// Split an equivalence class into smaller classes.
@@ -214,9 +239,6 @@ template <class ELFT>
template <class RelTy>
bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
const InputSection *SecB, ArrayRef<RelTy> RB) {
- if (RA.size() != RB.size())
- return false;
-
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))
@@ -284,6 +306,13 @@ bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
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))
+ return false;
+
if (A->AreRelocsRela)
return constantEq(A, A->template relas<ELFT>(), B,
B->template relas<ELFT>());
@@ -350,17 +379,12 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
// vector. Therefore, Sections vector can be considered as contiguous
// groups of sections, grouped by the class.
//
-// This function calls Fn on every group that starts within [Begin, End).
-// Note that a group must start in that range but doesn't necessarily
-// have to end before End.
+// This function calls Fn on every group within [Begin, End).
template <class ELFT>
void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
- std::function<void(size_t, size_t)> Fn) {
- if (Begin > 0)
- Begin = findBoundary(Begin - 1, End);
-
+ llvm::function_ref<void(size_t, size_t)> Fn) {
while (Begin < End) {
- size_t Mid = findBoundary(Begin, Sections.size());
+ size_t Mid = findBoundary(Begin, End);
Fn(Begin, Mid);
Begin = Mid;
}
@@ -368,7 +392,7 @@ void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
// Call Fn on each equivalence class.
template <class ELFT>
-void ICF<ELFT>::forEachClass(std::function<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) {
@@ -380,16 +404,32 @@ void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
Current = Cnt % 2;
Next = (Cnt + 1) % 2;
- // Split sections into 256 shards and call Fn in parallel.
- size_t NumShards = 256;
+ // 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;
- parallelForEachN(0, NumShards, [&](size_t I) {
- size_t End = (I == NumShards - 1) ? Sections.size() : (I + 1) * Step;
- forEachClassRange(I * Step, End, Fn);
+ 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);
});
++Cnt;
}
+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.
@@ -401,7 +441,7 @@ template <class ELFT> void ICF<ELFT>::run() {
// Initially, we use hash values to partition sections.
parallelForEach(Sections, [&](InputSection *S) {
// Set MSB to 1 to avoid collisions with non-hash IDs.
- S->Class[0] = getHash<ELFT>(S) | (1 << 31);
+ S->Class[0] = getHash<ELFT>(S) | (1U << 31);
});
// From now on, sections in Sections vector are ordered so that sections
@@ -424,25 +464,21 @@ template <class ELFT> void ICF<ELFT>::run() {
log("ICF needed " + Twine(Cnt) + " iterations");
// Merge sections by the equivalence class.
- forEachClass([&](size_t Begin, size_t End) {
+ forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) {
if (End - Begin == 1)
return;
-
- log("selected " + Sections[Begin]->Name);
+ print("selected section " + toString(Sections[Begin]));
for (size_t I = Begin + 1; I < End; ++I) {
- log(" removed " + Sections[I]->Name);
+ 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;
}
});
-
- // Mark ARM Exception Index table sections that refer to folded code
- // sections as not live. These sections have an implict dependency
- // via the link order dependency.
- if (Config->EMachine == EM_ARM)
- for (InputSectionBase *Sec : InputSections)
- if (auto *S = dyn_cast<InputSection>(Sec))
- if (S->Flags & SHF_LINK_ORDER)
- S->Live = S->getLinkOrderDep()->Live;
}
// ICF entry point function.
diff --git a/ELF/ICF.h b/ELF/ICF.h
index 24219855fc17..a6c8636ead6d 100644
--- a/ELF/ICF.h
+++ b/ELF/ICF.h
@@ -12,8 +12,10 @@
namespace lld {
namespace elf {
+
template <class ELFT> void doIcf();
-}
+
+} // namespace elf
} // namespace lld
#endif
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index f514870ca84a..6da722f6f30e 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -38,14 +38,23 @@ using namespace llvm::sys::fs;
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;
TarWriter *elf::Tar;
-InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), 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;
+}
Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
// The --chroot option changes our virtual root directory.
@@ -55,7 +64,7 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
log(Path);
- auto MBOrErr = MemoryBuffer::getFile(Path);
+ auto MBOrErr = MemoryBuffer::getFile(Path, -1, false);
if (auto EC = MBOrErr.getError()) {
error("cannot open " + Path + ": " + EC.message());
return None;
@@ -115,51 +124,60 @@ std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
}
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
- DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
- const DWARFObject &Obj = Dwarf.getDWARFObj();
- DwarfLine.reset(new DWARFDebugLine);
+ Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
+ const DWARFObject &Obj = Dwarf->getDWARFObj();
DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
Config->Wordsize);
- // The second parameter is offset in .debug_line section
- // for compilation unit (CU) of interest. We have only one
- // CU (object file), so offset is always 0.
- // FIXME: Provide the associated DWARFUnit if there is one. DWARF v5
- // needs it in order to find indirect strings.
- const DWARFDebugLine::LineTable *LT =
- DwarfLine->getOrParseLineTable(LineData, 0, nullptr);
-
- // Return if there is no debug information about CU available.
- if (!Dwarf.getNumCompileUnits())
- return;
-
- // Loop over variable records and insert them to VariableLoc.
- DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0);
- for (const auto &Entry : CU->dies()) {
- DWARFDie Die(CU, &Entry);
- // Skip all tags that are not variables.
- if (Die.getTag() != dwarf::DW_TAG_variable)
+ for (std::unique_ptr<DWARFCompileUnit> &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;
+ else
+ Report(ExpectedLT.takeError());
+ if (!LT)
continue;
+ LineTables.push_back(LT);
- // 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))
- continue;
+ // 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)
+ 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))
- 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))
+ continue;
- // Get the line number on which the variable is declared.
- unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
+ // Get the source filename index for the variable.
+ unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
+ if (!LT->hasFileAtIndex(File))
+ continue;
- // Get the name of the variable and add the collected information to
- // VariableLoc. Usually Name is non-empty, but it can be empty if the input
- // object file lacks some debug info.
- StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
- if (!Name.empty())
- VariableLoc.insert({Name, {File, Line}});
+ // Get the line number on which the variable is declared.
+ 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.
+ // 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}});
+ }
}
}
@@ -170,11 +188,6 @@ Optional<std::pair<std::string, unsigned>>
ObjFile<ELFT>::getVariableLoc(StringRef Name) {
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
- // There is always only one CU so it's offset is 0.
- const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0);
- if (!LT)
- return None;
-
// Return if we have no debug information about data object.
auto It = VariableLoc.find(Name);
if (It == VariableLoc.end())
@@ -182,12 +195,12 @@ ObjFile<ELFT>::getVariableLoc(StringRef Name) {
// Take file name string from line table.
std::string FileName;
- if (!LT->getFileNameByIndex(
- It->second.first /* File */, nullptr,
+ if (!It->second.LT->getFileNameByIndex(
+ It->second.File, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
return None;
- return std::make_pair(FileName, It->second.second /*Line*/);
+ return std::make_pair(FileName, It->second.Line);
}
// Returns source line information for a given offset
@@ -197,29 +210,15 @@ Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
uint64_t Offset) {
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
- // The offset to CU is 0.
- const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
- if (!Tbl)
- return None;
-
// Use fake address calcuated by adding section file offset and offset in
// section. See comments for ObjectInfo class.
DILineInfo Info;
- Tbl->getFileLineInfoForAddress(
- S->getOffsetInFile() + Offset, nullptr,
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info);
- if (Info.Line == 0)
- return None;
- return Info;
-}
-
-// Returns source line information for a given offset
-// using DWARF debug info.
-template <class ELFT>
-std::string ObjFile<ELFT>::getLineInfo(InputSectionBase *S, uint64_t Offset) {
- if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
- return Info->FileName + ":" + std::to_string(Info->Line);
- return "";
+ for (const llvm::DWARFDebugLine::LineTable *LT : LineTables)
+ if (LT->getFileLineInfoForAddress(
+ S->getOffsetInFile() + Offset, nullptr,
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info))
+ return Info;
+ return None;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
@@ -249,7 +248,7 @@ ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
template <class ELFT>
typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
- return makeArrayRef(ELFSyms.begin() + FirstNonLocal, ELFSyms.end());
+ return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end());
}
template <class ELFT>
@@ -260,9 +259,9 @@ uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
template <class ELFT>
void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
const Elf_Shdr *Symtab) {
- FirstNonLocal = Symtab->sh_info;
+ FirstGlobal = Symtab->sh_info;
ELFSyms = CHECK(getObj().symbols(Symtab), this);
- if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size())
+ if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
StringTable =
@@ -278,13 +277,22 @@ ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName)
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
if (this->Symbols.empty())
return {};
- return makeArrayRef(this->Symbols).slice(1, this->FirstNonLocal - 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);
}
template <class ELFT>
void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
- // Read section and symbol tables.
- initializeSections(ComdatGroups);
+ // Read a section table. JustSymbols is usually false.
+ if (this->JustSymbols)
+ initializeJustSymbols();
+ else
+ initializeSections(ComdatGroups);
+
+ // Read a symbol table.
initializeSymbols();
}
@@ -308,7 +316,7 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
// we use a section name as a signature.
//
// Such SHT_GROUP sections are invalid from the perspective of the ELF
- // standard, but GNU gold 1.14 (the neweset version as of July 2017) or
+ // 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)
@@ -328,9 +336,19 @@ ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
}
template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
- // We don't merge sections if -O0 (default is -O1). This makes sometimes
- // the linker significantly faster, although the output will be bigger.
- if (Config->Optimize == 0)
+ // 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.
+ //
+ // Doing the same for -r would create a problem as it would combine sections
+ // with different sh_entsize. One option would be to just copy every SHF_MERGE
+ // section as is to the output. While this would produce a valid ELF file with
+ // usable SHF_MERGE sections, tools like (llvm-)?dwarfdump get confused when
+ // they see two .debug_str. We could have separate logic for combining
+ // 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)
return false;
// A mergeable section with size 0 is useless because they don't have
@@ -361,12 +379,33 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
return true;
}
+// This is for --just-symbols.
+//
+// --just-symbols is a very minor feature that allows you to link your
+// output against other existing program, so that if you load both your
+// program and the other program into memory, your output can refer the
+// other program's symbols.
+//
+// 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());
+
+ for (const Elf_Shdr &Sec : ObjSections) {
+ if (Sec.sh_type != SHT_SYMTAB)
+ continue;
+ this->initSymtab(ObjSections, &Sec);
+ return;
+ }
+}
+
template <class ELFT>
void ObjFile<ELFT>::initializeSections(
DenseSet<CachedHashStringRef> &ComdatGroups) {
const ELFFile<ELFT> &Obj = this->getObj();
- ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
+ ArrayRef<Elf_Shdr> ObjSections = CHECK(Obj.sections(), this);
uint64_t Size = ObjSections.size();
this->Sections.resize(Size);
this->SectionStringTable =
@@ -381,6 +420,17 @@ void ObjFile<ELFT>::initializeSections(
// 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) {
+ // 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)
+ warn(toString(this) + ": --icf=safe is incompatible with object "
+ "files created using objcopy or ld -r");
+ }
this->Sections[I] = &InputSection::Discarded;
continue;
}
@@ -431,8 +481,15 @@ void ObjFile<ELFT>::initializeSections(
if (Sec.sh_link >= this->Sections.size())
fatal(toString(this) +
": invalid sh_link index: " + Twine(Sec.sh_link));
- this->Sections[Sec.sh_link]->DependentSections.push_back(
- cast<InputSection>(this->Sections[I]));
+
+ InputSectionBase *LinkSec = this->Sections[Sec.sh_link];
+ InputSection *IS = cast<InputSection>(this->Sections[I]);
+ LinkSec->DependentSections.push_back(IS);
+ if (!isa<InputSection>(LinkSec))
+ error("a section " + IS->Name +
+ " with SHF_LINK_ORDER should not refer a non-regular "
+ "section: " +
+ toString(LinkSec));
}
}
}
@@ -527,10 +584,11 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
}
case SHT_RELA:
case SHT_REL: {
- // Find the relocation target section and associate this
- // section with it. Target can be discarded, for example
- // if it is a duplicated member of SHT_GROUP section, we
- // do not create or proccess relocatable sections then.
+ // Find a relocation target section and associate this section with that.
+ // Target may have been discarded if it is in a different section group
+ // 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)
return nullptr;
@@ -545,32 +603,28 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
fatal(toString(this) +
": multiple relocation sections to one section are not supported");
- // Mergeable sections with relocations are tricky because relocations
- // need to be taken into account when comparing section contents for
- // merging. It's not worth supporting such mergeable sections because
- // they are rare and it'd complicates the internal design (we usually
- // have to determine if two sections are mergeable early in the link
- // process much before applying relocations). We simply handle mergeable
- // sections with relocations as non-mergeable.
+ // ELF spec allows mergeable sections with relocations, but they are
+ // rare, and it is in practice hard to merge such sections by contents,
+ // 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;
}
- size_t NumRelocations;
if (Sec.sh_type == SHT_RELA) {
ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
Target->FirstRelocation = Rels.begin();
- NumRelocations = Rels.size();
+ Target->NumRelocations = Rels.size();
Target->AreRelocsRela = true;
} else {
ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
Target->FirstRelocation = Rels.begin();
- NumRelocations = Rels.size();
+ Target->NumRelocations = Rels.size();
Target->AreRelocsRela = false;
}
- assert(isUInt<31>(NumRelocations));
- Target->NumRelocations = 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.
@@ -602,13 +656,24 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
if (Name == ".note.GNU-stack")
return &InputSection::Discarded;
- // Split stacks is a feature to support a discontiguous stack. At least
- // as of 2017, it seems that the feature is not being used widely.
- // Only GNU gold supports that. We don't. For the details about that,
- // see https://gcc.gnu.org/wiki/SplitStacks
+ // 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") {
- error(toString(this) +
- ": object file compiled with -fsplit-stack is not supported");
+ if (Config->Relocatable) {
+ error("Cannot mix split-stack and non-split-stack in a relocatable link");
+ 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;
}
@@ -620,6 +685,14 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
if (Name.startswith(".gnu.linkonce."))
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;
+
// 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.
@@ -701,33 +774,33 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
File(std::move(File)) {}
template <class ELFT> void ArchiveFile::parse() {
- Symbols.reserve(File->getNumberOfSymbols());
for (const Archive::Symbol &Sym : File->symbols())
- Symbols.push_back(Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym));
+ Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
-std::pair<MemoryBufferRef, uint64_t>
-ArchiveFile::getMember(const Archive::Symbol *Sym) {
+InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) {
Archive::Child C =
- CHECK(Sym->getMember(), toString(this) +
- ": could not get the member for symbol " +
- Sym->getName());
+ CHECK(Sym.getMember(), toString(this) +
+ ": could not get the member for symbol " +
+ Sym.getName());
if (!Seen.insert(C.getChildOffset()).second)
- return {MemoryBufferRef(), 0};
+ return nullptr;
- MemoryBufferRef Ret =
+ 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 (C.getParent()->isThin() && Tar)
- Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), Ret.getBuffer());
- if (C.getParent()->isThin())
- return {Ret, 0};
- return {Ret, C.getChildOffset()};
+ InputFile *File = createObjectFile(
+ MB, getName(), C.getParent()->isThin() ? 0 : C.getChildOffset());
+ File->GroupId = GroupId;
+ return File;
}
template <class ELFT>
@@ -784,34 +857,42 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
}
}
+// 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;
+}
+
// 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. The array
-// always has at least length 1.
+// n. Version identifiers that are not definitions map to nullptr.
template <class ELFT>
-std::vector<const typename ELFT::Verdef *>
-SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
- std::vector<const Elf_Verdef *> Verdefs(1);
- // We only need to process symbol versions for this DSO if it has both a
- // versym and a verdef section, which indicates that the DSO contains symbol
- // version definitions.
- if (!VersymSec || !VerdefSec)
- return Verdefs;
-
- // The location of the first global versym entry.
- const char *Base = this->MB.getBuffer().data();
- Versym = reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
- this->FirstNonLocal;
+std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
+ if (!VerdefSec)
+ 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;
- Verdefs.resize(VerdefCount + 1);
+ std::vector<const Elf_Verdef *> 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);
@@ -825,74 +906,99 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
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;
+}
+
// Fully parse the shared object file. This must be called after parseSoName().
+//
+// This function parses symbol versions. If a DSO has version information,
+// the file has a ".gnu.version_d" section which contains symbol version
+// definitions. Each symbol is associated to one version through a table in
+// ".gnu.version" section. That table is a parallel array for the symbol
+// table, and each table entry contains an index in ".gnu.version_d".
+//
+// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for
+// VER_NDX_GLOBAL. There's no table entry for these special versions in
+// ".gnu.version_d".
+//
+// 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() {
- // Create mapping from version identifiers to Elf_Verdef entries.
- const Elf_Versym *Versym = nullptr;
- Verdefs = parseVerdefs(Versym);
-
+ Verdefs = parseVerdefs(); // parse .gnu.version_d
+ std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version
ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
+ // 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;
+
// Add symbols to the symbol table.
- Elf_Sym_Range Syms = this->getGlobalELFSyms();
- for (const Elf_Sym &Sym : Syms) {
- unsigned VersymIndex = VER_NDX_GLOBAL;
- if (Versym) {
- VersymIndex = Versym->vs_index;
- ++Versym;
- }
- bool Hidden = VersymIndex & VERSYM_HIDDEN;
- VersymIndex = VersymIndex & ~VERSYM_HIDDEN;
+ ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms();
+ for (size_t I = 0; I < Syms.size(); ++I) {
+ const Elf_Sym &Sym = Syms[I];
StringRef Name = CHECK(Sym.getName(this->StringTable), this);
if (Sym.isUndefined()) {
- Undefs.push_back(Name);
+ Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
+ Sym.st_other, Sym.getType(),
+ /*CanOmitFromDynSym=*/false, this);
+ S->ExportDynamic = true;
continue;
}
+ // 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.
if (Sym.getBinding() == STB_LOCAL) {
warn("found local symbol '" + Name +
"' in global part of symbol table in file " + toString(this));
continue;
}
- const Elf_Verdef *Ver = nullptr;
- if (VersymIndex != VER_NDX_GLOBAL) {
- if (VersymIndex >= Verdefs.size() || VersymIndex == VER_NDX_LOCAL) {
- error("corrupt input file: version definition index " +
- Twine(VersymIndex) + " for symbol " + Name +
- " is out of bounds\n>>> defined in " + toString(this));
- continue;
- }
- Ver = Verdefs[VersymIndex];
- } else {
- VersymIndex = 0;
- }
-
- // 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 here.
- uint64_t Alignment = 1;
- if (Sym.st_value)
- Alignment = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
- if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) {
- uint64_t SecAlign = Sections[Sym.st_shndx].sh_addralign;
- Alignment = std::min(Alignment, SecAlign);
- }
- if (Alignment > UINT32_MAX)
- error(toString(this) + ": alignment too large: " + Name);
+ // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
+ // assigns VER_NDX_LOCAL to this section global symbol. Here is a
+ // workaround for this bug.
+ uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN;
+ if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL &&
+ Name == "_gp_disp")
+ continue;
- if (!Hidden)
- Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex);
+ uint64_t Alignment = getAlignment(Sections, Sym);
+ if (!(Versyms[I] & VERSYM_HIDDEN))
+ Symtab->addShared(Name, *this, Sym, Alignment, Idx);
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
- if (Ver) {
- StringRef VerName = this->StringTable.data() + Ver->getAux()->vda_name;
- Name = Saver.save(Name + "@" + VerName);
- Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex);
+ 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 " +
+ 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);
}
}
@@ -925,8 +1031,9 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
case Triple::x86_64:
return EM_X86_64;
default:
- fatal(Path + ": could not infer e_machine from bitcode target triple " +
+ error(Path + ": could not infer e_machine from bitcode target triple " +
T.str());
+ return EM_NONE;
}
}
@@ -935,17 +1042,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
: InputFile(BitcodeKind, MB) {
this->ArchiveName = ArchiveName;
- // Here we pass a new MemoryBufferRef which is identified by ArchiveName
- // (the fully resolved path of the archive) + member name + offset of the
- // member in the archive.
- // ThinLTO uses the MemoryBufferRef identifier to access its internal
- // data structures and 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).
- MemoryBufferRef MBRef(MB.getBuffer(),
- Saver.save(ArchiveName + MB.getBufferIdentifier() +
- utostr(OffsetInArchive)));
+ 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), this);
Triple T(Obj->getTargetTriple());
@@ -969,7 +1080,7 @@ template <class ELFT>
static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
const lto::InputFile::Symbol &ObjSym,
BitcodeFile &F) {
- StringRef NameRef = Saver.save(ObjSym.getName());
+ StringRef Name = Saver.save(ObjSym.getName());
uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL;
uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE;
@@ -978,20 +1089,20 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
int C = ObjSym.getComdatIndex();
if (C != -1 && !KeptComdats[C])
- return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type,
+ return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
CanOmitFromDynSym, &F);
if (ObjSym.isUndefined())
- return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type,
+ return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
CanOmitFromDynSym, &F);
if (ObjSym.isCommon())
- return Symtab->addCommon(NameRef, ObjSym.getCommonSize(),
+ return Symtab->addCommon(Name, ObjSym.getCommonSize(),
ObjSym.getCommonAlignment(), Binding, Visibility,
STT_OBJECT, F);
- return Symtab->addBitcode(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, F);
+ return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym,
+ F);
}
template <class ELFT>
@@ -1026,8 +1137,8 @@ static ELFKind getELFKind(MemoryBufferRef MB) {
void BinaryFile::parse() {
ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer());
- auto *Section = make<InputSection>(nullptr, SHF_ALLOC | SHF_WRITE,
- SHT_PROGBITS, 8, Data, ".data");
+ 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
@@ -1047,11 +1158,6 @@ void BinaryFile::parse() {
Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
}
-static bool isBitcode(MemoryBufferRef MB) {
- using namespace sys::fs;
- return identify_magic(MB.getBuffer()) == file_magic::bitcode;
-}
-
InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
uint64_t OffsetInArchive) {
if (isBitcode(MB))
@@ -1087,9 +1193,9 @@ InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
}
MemoryBufferRef LazyObjFile::getBuffer() {
- if (Seen)
+ if (AddedToLink)
return MemoryBufferRef();
- Seen = true;
+ AddedToLink = true;
return MB;
}
@@ -1097,66 +1203,72 @@ InputFile *LazyObjFile::fetch() {
MemoryBufferRef MBRef = getBuffer();
if (MBRef.getBuffer().empty())
return nullptr;
- return createObjectFile(MBRef, ArchiveName, OffsetInArchive);
+
+ InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive);
+ File->GroupId = GroupId;
+ return File;
}
template <class ELFT> void LazyObjFile::parse() {
- for (StringRef Sym : getSymbolNames())
- Symtab->addLazyObject<ELFT>(Sym, *this);
+ // 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);
+ return;
+ }
+
+ switch (getELFKind(this->MB)) {
+ case ELF32LEKind:
+ addElfSymbols<ELF32LE>();
+ return;
+ case ELF32BEKind:
+ addElfSymbols<ELF32BE>();
+ return;
+ case ELF64LEKind:
+ addElfSymbols<ELF64LE>();
+ return;
+ case ELF64BEKind:
+ addElfSymbols<ELF64BE>();
+ return;
+ default:
+ llvm_unreachable("getELFKind");
+ }
}
-template <class ELFT> std::vector<StringRef> LazyObjFile::getElfSymbols() {
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::SymRange Elf_Sym_Range;
+template <class ELFT> void LazyObjFile::addElfSymbols() {
+ ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer()));
+ ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this);
- ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer()));
- ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
- for (const Elf_Shdr &Sec : Sections) {
+ for (const typename ELFT::Shdr &Sec : Sections) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
- Elf_Sym_Range Syms = CHECK(Obj.symbols(&Sec), this);
- uint32_t FirstNonLocal = Sec.sh_info;
+ typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this);
+ uint32_t FirstGlobal = Sec.sh_info;
StringRef StringTable =
CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
- std::vector<StringRef> V;
- for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
+ for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal))
if (Sym.st_shndx != SHN_UNDEF)
- V.push_back(CHECK(Sym.getName(StringTable), this));
- return V;
+ Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this),
+ *this);
+ return;
}
- return {};
}
-std::vector<StringRef> LazyObjFile::getBitcodeSymbols() {
- std::unique_ptr<lto::InputFile> Obj =
- CHECK(lto::InputFile::create(this->MB), this);
- std::vector<StringRef> V;
- for (const lto::InputFile::Symbol &Sym : Obj->symbols())
- if (!Sym.isUndefined())
- V.push_back(Saver.save(Sym.getName()));
- return V;
-}
+std::string elf::replaceThinLTOSuffix(StringRef Path) {
+ StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first;
+ StringRef Repl = Config->ThinLTOObjectSuffixReplace.second;
-// Returns a vector of globally-visible defined symbol names.
-std::vector<StringRef> LazyObjFile::getSymbolNames() {
- if (isBitcode(this->MB))
- return getBitcodeSymbols();
-
- switch (getELFKind(this->MB)) {
- case ELF32LEKind:
- return getElfSymbols<ELF32LE>();
- case ELF32BEKind:
- return getElfSymbols<ELF32BE>();
- case ELF64LEKind:
- return getElfSymbols<ELF64LE>();
- case ELF64BEKind:
- return getElfSymbols<ELF64BE>();
- default:
- llvm_unreachable("getELFKind");
+ if (!Path.endswith(Suffix)) {
+ error("-thinlto-object-suffix-replace=" + Suffix + ";" + Repl +
+ " was given, but " + Path + " does not end with the suffix");
+ return "";
}
+ return (Path.drop_back(Suffix.size()) + Repl).str();
}
template void ArchiveFile::parse<ELF32LE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index dda1de81570c..0db3203b0ba2 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -12,22 +12,20 @@
#include "Config.h"
#include "lld/Common/ErrorHandler.h"
-
#include "lld/Common/LLVM.h"
#include "lld/Common/Reproduce.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/Threading.h"
-
#include <map>
namespace llvm {
-class DWARFDebugLine;
class TarWriter;
struct DILineInfo;
namespace lto {
@@ -48,7 +46,6 @@ namespace elf {
using llvm::object::Archive;
-class Lazy;
class Symbol;
// If -reproduce option is given, all input files are written
@@ -90,15 +87,15 @@ public:
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
ArrayRef<Symbol *> getSymbols() {
- assert(FileKind == ObjKind || FileKind == BitcodeKind ||
- FileKind == ArchiveKind);
+ 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.
- StringRef 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.
@@ -112,6 +109,20 @@ public:
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
+ // 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;
+
+ // Index of MIPS GOT built for this file.
+ llvm::Optional<size_t> MipsGotIndex;
+
protected:
InputFile(Kind K, MemoryBufferRef M);
std::vector<InputSectionBase *> Sections;
@@ -144,7 +155,7 @@ public:
protected:
ArrayRef<Elf_Sym> ELFSyms;
- uint32_t FirstNonLocal = 0;
+ uint32_t FirstGlobal = 0;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
@@ -167,6 +178,7 @@ public:
static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
ArrayRef<Symbol *> getLocalSymbols();
+ ArrayRef<Symbol *> getGlobalSymbols();
ObjFile(MemoryBufferRef M, StringRef ArchiveName);
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
@@ -182,9 +194,6 @@ public:
return getSymbol(SymIndex);
}
- // Returns source line information for a given offset.
- // If no information is available, returns "".
- std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
@@ -198,10 +207,22 @@ public:
// symbol table.
StringRef SourceFile;
+ // True if the file defines functions compiled with
+ // -fsplit-stack. Usually 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;
+
+ // Pointer to this input file's .llvm_addrsig section, if it has one.
+ const Elf_Shdr *AddrsigSec = nullptr;
+
private:
void
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
void initializeSymbols();
+ void initializeJustSymbols();
void initializeDwarf();
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
@@ -217,8 +238,14 @@ private:
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
// parse it only once for each object file we link.
- std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
- llvm::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc;
+ std::unique_ptr<llvm::DWARFContext> Dwarf;
+ std::vector<const llvm::DWARFDebugLine::LineTable *> LineTables;
+ struct VarLoc {
+ const llvm::DWARFDebugLine::LineTable *LT;
+ unsigned File;
+ unsigned Line;
+ };
+ llvm::DenseMap<StringRef, VarLoc> VariableLoc;
llvm::once_flag InitDwarfLine;
};
@@ -242,13 +269,11 @@ public:
template <class ELFT> void parse();
MemoryBufferRef getBuffer();
InputFile *fetch();
+ bool AddedToLink = false;
private:
- std::vector<StringRef> getSymbolNames();
- template <class ELFT> std::vector<StringRef> getElfSymbols();
- std::vector<StringRef> getBitcodeSymbols();
+ template <class ELFT> void addElfSymbols();
- bool Seen = false;
uint64_t OffsetInArchive;
};
@@ -259,11 +284,11 @@ public:
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse();
- // Returns a memory buffer for a given symbol and the offset in the archive
- // for the member. An empty memory buffer and an offset of zero
- // is returned if we have already returned the same memory buffer.
- // (So that we don't instantiate same members more than once.)
- std::pair<MemoryBufferRef, uint64_t> getMember(const Archive::Symbol *Sym);
+ // 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
+ // more than once.)
+ InputFile *fetch(const Archive::Symbol &Sym);
private:
std::unique_ptr<Archive> File;
@@ -290,7 +315,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef typename ELFT::Verdef Elf_Verdef;
typedef typename ELFT::Versym Elf_Versym;
- std::vector<StringRef> Undefs;
const Elf_Shdr *VersymSec = nullptr;
const Elf_Shdr *VerdefSec = nullptr;
@@ -298,8 +322,6 @@ public:
std::vector<const Elf_Verdef *> Verdefs;
std::string SoName;
- llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
-
static bool classof(const InputFile *F) {
return F->kind() == Base::SharedKind;
}
@@ -308,7 +330,9 @@ public:
void parseSoName();
void parseRest();
- std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym);
+ uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym);
+ std::vector<const Elf_Verdef *> parseVerdefs();
+ std::vector<uint32_t> parseVersyms();
struct NeededVer {
// The string table offset of the version name in the output file.
@@ -337,8 +361,15 @@ InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
uint64_t OffsetInArchive = 0);
InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
+inline bool isBitcode(MemoryBufferRef MB) {
+ return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode;
+}
+
+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;
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 93baefadce6e..d6e9a19051e0 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -14,6 +14,7 @@
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Relocations.h"
+#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
@@ -26,7 +27,10 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/xxhash.h"
+#include <algorithm>
#include <mutex>
+#include <set>
+#include <vector>
using namespace llvm;
using namespace llvm::ELF;
@@ -45,32 +49,6 @@ std::string lld::toString(const InputSectionBase *Sec) {
return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
}
-DenseMap<SectionBase *, int> elf::buildSectionOrder() {
- DenseMap<SectionBase *, int> SectionOrder;
- if (Config->SymbolOrderingFile.empty())
- return SectionOrder;
-
- // 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, int> SymbolOrder;
- int Priority = -Config->SymbolOrderingFile.size();
- for (StringRef S : Config->SymbolOrderingFile)
- SymbolOrder.insert({S, Priority++});
-
- // Build a map from sections to their priorities.
- for (InputFile *File : ObjectFiles) {
- for (Symbol *Sym : File->getSymbols()) {
- auto *D = dyn_cast<Defined>(Sym);
- if (!D || !D->Section)
- continue;
- int &Priority = SectionOrder[D->Section];
- Priority = std::min(Priority, SymbolOrder.lookup(D->getName()));
- }
- }
- return SectionOrder;
-}
-
template <class ELFT>
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &File,
const typename ELFT::Shdr &Hdr) {
@@ -168,12 +146,8 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
return Offset == uint64_t(-1) ? OS->Size : Offset;
}
case Regular:
- return cast<InputSection>(this)->OutSecOff + Offset;
- case Synthetic: {
- auto *IS = cast<InputSection>(this);
- // For synthetic sections we treat offset -1 as the end of the section.
- return IS->OutSecOff + (Offset == uint64_t(-1) ? IS->getSize() : Offset);
- }
+ case Synthetic:
+ 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
@@ -182,16 +156,21 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
case Merge:
const MergeInputSection *MS = cast<MergeInputSection>(this);
if (InputSection *IS = MS->getParent())
- return IS->OutSecOff + MS->getOffset(Offset);
- return MS->getOffset(Offset);
+ return IS->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);
+}
+
OutputSection *SectionBase::getOutputSection() {
InputSection *Sec;
if (auto *IS = dyn_cast<InputSection>(this))
- return IS->getParent();
+ Sec = IS;
else if (auto *MS = dyn_cast<MergeInputSection>(this))
Sec = MS->getParent();
else if (auto *EH = dyn_cast<EhInputSection>(this))
@@ -201,34 +180,50 @@ OutputSection *SectionBase::getOutputSection() {
return Sec ? Sec->getParent() : nullptr;
}
-// Uncompress section contents if required. Note that this function
+// Decompress section contents if required. Note that this function
// is called from parallelForEach, so it must be thread-safe.
-void InputSectionBase::maybeUncompress() {
- if (UncompressBuf || !Decompressor::isCompressedELFSection(Flags, Name))
+void InputSectionBase::maybeDecompress() {
+ if (DecompressBuf)
+ return;
+ if (!(Flags & SHF_COMPRESSED) && !Name.startswith(".zdebug"))
return;
+ // Decompress a section.
Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data),
Config->IsLE, Config->Is64));
size_t Size = Dec.getDecompressedSize();
- UncompressBuf.reset(new char[Size]());
- if (Error E = Dec.decompress({UncompressBuf.get(), Size}))
+ DecompressBuf.reset(new char[Size + Name.size()]());
+ if (Error E = Dec.decompress({DecompressBuf.get(), Size}))
fatal(toString(this) +
": decompress failed: " + llvm::toString(std::move(E)));
- Data = makeArrayRef((uint8_t *)UncompressBuf.get(), Size);
+ Data = makeArrayRef((uint8_t *)DecompressBuf.get(), Size);
Flags &= ~(uint64_t)SHF_COMPRESSED;
+
+ // A section name may have been altered if compressed. If that's
+ // the case, restore the original name. (i.e. ".zdebug_" -> ".debug_")
+ if (Name.startswith(".zdebug")) {
+ DecompressBuf[Size] = '.';
+ memcpy(&DecompressBuf[Size + 1], Name.data() + 2, Name.size() - 2);
+ Name = StringRef(&DecompressBuf[Size], Name.size() - 1);
+ }
}
InputSection *InputSectionBase::getLinkOrderDep() const {
- if ((Flags & SHF_LINK_ORDER) && Link != 0) {
- InputSectionBase *L = File->getSections()[Link];
- if (auto *IS = dyn_cast<InputSection>(L))
- return IS;
- error("a section with SHF_LINK_ORDER should not refer a non-regular "
- "section: " +
- toString(L));
- }
+ 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;
return nullptr;
}
@@ -241,9 +236,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
.str();
// First check if we can get desired values from debugging information.
- std::string LineInfo = getFile<ELFT>()->getLineInfo(this, Offset);
- if (!LineInfo.empty())
- return LineInfo;
+ if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset))
+ return Info->FileName + ":" + std::to_string(Info->Line);
// File->SourceFile contains STT_FILE symbol that contains a
// source file name. If it's missing, we use an object file name.
@@ -251,12 +245,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
if (SrcFile.empty())
SrcFile = toString(File);
- // Find a function symbol that encloses a given location.
- for (Symbol *B : File->getSymbols())
- if (auto *D = dyn_cast<Defined>(B))
- if (D->Section == this && D->Type == STT_FUNC)
- if (D->Value <= Offset && Offset < D->Value + D->Size)
- return SrcFile + ":(function " + toString(*D) + ")";
+ if (Defined *D = getEnclosingFunction<ELFT>(Offset))
+ return SrcFile + ":(function " + toString(*D) + ")";
// If there's no symbol, print out the offset in the section.
return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
@@ -292,7 +282,7 @@ std::string InputSectionBase::getObjMsg(uint64_t Off) {
std::string Archive;
if (!File->ArchiveName.empty())
- Archive = (" in archive " + File->ArchiveName).str();
+ Archive = " in archive " + File->ArchiveName;
// Find a symbol that encloses a given location.
for (Symbol *B : File->getSymbols())
@@ -345,8 +335,9 @@ template <class ELFT> void InputSection::copyShtGroup(uint8_t *Buf) {
*To++ = Sections[Idx]->getOutputSection()->SectionIndex;
}
-InputSectionBase *InputSection::getRelocatedSection() {
- assert(Type == SHT_RELA || Type == SHT_REL);
+InputSectionBase *InputSection::getRelocatedSection() const {
+ if (!File || (Type != SHT_RELA && Type != SHT_REL))
+ return nullptr;
ArrayRef<InputSectionBase *> Sections = File->getSections();
return Sections[Info];
}
@@ -365,12 +356,12 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
Buf += sizeof(RelTy);
- if (Config->IsRela)
+ if (RelTy::IsRela)
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->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset);
+ P->r_offset = Sec->getVA(Rel.r_offset);
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
Config->IsMips64EL);
@@ -395,17 +386,32 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
continue;
}
- if (Config->IsRela) {
- P->r_addend =
- Sym.getVA(getAddend<ELFT>(Rel)) - Section->getOutputSection()->Addr;
- } else if (Config->Relocatable) {
- const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
- Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset,
- Target->getImplicitAddend(BufLoc, Type),
- &Sym});
+ int64_t Addend = getAddend<ELFT>(Rel);
+ const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
+ if (!RelTy::IsRela)
+ Addend = Target->getImplicitAddend(BufLoc, Type);
+
+ 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
+ // might redefine this default value and we must use it
+ // for a calculation of the relocation result. When we
+ // generate EXE or DSO it's trivial. Generating a relocatable
+ // output is more difficult case because the linker does
+ // not calculate relocations in this mode and loses
+ // 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;
}
- }
+ 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});
+ }
}
}
@@ -481,14 +487,17 @@ static uint64_t getARMStaticBase(const Symbol &Sym) {
return OS->PtLoad->FirstSec->Addr;
}
-static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
- const Symbol &Sym, RelExpr Expr) {
+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;
case R_ABS:
+ case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
return Sym.getVA(A);
+ case R_ADDEND:
+ return A;
case R_ARM_SBREL:
return Sym.getVA(A) - getARMStaticBase(Sym);
case R_GOT:
@@ -505,7 +514,9 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
case R_GOT_FROM_END:
case R_RELAX_TLS_GD_TO_IE_END:
return Sym.getGotOffset() + A - InX::Got->getSize();
+ case R_TLSLD_GOT_OFF:
case R_GOT_OFF:
+ case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
return Sym.getGotOffset() + A;
case R_GOT_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
@@ -516,11 +527,12 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
case R_HINT:
case R_NONE:
case R_TLSDESC_CALL:
+ case R_TLSLD_HINT:
llvm_unreachable("cannot relocate hint relocs");
case R_MIPS_GOTREL:
- return Sym.getVA(A) - InX::MipsGot->getGp();
+ return Sym.getVA(A) - InX::MipsGot->getGp(File);
case R_MIPS_GOT_GP:
- return InX::MipsGot->getGp() + A;
+ return InX::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
@@ -529,7 +541,7 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
// 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 = InX::MipsGot->getGp() + A - P;
+ uint64_t V = InX::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)
@@ -540,21 +552,23 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
// 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 InX::MipsGot->getVA() + InX::MipsGot->getPageEntryOffset(Sym, A) -
- InX::MipsGot->getGp();
+ return InX::MipsGot->getVA() +
+ InX::MipsGot->getPageEntryOffset(File, Sym, A) -
+ InX::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 InX::MipsGot->getVA() + InX::MipsGot->getSymEntryOffset(Sym, A) -
- InX::MipsGot->getGp();
+ return InX::MipsGot->getVA() +
+ InX::MipsGot->getSymEntryOffset(File, Sym, A) -
+ InX::MipsGot->getGp(File);
case R_MIPS_TLSGD:
- return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
- InX::MipsGot->getGlobalDynOffset(Sym) - InX::MipsGot->getGp();
+ return InX::MipsGot->getVA() + InX::MipsGot->getGlobalDynOffset(File, Sym) -
+ InX::MipsGot->getGp(File);
case R_MIPS_TLSLD:
- return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
- InX::MipsGot->getTlsIndexOff() - InX::MipsGot->getGp();
+ return InX::MipsGot->getVA() + InX::MipsGot->getTlsIndexOffset(File) -
+ InX::MipsGot->getGp(File);
case R_PAGE_PC:
case R_PLT_PAGE_PC: {
uint64_t Dest;
@@ -583,25 +597,27 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
case R_PLT:
return Sym.getPltVA() + A;
case R_PLT_PC:
- case R_PPC_PLT_OPD:
+ case R_PPC_CALL_PLT:
return Sym.getPltVA() + A - P;
- case R_PPC_OPD: {
+ case R_PPC_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)
return 0;
- if (Out::Opd) {
- // If this is a local call, and we currently have the address of a
- // function-descriptor, get the underlying code address instead.
- uint64_t OpdStart = Out::Opd->Addr;
- uint64_t OpdEnd = OpdStart + Out::Opd->Size;
- bool InOpd = OpdStart <= SymVA && SymVA < OpdEnd;
- if (InOpd)
- SymVA = read64be(&Out::OpdBuf[SymVA - OpdStart]);
- }
- return SymVA - P;
+
+ // PPC64 V2 ABI describes two entry points to a function. The global entry
+ // point sets up the TOC base pointer. When calling a local function, the
+ // call should branch to the local entry point rather than the global entry
+ // point. Section 3.4.1 describes using the 3 most significant bits of the
+ // st_other field to find out how many instructions there are between the
+ // local and global entry point.
+ uint8_t StOther = (Sym.StOther >> 5) & 7;
+ if (StOther == 0 || StOther == 1)
+ return SymVA - P;
+
+ return SymVA - P + (1LL << StOther);
}
case R_PPC_TOC:
return getPPC64TocBase() + A;
@@ -618,25 +634,44 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
// statically to zero.
if (Sym.isTls() && Sym.isUndefWeak())
return 0;
- if (Target->TcbSize)
+
+ // For TLS variant 1 the TCB is a fixed size, whereas for TLS variant 2 the
+ // TCB is on unspecified size and content. Targets that implement variant 1
+ // should set TcbSize.
+ if (Target->TcbSize) {
+ // PPC64 V2 ABI has the thread pointer offset into the middle of the TLS
+ // storage area by TlsTpOffset for efficient addressing TCB and up to
+ // 4KB – 8 B of other thread library information (placed before the TCB).
+ // Subtracting this offset will get the address of the first TLS block.
+ if (Target->TlsTpOffset)
+ return Sym.getVA(A) - Target->TlsTpOffset;
+
+ // If thread pointer is not offset into the middle, the first thing in the
+ // TLS storage area is the TCB. Add the TcbSize to get the address of the
+ // first TLS block.
return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
+ }
return Sym.getVA(A) - Out::TlsPhdr->p_memsz;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
return Out::TlsPhdr->p_memsz - Sym.getVA(A);
case R_SIZE:
- return A; // Sym.getSize was already folded into the addend.
+ return Sym.getSize() + A;
case R_TLSDESC:
return InX::Got->getGlobalDynAddr(Sym) + A;
case R_TLSDESC_PAGE:
return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) -
getAArch64Page(P);
- case R_TLSGD:
+ case R_TLSGD_GOT:
+ return InX::Got->getGlobalDynOffset(Sym) + A;
+ case R_TLSGD_GOT_FROM_END:
return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize();
case R_TLSGD_PC:
return InX::Got->getGlobalDynAddr(Sym) + A - P;
- case R_TLSLD:
+ case R_TLSLD_GOT_FROM_END:
return InX::Got->getTlsIndexOff() + A - InX::Got->getSize();
+ case R_TLSLD_GOT:
+ return InX::Got->getTlsIndexOff() + A;
case R_TLSLD_PC:
return InX::Got->getTlsIndexVA() + A - P;
}
@@ -656,6 +691,14 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
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)
+ continue;
+
uint64_t Offset = getOffset(Rel.r_offset);
uint8_t *BufLoc = Buf + Offset;
int64_t Addend = getAddend<ELFT>(Rel);
@@ -666,17 +709,27 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc);
if (Expr == R_NONE)
continue;
+
if (Expr != R_ABS) {
- // GCC 8.0 or earlier have a bug that it emits R_386_GOTPC relocations
- // against _GLOBAL_OFFSET_TABLE for .debug_info. The bug seems to have
- // 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)
- continue;
+ std::string Msg = getLocation<ELFT>(Offset) +
+ ": has non-ABS relocation " + toString(Type) +
+ " against symbol '" + toString(Sym) + "'";
+ if (Expr != R_PC) {
+ error(Msg);
+ return;
+ }
- error(getLocation<ELFT>(Offset) + ": has non-ABS relocation " +
- toString(Type) + " against symbol '" + toString(Sym) + "'");
- return;
+ // If the control reaches here, we found a PC-relative relocation in a
+ // non-ALLOC section. Since non-ALLOC section is not loaded into memory
+ // at runtime, the notion of PC-relative doesn't make sense here. So,
+ // this is a usage error. However, GNU linkers historically accept such
+ // 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)));
+ continue;
}
if (Sym.isTls() && !Out::TlsPhdr)
@@ -686,15 +739,37 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
}
}
+// This is used when '-r' is given.
+// For REL targets, InputSection::copyRelocations() may store artificial
+// 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;
+
+ 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);
+ }
+}
+
template <class ELFT>
void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) {
+ if (Flags & SHF_EXECINSTR)
+ adjustSplitStackFunctionPrologues<ELFT>(Buf, BufEnd);
+
if (Flags & SHF_ALLOC) {
relocateAlloc(Buf, BufEnd);
return;
}
auto *Sec = cast<InputSection>(this);
- if (Sec->AreRelocsRela)
+ 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>());
@@ -705,14 +780,17 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
const unsigned Bits = Config->Wordsize * 8;
for (const Relocation &Rel : Relocations) {
- uint64_t Offset = getOffset(Rel.Offset);
+ 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(Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), Bits);
+ getRelocTargetVA(File, Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr),
+ Bits);
switch (Expr) {
case R_RELAX_GOT_PC:
@@ -723,6 +801,7 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
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);
break;
case R_RELAX_TLS_GD_TO_LE:
@@ -731,15 +810,28 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
break;
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_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_END:
Target->relaxTlsGdToIe(BufLoc, Type, TargetVA);
break;
- case R_PPC_PLT_OPD:
+ case R_PPC_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
+ break;
+
// Patch a nop (0x60000000) to a ld.
- if (BufLoc + 8 <= BufEnd && read32be(BufLoc + 4) == 0x60000000)
- write32be(BufLoc + 4, 0xe8410028); // ld %r2, 40(%r1)
- LLVM_FALLTHROUGH;
+ 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)
+ }
+ Target->relocateOne(BufLoc, Type, TargetVA);
+ break;
default:
Target->relocateOne(BufLoc, Type, TargetVA);
break;
@@ -747,6 +839,103 @@ 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(
+ llvm::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");
+
+ // Sort both collections to compare addresses efficiently.
+ llvm::sort(MorestackCalls.begin(), MorestackCalls.end(),
+ [](const Relocation *L, const Relocation *R) {
+ return L->Offset < R->Offset;
+ });
+ std::vector<Defined *> Functions(Prologues.begin(), Prologues.end());
+ llvm::sort(
+ Functions.begin(), Functions.end(),
+ [](const Defined *L, const Defined *R) { return L->Value < R->Value; });
+
+ 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;
+ // Adjust all calls inside the function.
+ while (It != MorestackCalls.end() && (*It)->Offset < F->Value + F->Size) {
+ (*It)->Sym = MoreStackNonSplit;
+ ++It;
+ }
+ }
+}
+
+static bool
+enclosingPrologueAdjusted(uint64_t Offset,
+ const llvm::DenseSet<Defined *> &Prologues) {
+ for (Defined *F : Prologues)
+ if (F->Value <= Offset && Offset < F->Value + F->Size)
+ return true;
+ return false;
+}
+
+// If a function compiled for split stack calls a function not
+// compiled for split stack, then the caller needs its prologue
+// 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)
+ return;
+ llvm::DenseSet<Defined *> AdjustedPrologues;
+ std::vector<Relocation *> MorestackCalls;
+
+ 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())
+ continue;
+
+ Defined *D = dyn_cast<Defined>(Rel.Sym);
+ // A reference to an undefined symbol was an error, and should not
+ // have gotten to this point.
+ if (!D)
+ continue;
+
+ // Ignore calls into the split-stack api.
+ if (D->getName().startswith("__morestack")) {
+ if (D->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 (D->Type != STT_FUNC)
+ continue;
+
+ if (enclosingPrologueAdjusted(Rel.Offset, AdjustedPrologues))
+ continue;
+
+ if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) {
+ if (Target->adjustPrologueForCrossSplitStack(Buf + F->Value, End)) {
+ AdjustedPrologues.insert(F);
+ continue;
+ }
+ }
+ if (!getFile<ELFT>()->SomeNoSplitStack)
+ error("function call at " + getErrorLocation(Buf + Rel.Offset) +
+ "crosses a split-stack boundary, but unable " +
+ "to adjust the enclosing function's prologue");
+ }
+ switchMorestackCallsToMorestackNonSplit(AdjustedPrologues, MorestackCalls);
+}
+
template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
if (Type == SHT_NOBITS)
return;
@@ -818,10 +1007,6 @@ 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() {
- // Early exit if already split.
- if (!Pieces.empty())
- return;
-
if (AreRelocsRela)
split<ELFT>(relas<ELFT>());
else
@@ -916,15 +1101,9 @@ void MergeInputSection::splitIntoPieces() {
else
splitNonStrings(Data, Entsize);
- if (Config->GcSections && (Flags & SHF_ALLOC))
- for (uint64_t Off : LiveOffsets)
- getSectionPiece(Off)->Live = true;
-}
-
-// Do binary search to get a section piece at a given input offset.
-SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
- auto *This = static_cast<const MergeInputSection *>(this);
- return const_cast<SectionPiece *>(This->getSectionPiece(Offset));
+ OffsetMap.reserve(Pieces.size());
+ for (size_t I = 0, E = Pieces.size(); I != E; ++I)
+ OffsetMap[Pieces[I].InputOff] = I;
}
template <class It, class T, class Compare>
@@ -940,32 +1119,34 @@ static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
return Comp(Value, *First) ? First : First + 1;
}
-const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
- if (Data.size() <= Offset)
- fatal(toString(this) + ": entry is past the end of the section");
+// Do binary search to get a section piece at a given input offset.
+static SectionPiece *findSectionPiece(MergeInputSection *Sec, uint64_t Offset) {
+ if (Sec->Data.size() <= Offset)
+ fatal(toString(Sec) + ": entry is past the end of the section");
// Find the element this offset points to.
auto I = fastUpperBound(
- Pieces.begin(), Pieces.end(), Offset,
+ Sec->Pieces.begin(), Sec->Pieces.end(), Offset,
[](const uint64_t &A, const SectionPiece &B) { return A < B.InputOff; });
--I;
return &*I;
}
+SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
+ // Find a piece starting at a given offset.
+ auto It = OffsetMap.find(Offset);
+ if (It != OffsetMap.end())
+ return &Pieces[It->second];
+
+ // 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.
+ return findSectionPiece(this, Offset);
+}
+
// 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::getOffset(uint64_t Offset) const {
- if (!Live)
- return 0;
-
- // Initialize OffsetMap lazily.
- llvm::call_once(InitOffsetMap, [&] {
- OffsetMap.reserve(Pieces.size());
- for (size_t I = 0; I < Pieces.size(); ++I)
- OffsetMap[Pieces[I].InputOff] = I;
- });
-
+uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const {
// Find a string starting at a given offset.
auto It = OffsetMap.find(Offset);
if (It != OffsetMap.end())
@@ -973,10 +1154,8 @@ uint64_t MergeInputSection::getOffset(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 = *getSectionPiece(Offset);
- if (!Piece.Live)
- return 0;
-
+ const SectionPiece &Piece =
+ *findSectionPiece(const_cast<MergeInputSection *>(this), Offset);
uint64_t Addend = Offset - Piece.InputOff;
return Piece.OutputOff + Addend;
}
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index 8c114ae71948..4db01e035e32 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -18,8 +18,6 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Support/Threading.h"
-#include <mutex>
namespace lld {
namespace elf {
@@ -63,6 +61,9 @@ public:
unsigned Bss : 1;
+ // Set for sections that should not be folded by ICF.
+ unsigned KeepUnique : 1;
+
// These corresponds to the fields in Elf_Shdr.
uint32_t Alignment;
uint64_t Flags;
@@ -80,13 +81,15 @@ public:
// section.
uint64_t getOffset(uint64_t Offset) const;
+ uint64_t getVA(uint64_t Offset = 0) const;
+
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), Alignment(Alignment), Flags(Flags), Entsize(Entsize),
- Type(Type), Link(Link), Info(Info) {}
+ Bss(false), KeepUnique(false), Alignment(Alignment), Flags(Flags),
+ Entsize(Entsize), Type(Type), Link(Link), Info(Info) {}
};
// This corresponds to a section of an input file.
@@ -103,7 +106,7 @@ public:
static bool classof(const SectionBase *S) { return S->kind() != Output; }
- // The file which contains this section. It's dynamic type is always
+ // 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;
@@ -161,10 +164,15 @@ public:
InputSection *getLinkOrderDep() const;
+ // Get the function symbol that encloses this offset from within the
+ // section.
+ template <class ELFT>
+ Defined *getEnclosingFunction(uint64_t Offset);
+
// Compilers emit zlib-compressed debug sections if the -gz option
// is given. This function checks if this section is compressed, and
// if so, decompress in memory.
- void maybeUncompress();
+ void maybeDecompress();
// Returns a source location string. Used to construct an error message.
template <class ELFT> std::string getLocation(uint64_t Offset);
@@ -182,6 +190,15 @@ public:
// This vector contains such "cooked" relocations.
std::vector<Relocation> Relocations;
+ // A function compiled with -fsplit-stack calling a function
+ // compiled without -fsplit-stack needs its prologue adjusted. Find
+ // such functions and adjust their prologues. This is very similar
+ // to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more
+ // information.
+ template <typename ELFT>
+ 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);
@@ -189,9 +206,9 @@ public:
}
private:
- // A pointer that owns uncompressed data if a section is compressed by zlib.
+ // A pointer that owns decompressed data if a section is compressed by zlib.
// Since the feature is not used often, this is usually a nullptr.
- std::unique_ptr<char[]> UncompressBuf;
+ std::unique_ptr<char[]> DecompressBuf;
};
// SectionPiece represents a piece of splittable section contents.
@@ -200,7 +217,7 @@ private:
// be found by looking at the next one).
struct SectionPiece {
SectionPiece(size_t Off, uint32_t Hash, bool Live)
- : InputOff(Off), Hash(Hash), OutputOff(-1),
+ : InputOff(Off), Hash(Hash), OutputOff(0),
Live(Live || !Config->GcSections) {}
uint32_t InputOff;
@@ -223,19 +240,14 @@ public:
static bool classof(const SectionBase *S) { return S->kind() == Merge; }
void splitIntoPieces();
- // Mark the piece at a given offset live. Used by GC.
- void markLiveAt(uint64_t Offset) {
- if (this->Flags & llvm::ELF::SHF_ALLOC)
- LiveOffsets.insert(Offset);
- }
-
- // Translate an offset in the input section to an offset
- // in the output section.
- uint64_t getOffset(uint64_t Offset) const;
+ // Translate an offset in the input section to an offset in the parent
+ // MergeSyntheticSection.
+ 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;
+ llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
// Returns I'th piece's data. This function is very hot when
// string merging is enabled, so we want to inline.
@@ -249,18 +261,15 @@ public:
// Returns the SectionPiece at a given input section offset.
SectionPiece *getSectionPiece(uint64_t Offset);
- const SectionPiece *getSectionPiece(uint64_t Offset) const;
+ 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);
-
- mutable llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
- mutable llvm::once_flag InitOffsetMap;
-
- llvm::DenseSet<uint64_t> LiveOffsets;
};
struct EhSectionPiece {
@@ -310,6 +319,8 @@ public:
// beginning of the output section.
template <class ELFT> void writeTo(uint8_t *Buf);
+ uint64_t getOffset(uint64_t Offset) const { return OutSecOff + Offset; }
+
OutputSection *getParent() const;
// This variable has two usages. Initially, it represents an index in the
@@ -320,7 +331,7 @@ public:
static bool classof(const SectionBase *S);
- InputSectionBase *getRelocatedSection();
+ InputSectionBase *getRelocatedSection() const;
template <class ELFT, class RelTy>
void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
@@ -342,10 +353,6 @@ private:
// The list of all input sections.
extern std::vector<InputSectionBase *> InputSections;
-
-// Builds section order for handling --symbol-ordering-file.
-llvm::DenseMap<SectionBase *, int> buildSectionOrder();
-
} // namespace elf
std::string toString(const elf::InputSectionBase *);
diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp
index bfd85288d186..ef58932e86cc 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -20,6 +20,8 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Caching.h"
#include "llvm/LTO/Config.h"
@@ -29,7 +31,6 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstddef>
#include <memory>
@@ -44,70 +45,92 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-// This is for use when debugging LTO.
-static void saveBuffer(StringRef Buffer, const Twine &Path) {
+// 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;
- raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
- if (EC)
- error("cannot create " + Path + ": " + EC.message());
- OS << Buffer;
-}
-
-static void diagnosticHandler(const DiagnosticInfo &DI) {
- SmallString<128> ErrStorage;
- raw_svector_ostream OS(ErrStorage);
- DiagnosticPrinterRawOStream DP(OS);
- DI.print(DP);
- warn(ErrStorage);
+ 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 void checkError(Error E) {
- handleAllErrors(std::move(E),
- [&](ErrorInfoBase &EIB) { error(EIB.message()); });
+static std::string getThinLTOOutputFile(StringRef ModulePath) {
+ return lto::getThinLTOOutputFile(ModulePath,
+ Config->ThinLTOPrefixReplace.first,
+ Config->ThinLTOPrefixReplace.second);
}
-static std::unique_ptr<lto::LTO> createLTO() {
- lto::Config Conf;
+static lto::Config createConfig() {
+ lto::Config C;
// LLD supports the new relocations.
- Conf.Options = InitTargetOptionsFromCodeGenFlags();
- Conf.Options.RelaxELFRelocations = true;
+ C.Options = InitTargetOptionsFromCodeGenFlags();
+ C.Options.RelaxELFRelocations = true;
// Always emit a section per function/datum with LTO.
- Conf.Options.FunctionSections = true;
- Conf.Options.DataSections = true;
+ C.Options.FunctionSections = true;
+ C.Options.DataSections = true;
if (Config->Relocatable)
- Conf.RelocModel = None;
+ C.RelocModel = None;
else if (Config->Pic)
- Conf.RelocModel = Reloc::PIC_;
+ C.RelocModel = Reloc::PIC_;
else
- Conf.RelocModel = Reloc::Static;
- Conf.CodeModel = GetCodeModelFromCMModel();
- Conf.DisableVerify = Config->DisableVerify;
- Conf.DiagHandler = diagnosticHandler;
- Conf.OptLevel = Config->LTOO;
+ C.RelocModel = Reloc::Static;
+
+ C.CodeModel = GetCodeModelFromCMModel();
+ C.DisableVerify = Config->DisableVerify;
+ C.DiagHandler = diagnosticHandler;
+ C.OptLevel = Config->LTOO;
+ C.CPU = GetCPUStr();
// Set up a custom pipeline if we've been asked to.
- Conf.OptPipeline = Config->LTONewPmPasses;
- Conf.AAPipeline = Config->LTOAAPipeline;
+ C.OptPipeline = Config->LTONewPmPasses;
+ C.AAPipeline = Config->LTOAAPipeline;
// Set up optimization remarks if we've been asked to.
- Conf.RemarksFilename = Config->OptRemarksFilename;
- Conf.RemarksWithHotness = Config->OptRemarksWithHotness;
+ 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->SaveTemps)
- checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
- /*UseInputModulePath*/ true));
+ checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
+ /*UseInputModulePath*/ true));
+ return C;
+}
+BitcodeCompiler::BitcodeCompiler() {
+ // Initialize LTOObj.
lto::ThinBackend Backend;
- if (Config->ThinLTOJobs != -1u)
+
+ if (Config->ThinLTOIndexOnly) {
+ StringRef Path = Config->ThinLTOIndexOnlyArg;
+ if (!Path.empty())
+ IndexFile = openFile(Path);
+
+ auto OnIndexWrite = [&](const std::string &Identifier) {
+ ObjectToIndexFileState[Identifier] = true;
+ };
+
+ Backend = lto::createWriteIndexesThinBackend(
+ Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second,
+ Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite);
+ } else if (Config->ThinLTOJobs != -1U) {
Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
- return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
- Config->LTOPartitions);
-}
+ }
-BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {
+ LTOObj = llvm::make_unique<lto::LTO>(createConfig(), Backend,
+ Config->LTOPartitions);
+
+ // Initialize UsedStartStop.
for (Symbol *Sym : Symtab->getSymbols()) {
StringRef Name = Sym->getName();
for (StringRef Prefix : {"__start_", "__stop_"})
@@ -125,20 +148,20 @@ static void undefine(Symbol *S) {
void BitcodeCompiler::add(BitcodeFile &F) {
lto::InputFile &Obj = *F.Obj;
- unsigned SymNum = 0;
- std::vector<Symbol *> Syms = F.getSymbols();
- std::vector<lto::SymbolResolution> Resols(Syms.size());
+ bool IsExec = !Config->Shared && !Config->Relocatable;
- DenseSet<StringRef> ScriptSymbols;
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
- ScriptSymbols.insert(Cmd->Name);
+ if (Config->ThinLTOIndexOnly)
+ ObjectToIndexFileState.insert({Obj.getName(), false});
+
+ 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 (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
- Symbol *Sym = Syms[SymNum];
- lto::SymbolResolution &R = Resols[SymNum];
- ++SymNum;
+ 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
@@ -156,25 +179,46 @@ void BitcodeCompiler::add(BitcodeFile &F) {
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()));
+
if (R.Prevailing)
undefine(Sym);
- // We tell LTO to not apply interprocedural optimization for following
- // symbols because otherwise LTO would inline them while their values are
- // still not final:
- // 1) Aliased (with --defsym) or wrapped (with --wrap) symbols.
- // 2) Symbols redefined in linker script.
- R.LinkerRedefined = !Sym->CanInline || ScriptSymbols.count(Sym->getName());
+ // 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;
}
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 (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() {
- std::vector<InputFile *> Ret;
unsigned MaxTasks = LTOObj->getMaxTasks();
- Buff.resize(MaxTasks);
+ Buf.resize(MaxTasks);
Files.resize(MaxTasks);
// The --thinlto-cache-dir option specifies the path to a directory in which
@@ -184,35 +228,67 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
if (!Config->ThinLTOCacheDir.empty())
Cache = check(
lto::localCache(Config->ThinLTOCacheDir,
- [&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
- StringRef Path) { Files[Task] = std::move(MB); }));
+ [&](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>(Buff[Task]));
+ llvm::make_unique<raw_svector_ostream>(Buf[Task]));
},
Cache));
+ // Emit empty index files for non-indexed files
+ if (Config->ThinLTOIndexOnly) {
+ for (auto &Identifier : ObjectToIndexFileState)
+ if (!Identifier.getValue()) {
+ std::string Path = getThinLTOOutputFile(Identifier.getKey());
+ 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->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();
+ return {};
+ }
+
if (!Config->ThinLTOCacheDir.empty())
pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);
+ std::vector<InputFile *> Ret;
for (unsigned I = 0; I != MaxTasks; ++I) {
- if (Buff[I].empty())
+ if (Buf[I].empty())
continue;
if (Config->SaveTemps) {
if (I == 0)
- saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
+ saveBuffer(Buf[I], Config->OutputFile + ".lto.o");
else
- saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
+ saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o");
}
- InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp"));
+ InputFile *Obj = createObjectFile(MemoryBufferRef(Buf[I], "lto.tmp"));
Ret.push_back(Obj);
}
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 223af507a97d..8803078eb1df 100644
--- a/ELF/LTO.h
+++ b/ELF/LTO.h
@@ -24,6 +24,7 @@
#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>
@@ -38,6 +39,7 @@ namespace elf {
class BitcodeFile;
class InputFile;
+class LazyObjFile;
class BitcodeCompiler {
public:
@@ -49,9 +51,11 @@ public:
private:
std::unique_ptr<llvm::lto::LTO> LTOObj;
- std::vector<SmallString<0>> Buff;
+ 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::StringMap<bool> ObjectToIndexFileState;
};
} // namespace elf
} // namespace lld
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index 8f50a977fd75..abdd899da487 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -15,13 +15,13 @@
#include "Config.h"
#include "InputSection.h"
#include "OutputSections.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -74,7 +74,7 @@ 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)
+ if (Alignment == 1 && (!Sec || !Sec->getOutputSection()))
return Val;
return getValue() - getSecAddr();
}
@@ -102,28 +102,67 @@ OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) {
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");
+}
+
+void LinkerScript::expandMemoryRegions(uint64_t Size) {
+ if (Ctx->MemRegion)
+ expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
+ Ctx->OutSec->Name);
+ if (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::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);
- Dot = Val;
// Update to location counter means update to section size.
if (InSec)
- Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
+ expandOutputSection(Val - Dot);
+ else
+ expandMemoryRegions(Val - Dot);
+
+ Dot = Val;
}
-// This function is called from processSectionCommands,
-// while we are fixing the output section layout.
-void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
+// 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 == ".")
- return;
+ return false;
- // If a symbol was in PROVIDE(), we need to define it only when
- // it is a referenced undefined symbol.
+ 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 (Cmd->Provide && (!B || B->isDefined()))
+ 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))
return;
// Define a symbol.
@@ -153,6 +192,76 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
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))
+ return;
+
+ // 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, /*Type*/ 0, 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;
+}
+
+// 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();
+ };
+
+ 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);
+ }
+
+ for (auto &Cmds : {InsertBeforeCommands, InsertAfterCommands})
+ for (const std::pair<StringRef, std::vector<BaseCommand *>> &P : Cmds)
+ if (!P.second.empty())
+ error("unable to INSERT AFTER/BEFORE " + P.first +
+ ": section not defined");
+
+ SectionCommands = std::move(V);
+}
+
+// Symbols defined in script should not be inlined by LTO. At the same time
+// 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);
+ continue;
+ }
+
+ // If the output section directive has constraints,
+ // 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)
+ continue;
+ 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.
@@ -249,23 +358,11 @@ 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.
-// 5. If no SORT commands are given and --sort-section is not specified,
-// apply sorting provided by --symbol-ordering-file if any exist.
-static void sortInputSections(
- MutableArrayRef<InputSection *> Vec, const SectionPattern &Pat,
- const DenseMap<SectionBase *, int> &Order) {
+static void sortInputSections(MutableArrayRef<InputSection *> Vec,
+ const SectionPattern &Pat) {
if (Pat.SortOuter == SortSectionPolicy::None)
return;
- if (Pat.SortOuter == SortSectionPolicy::Default &&
- Config->SortSection == SortSectionPolicy::Default) {
- // If -symbol-ordering-file was given, sort accordingly.
- // Usually, Order is empty.
- if (!Order.empty())
- sortByOrder(Vec, [&](InputSectionBase *S) { return Order.lookup(S); });
- return;
- }
-
if (Pat.SortInner == SortSectionPolicy::Default)
sortSections(Vec, Config->SortSection);
else
@@ -275,8 +372,7 @@ static void sortInputSections(
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSection *>
-LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
- const DenseMap<SectionBase *, int> &Order) {
+LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
std::vector<InputSection *> Ret;
// Collects all sections that satisfy constraints of Cmd.
@@ -290,8 +386,11 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
// For -emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
// which are common because they are in the default bfd script.
- if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
- continue;
+ // 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())
+ continue;
std::string Filename = getFilename(Sec->File);
if (!Cmd->FilePat.match(Filename) ||
@@ -307,7 +406,7 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
}
sortInputSections(MutableArrayRef<InputSection *>(Ret).slice(SizeBefore),
- Pat, Order);
+ Pat);
}
return Ret;
}
@@ -315,22 +414,31 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
void LinkerScript::discard(ArrayRef<InputSection *> V) {
for (InputSection *S : V) {
if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab ||
- S == InX::DynStrTab)
+ S == InX::DynStrTab || S == InX::RelaPlt || S == InX::RelaDyn ||
+ S == InX::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 == InX::GnuHashTab)
+ InX::GnuHashTab = nullptr;
+ if (S == InX::HashTab)
+ InX::HashTab = nullptr;
+
S->Assigned = false;
S->Live = false;
discard(S->DependentSections);
}
}
-std::vector<InputSection *> LinkerScript::createInputSectionList(
- OutputSection &OutCmd, const DenseMap<SectionBase *, int> &Order) {
+std::vector<InputSection *>
+LinkerScript::createInputSectionList(OutputSection &OutCmd) {
std::vector<InputSection *> Ret;
for (BaseCommand *Base : OutCmd.SectionCommands) {
if (auto *Cmd = dyn_cast<InputSectionDescription>(Base)) {
- Cmd->Sections = computeInputSections(Cmd, Order);
+ Cmd->Sections = computeInputSections(Cmd);
Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
}
}
@@ -359,7 +467,6 @@ void LinkerScript::processSectionCommands() {
Ctx->OutSec = Aether;
size_t I = 0;
- DenseMap<SectionBase *, int> Order = buildSectionOrder();
// Add input sections to output sections.
for (BaseCommand *Base : SectionCommands) {
// Handle symbol assignments outside of any output section.
@@ -369,12 +476,13 @@ void LinkerScript::processSectionCommands() {
}
if (auto *Sec = dyn_cast<OutputSection>(Base)) {
- std::vector<InputSection *> V = createInputSectionList(*Sec, Order);
+ 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();
continue;
}
@@ -414,6 +522,8 @@ void LinkerScript::processSectionCommands() {
Sec->SectionIndex = I++;
if (Sec->Noload)
Sec->Type = SHT_NOBITS;
+ if (Sec->NonAlloc)
+ Sec->Flags &= ~(uint64_t)SHF_ALLOC;
}
}
Ctx = nullptr;
@@ -484,7 +594,7 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
// ignored. We should not have two output .text sections just because one was
// in a group and another was not for example.
//
- // It also seems that that wording was a late addition and didn't get the
+ // It also seems that wording was a late addition and didn't get the
// necessary scrutiny.
//
// Merging sections with different flags is expected by some users. One
@@ -529,11 +639,11 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
void LinkerScript::addOrphanSections() {
unsigned End = SectionCommands.size();
StringMap<OutputSection *> Map;
-
std::vector<OutputSection *> V;
- for (InputSectionBase *S : InputSections) {
+
+ auto Add = [&](InputSectionBase *S) {
if (!S->Live || S->Parent)
- continue;
+ return;
StringRef Name = getOutputSectionName(S);
@@ -545,12 +655,24 @@ void LinkerScript::addOrphanSections() {
if (OutputSection *Sec =
findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
Sec->addSection(cast<InputSection>(S));
- continue;
+ return;
}
if (OutputSection *OS = addInputSec(Map, S, Name))
V.push_back(OS);
- assert(S->getOutputSection()->SectionIndex == INT_MAX);
+ 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);
}
// If no SECTIONS command was given, we should insert sections commands
@@ -585,36 +707,15 @@ void LinkerScript::output(InputSection *S) {
// 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) }
- Ctx->OutSec->Size = Pos - Ctx->OutSec->Addr;
-
- // If there is a memory region associated with this input section, then
- // place the section in that region and update the region index.
- if (Ctx->MemRegion) {
- uint64_t &CurOffset = Ctx->MemRegionOffset[Ctx->MemRegion];
- CurOffset += Pos - Before;
- uint64_t CurSize = CurOffset - Ctx->MemRegion->Origin;
- if (CurSize > Ctx->MemRegion->Length) {
- uint64_t OverflowAmt = CurSize - Ctx->MemRegion->Length;
- error("section '" + Ctx->OutSec->Name + "' will not fit in region '" +
- Ctx->MemRegion->Name + "': overflowed by " + Twine(OverflowAmt) +
- " bytes");
- }
- }
+ expandOutputSection(Pos - Before);
}
void LinkerScript::switchTo(OutputSection *Sec) {
- if (Ctx->OutSec == Sec)
- return;
-
Ctx->OutSec = Sec;
- Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
- // If neither AT nor AT> is specified for an allocatable section, the linker
- // will set the LMA such that the difference between VMA and LMA for the
- // section is the same as the preceding output section in the same region
- // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
- if (Ctx->LMAOffset)
- Ctx->OutSec->LMAOffset = Ctx->LMAOffset();
+ 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
@@ -624,9 +725,8 @@ 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()) {
- auto It = MemoryRegions.find(Sec->MemoryRegionName);
- if (It != MemoryRegions.end())
- return It->second;
+ if (MemoryRegion *M = MemoryRegions.lookup(Sec->MemoryRegionName))
+ return M;
error("memory region '" + Sec->MemoryRegionName + "' not declared");
return nullptr;
}
@@ -659,24 +759,27 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
setDot(Sec->AddrExpr, Sec->Location, false);
Ctx->MemRegion = Sec->MemRegion;
+ Ctx->LMARegion = Sec->LMARegion;
if (Ctx->MemRegion)
- Dot = Ctx->MemRegionOffset[Ctx->MemRegion];
-
- if (Sec->LMAExpr) {
- uint64_t D = Dot;
- Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; };
- }
+ Dot = Ctx->MemRegion->CurPos;
switchTo(Sec);
- // We do not support custom layout for compressed debug sectons.
- // At this point we already know their size and have compressed content.
- if (Ctx->OutSec->Flags & SHF_COMPRESSED)
- return;
+ 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
+ // section is the same as the preceding output section in the same region
+ // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
+ if (PhdrEntry *L = Ctx->OutSec->PtLoad)
+ L->LMAOffset = Ctx->LMAOffset;
- // The Size previously denoted how many InputSections had been added to this
- // section, and was used for sorting SHF_LINK_ORDER sections. Reset it to
- // compute the actual size value.
+ // We can call this method multiple times during the creation of
+ // thunks and want to start over calculation each time.
Sec->Size = 0;
// We visited SectionsCommands from processSectionCommands to
@@ -685,7 +788,9 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
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;
continue;
}
@@ -693,15 +798,7 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
Cmd->Offset = Dot - Ctx->OutSec->Addr;
Dot += Cmd->Size;
- if (Ctx->MemRegion)
- Ctx->MemRegionOffset[Ctx->MemRegion] += Cmd->Size;
- Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
- continue;
- }
-
- // Handle ASSERT().
- if (auto *Cmd = dyn_cast<AssertCommand>(Base)) {
- Cmd->Expression();
+ expandOutputSection(Cmd->Size);
continue;
}
@@ -726,24 +823,28 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
}
}
-void LinkerScript::removeEmptyCommands() {
- // It is common practice to use very generic linker scripts. So for any
- // given run some of the output sections in the script will be empty.
- // We could create corresponding empty output sections, but that would
- // 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) {
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- return !Sec->Live;
+static bool isDiscardable(OutputSection &Sec) {
+ // We do not remove empty sections that are explicitly
+ // assigned to any segment.
+ if (!Sec.Phdrs.empty())
return false;
- });
-}
-static bool isAllSectionDescription(const OutputSection &Cmd) {
- for (BaseCommand *Base : Cmd.SectionCommands)
+ // 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)
+ 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)
+ continue;
+
if (!isa<InputSectionDescription>(*Base))
return false;
+ }
return true;
}
@@ -759,7 +860,7 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// Given that we want to create the section, we have to worry what impact
// it will have on the link. For example, if we just create a section with
// 0 for flags, it would change which PT_LOADs are created.
- // We could remember that that particular section is dummy and ignore it in
+ // We could remember that particular section is dummy and ignore it in
// other parts of the linker, but unfortunately there are quite a few places
// that would need to change:
// * The program header creation.
@@ -770,34 +871,54 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// the previous sections. Only a few flags are needed to keep the impact low.
uint64_t Flags = SHF_ALLOC;
- for (BaseCommand *Cmd : SectionCommands) {
+ for (BaseCommand *&Cmd : SectionCommands) {
auto *Sec = dyn_cast<OutputSection>(Cmd);
if (!Sec)
continue;
- if (Sec->Live) {
- Flags = Sec->Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
- continue;
- }
-
- if (isAllSectionDescription(*Sec))
- continue;
- Sec->Live = true;
- Sec->Flags = Flags;
+ // Handle align (e.g. ".foo : ALIGN(16) { ... }").
+ 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;
+
+ // 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;
+ }
}
+
+ // It is common practice to use very generic linker scripts. So for any
+ // given run some of the output sections in the script will be empty.
+ // We could create corresponding empty output sections, but that would
+ // 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; });
}
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->Live)
- continue;
+ if (!Sec->LMARegionName.empty()) {
+ if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName))
+ Sec->LMARegion = M;
+ else
+ error("memory region '" + Sec->LMARegionName + "' not declared");
+ }
Sec->MemRegion = findMemoryRegion(Sec);
- // Handle align (e.g. ".foo : ALIGN(16) { ... }").
- if (Sec->AlignExpr)
- Sec->Alignment =
- std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue());
}
}
@@ -808,9 +929,9 @@ void LinkerScript::adjustSectionsAfterSorting() {
// PHDRS { seg PT_LOAD; }
// SECTIONS { .aaa : { *(.aaa) } }
std::vector<StringRef> DefPhdrs;
- auto FirstPtLoad =
- std::find_if(PhdrsCommands.begin(), PhdrsCommands.end(),
- [](const PhdrsCommand &Cmd) { return Cmd.Type == PT_LOAD; });
+ auto FirstPtLoad = llvm::find_if(PhdrsCommands, [](const PhdrsCommand &Cmd) {
+ return Cmd.Type == PT_LOAD;
+ });
if (FirstPtLoad != PhdrsCommands.end())
DefPhdrs.push_back(FirstPtLoad->Name);
@@ -839,6 +960,15 @@ static OutputSection *findFirstSection(PhdrEntry *Load) {
return nullptr;
}
+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)
+ return 0;
+ // Otherwise only allocate program headers if that would not add a page.
+ return alignDown(Min, Config->MaxPageSize);
+}
+
// Try to find an address for the file and program headers output sections,
// which were unconditionally added to the first PT_LOAD segment earlier.
//
@@ -862,17 +992,22 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
return;
PhdrEntry *FirstPTLoad = *It;
+ bool HasExplicitHeaders =
+ llvm::any_of(PhdrsCommands, [](const PhdrsCommand &Cmd) {
+ return Cmd.HasPhdrs || Cmd.HasFilehdr;
+ });
uint64_t HeaderSize = getHeaderSize();
- // When linker script with SECTIONS is being used, don't output headers
- // unless there's a space for them.
- uint64_t Base = HasSectionsCommand ? alignDown(Min, Config->MaxPageSize) : 0;
- if (HeaderSize <= Min - Base || Script->hasPhdrsCommands()) {
+ if (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)
+ error("could not allocate headers");
+
Out::ElfHeader->PtLoad = nullptr;
Out::ProgramHeaders->PtLoad = nullptr;
FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad);
@@ -883,8 +1018,8 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
LinkerScript::AddressState::AddressState() {
for (auto &MRI : Script->MemoryRegions) {
- const MemoryRegion *MR = MRI.second;
- MemRegionOffset[MR] = MR->Origin;
+ MemoryRegion *MR = MRI.second;
+ MR->CurPos = MR->Origin;
}
}
@@ -916,15 +1051,11 @@ void LinkerScript::assignAddresses() {
for (BaseCommand *Base : SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
+ Cmd->Addr = Dot;
assignSymbol(Cmd, false);
+ Cmd->Size = Dot - Cmd->Addr;
continue;
}
-
- if (auto *Cmd = dyn_cast<AssertCommand>(Base)) {
- Cmd->Expression();
- continue;
- }
-
assignOffsets(cast<OutputSection>(Base));
}
Ctx = nullptr;
@@ -988,9 +1119,9 @@ ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) {
if (Symbol *Sym = Symtab->find(Name)) {
if (auto *DS = dyn_cast<Defined>(Sym))
return {DS->Section, false, DS->Value, Loc};
- if (auto *SS = dyn_cast<SharedSymbol>(Sym))
- if (!ErrorOnMissingSection || SS->CopyRelSec)
- return {SS->CopyRelSec, false, 0, Loc};
+ if (isa<SharedSymbol>(Sym))
+ if (!ErrorOnMissingSection)
+ return {nullptr, false, 0, Loc};
}
error(Loc + ": symbol not found: " + Name);
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 11131dda8e26..3b790dd4669f 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -11,9 +11,9 @@
#define LLD_ELF_LINKER_SCRIPT_H
#include "Config.h"
-#include "Strings.h"
#include "Writer.h"
#include "lld/Common/LLVM.h"
+#include "lld/Common/Strings.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
@@ -75,7 +75,6 @@ enum SectionsCommandKind {
AssignmentKind, // . = expr or <sym> = expr
OutputSectionKind,
InputSectionKind,
- AssertKind, // ASSERT(expr)
ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
};
@@ -106,6 +105,16 @@ struct SymbolAssignment : BaseCommand {
// Holds file name and line number for error reporting.
std::string Location;
+
+ // A string representation of this command. We use this for -Map.
+ std::string CommandString;
+
+ // Address of this assignment command.
+ unsigned Addr;
+
+ // Size of this assignment command. This is usually 0, but if
+ // you move '.' this may be greater than 0.
+ unsigned Size;
};
// Linker scripts allow additional constraints to be put on ouput sections.
@@ -118,11 +127,17 @@ 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;
};
// This struct represents one section match pattern in SECTIONS() command.
@@ -161,24 +176,23 @@ struct InputSectionDescription : BaseCommand {
std::vector<std::pair<ThunkSection *, uint32_t>> ThunkSections;
};
-// Represents an ASSERT().
-struct AssertCommand : BaseCommand {
- AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {}
-
- static bool classof(const BaseCommand *C) { return C->Kind == AssertKind; }
-
- Expr Expression;
-};
-
// Represents BYTE(), SHORT(), LONG(), or QUAD().
struct ByteCommand : BaseCommand {
- ByteCommand(Expr E, unsigned Size)
- : BaseCommand(ByteKind), 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; }
+ // Keeps string representing the command. Used for -Map" is perhaps better.
+ std::string CommandString;
+
Expr Expression;
+
+ // This is just an offset of this assignment command in the output section.
unsigned Offset;
+
+ // Size of this data command.
unsigned Size;
};
@@ -200,8 +214,8 @@ class LinkerScript final {
uint64_t ThreadBssOffset = 0;
OutputSection *OutSec = nullptr;
MemoryRegion *MemRegion = nullptr;
- llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
- std::function<uint64_t()> LMAOffset;
+ MemoryRegion *LMARegion = nullptr;
+ uint64_t LMAOffset = 0;
};
llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
@@ -209,14 +223,13 @@ class LinkerScript final {
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 *,
- const llvm::DenseMap<SectionBase *, int> &Order);
+ computeInputSections(const InputSectionDescription *);
- std::vector<InputSection *>
- createInputSectionList(OutputSection &Cmd,
- const llvm::DenseMap<SectionBase *, int> &Order);
+ std::vector<InputSection *> createInputSectionList(OutputSection &Cmd);
std::vector<size_t> getPhdrIndices(OutputSection *Sec);
@@ -251,7 +264,6 @@ public:
ExprValue getSymbolValue(StringRef Name, const Twine &Loc);
void addOrphanSections();
- void removeEmptyCommands();
void adjustSectionsBeforeSorting();
void adjustSectionsAfterSorting();
@@ -262,6 +274,10 @@ public:
void assignAddresses();
void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
void processSectionCommands();
+ void declareSymbols();
+
+ // Used to handle INSERT AFTER statements.
+ void processInsertCommands();
// SECTIONS command list.
std::vector<BaseCommand *> SectionCommands;
@@ -281,6 +297,11 @@ public:
// A list of symbols referenced by the script.
std::vector<llvm::StringRef> ReferencedSymbols;
+
+ // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
+ // to be inserted into SECTIONS commands list.
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertAfterCommands;
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertBeforeCommands;
};
extern LinkerScript *Script;
diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp
index dcc829315e64..54fddfb7b299 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -23,11 +23,13 @@
#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
+#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -36,57 +38,46 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-typedef DenseMap<const SectionBase *, SmallVector<Symbol *, 4>> SymbolMapTy;
+typedef DenseMap<const SectionBase *, SmallVector<Defined *, 4>> SymbolMapTy;
+
+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) {
- int W = Config->Is64 ? 16 : 8;
- OS << format("%0*llx %0*llx %5lld ", W, Addr, W, 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);
}
-static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
-
// Returns a list of all symbols that we want to print out.
-static std::vector<Symbol *> getSymbols() {
- std::vector<Symbol *> V;
- for (InputFile *File : ObjectFiles) {
- for (Symbol *B : File->getSymbols()) {
- if (auto *SS = dyn_cast<SharedSymbol>(B))
- if (SS->CopyRelSec || SS->NeedsPltAddr)
- V.push_back(SS);
+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->File == File && !DR->isSection() && DR->Section &&
- DR->Section->Live)
+ if (!DR->isSection() && DR->Section && DR->Section->Live &&
+ (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<Symbol *> Syms) {
+static SymbolMapTy getSectionSyms(ArrayRef<Defined *> Syms) {
SymbolMapTy Ret;
- for (Symbol *S : Syms) {
- if (auto *DR = dyn_cast<Defined>(S)) {
- Ret[DR->Section].push_back(S);
- continue;
- }
-
- SharedSymbol *SS = cast<SharedSymbol>(S);
- if (SS->CopyRelSec)
- Ret[SS->CopyRelSec].push_back(S);
- else
- Ret[InX::Plt].push_back(S);
- }
+ 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<Symbol *> &V = It.second;
- std::sort(V.begin(), V.end(),
- [](Symbol *A, Symbol *B) { return A->getVA() < B->getVA(); });
+ SmallVectorImpl<Defined *> &V = It.second;
+ std::stable_sort(V.begin(), V.end(), [](Defined *A, Defined *B) {
+ return A->getVA() < B->getVA();
+ });
}
return Ret;
}
@@ -95,12 +86,15 @@ static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) {
// 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<Symbol *> Syms) {
+getSymbolStrings(ArrayRef<Defined *> Syms) {
std::vector<std::string> Str(Syms.size());
parallelForEachN(0, Syms.size(), [&](size_t I) {
raw_string_ostream OS(Str[I]);
- writeHeader(OS, Syms[I]->getVA(), Syms[I]->getSize(), 0);
- OS << indent(2) << toString(*Syms[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;
@@ -109,6 +103,44 @@ getSymbolStrings(ArrayRef<Symbol *> Syms) {
return Ret;
}
+// Print .eh_frame contents. Since the section consists of EhSectionPieces,
+// we need a specialized printer for that section.
+//
+// .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;
+
+ 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;
+ return;
+ }
+ }
+ Pieces.push_back(P);
+ };
+
+ // Gather section pieces.
+ for (const CieRecord *Rec : InX::EhFrame->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";
+ }
+}
+
void elf::writeMapFile() {
if (Config->MapFile.empty())
return;
@@ -122,32 +154,109 @@ void elf::writeMapFile() {
}
// Collect symbol info that we want to print out.
- std::vector<Symbol *> Syms = getSymbols();
+ 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 << left_justify("Address", W) << ' ' << left_justify("Size", W)
- << " Align Out In Symbol\n";
+ OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W)
+ << " Size Align Out In Symbol\n";
+
+ for (BaseCommand *Base : Script->SectionCommands) {
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
+ if (Cmd->Provide && !Cmd->Sym)
+ continue;
+ //FIXME: calculate and print LMA.
+ writeHeader(OS, Cmd->Addr, 0, Cmd->Size, 1);
+ OS << Cmd->CommandString << '\n';
+ continue;
+ }
- // Print out file contents.
- for (OutputSection *OSec : OutputSections) {
- writeHeader(OS, OSec->Addr, OSec->Size, OSec->Alignment);
+ auto *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) {
- auto *ISD = dyn_cast<InputSectionDescription>(Base);
- if (!ISD)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
+ for (InputSection *IS : ISD->Sections) {
+ if (IS == InX::EhFrame) {
+ printEhFrame(OS, OSec);
+ 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';
+ }
+ 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';
+ continue;
+ }
+
+ 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';
continue;
- for (InputSection *IS : ISD->Sections) {
- writeHeader(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
- IS->Alignment);
- OS << indent(1) << toString(IS) << '\n';
- for (Symbol *Sym : SectionSyms[IS])
- OS << SymStr[Sym] << '\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.
+//
+// For each global symbol, we print out a file that defines the symbol
+// followed by files that uses that symbol. Here is an example.
+//
+// strlen /lib/x86_64-linux-gnu/libc.so.6
+// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o
+// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)
+//
+// In this case, strlen is defined by libc.so.6 and used by other two
+// files.
+void elf::writeCrossReferenceTable() {
+ 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);
+ }
+ }
+
+ // Print out a header.
+ outs() << "Cross Reference Table\n\n";
+ print("Symbol", "File");
+
+ // Print out a table.
+ 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));
+ }
+}
diff --git a/ELF/MapFile.h b/ELF/MapFile.h
index 2d93e26d4cf8..0282425888b7 100644
--- a/ELF/MapFile.h
+++ b/ELF/MapFile.h
@@ -13,6 +13,7 @@
namespace lld {
namespace elf {
void writeMapFile();
+void writeCrossReferenceTable();
} // namespace elf
} // namespace lld
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index 88f558c7a3c6..a8371e212c3e 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -20,15 +20,15 @@
//
//===----------------------------------------------------------------------===//
+#include "MarkLive.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "OutputSections.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
-#include "Writer.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
#include <functional>
@@ -60,8 +60,9 @@ static typename ELFT::uint getAddend(InputSectionBase &Sec,
static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
template <class ELFT, class RelT>
-static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
- std::function<void(InputSectionBase *, uint64_t)> Fn) {
+static void
+resolveReloc(InputSectionBase &Sec, RelT &Rel,
+ llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
// If a symbol is referenced in a live section, it is used.
@@ -90,7 +91,7 @@ static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
template <class ELFT>
static void
forEachSuccessor(InputSection &Sec,
- std::function<void(InputSectionBase *, uint64_t)> Fn) {
+ 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);
@@ -120,7 +121,7 @@ forEachSuccessor(InputSection &Sec,
template <class ELFT, class RelTy>
static void
scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
- std::function<void(InputSectionBase *, uint64_t)> Fn) {
+ llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
const endianness E = ELFT::TargetEndianness;
for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
@@ -155,14 +156,10 @@ scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
template <class ELFT>
static void
scanEhFrameSection(EhInputSection &EH,
- std::function<void(InputSectionBase *, uint64_t)> Fn) {
+ llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
if (!EH.NumRelocations)
return;
- // Unfortunately we need to split .eh_frame early since some relocations in
- // .eh_frame keep other section alive and some don't.
- EH.split<ELFT>();
-
if (EH.AreRelocsRela)
scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn);
else
@@ -207,7 +204,7 @@ template <class ELFT> static void doGcSections() {
// (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->markLiveAt(Offset);
+ MS->getSectionPiece(Offset)->Live = true;
if (Sec->Live)
return;
@@ -279,13 +276,18 @@ template <class ELFT> void elf::markLive() {
// 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.
+ // unconditionally make non-SHF_ALLOC sections alive except
+ // SHF_LINK_ORDER and SHT_REL/SHT_RELA sections.
//
- // Non SHF_ALLOC sections are not removed even if they are
+ // Usually, 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
- // want to keep it.)
+ // want to keep it.).
+ //
+ // Note on SHF_LINK_ORDER: Such sections contain metadata and they
+ // have a reverse dependency on the InputSection they are linked with.
+ // We are able to garbage collect them.
//
// Note on SHF_REL{,A}: Such sections reach here only when -r
// or -emit-reloc were given. And they are subject of garbage
@@ -293,8 +295,9 @@ template <class ELFT> void elf::markLive() {
// 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 && !IsRel)
+ if (!IsAlloc && !IsLinkOrder && !IsRel)
Sec->Live = true;
}
@@ -305,8 +308,7 @@ template <class ELFT> void elf::markLive() {
if (Config->PrintGcSections)
for (InputSectionBase *Sec : InputSections)
if (!Sec->Live)
- message("removing unused section from '" + Sec->Name + "' in file '" +
- Sec->File->getName() + "'");
+ message("removing unused section " + toString(Sec));
}
template void elf::markLive<ELF32LE>();
diff --git a/ELF/MarkLive.h b/ELF/MarkLive.h
new file mode 100644
index 000000000000..c9b99add34de
--- /dev/null
+++ b/ELF/MarkLive.h
@@ -0,0 +1,21 @@
+//===- MarkLive.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_MARKLIVE_H
+#define LLD_ELF_MARKLIVE_H
+
+namespace lld {
+namespace elf {
+
+template <class ELFT> void markLive();
+
+} // namespace elf
+} // namespace lld
+
+#endif // LLD_ELF_MARKLIVE_H
diff --git a/ELF/Options.td b/ELF/Options.td
index 20027e90aefd..73457db8332f 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -4,66 +4,91 @@ include "llvm/Option/OptParser.td"
// two can precede the option name except those that start with 'o'.
class F<string name>: Flag<["--", "-"], name>;
class J<string name>: Joined<["--", "-"], name>;
-class S<string name>: Separate<["--", "-"], name>;
-multiclass Eq<string name> {
- def "": Separate<["--", "-"], name>;
- def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+multiclass Eq<string name, string help> {
+ def NAME: Separate<["--", "-"], name>;
+ def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
}
-def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">;
+multiclass B<string name, string help1, string help2> {
+ def NAME: Flag<["--", "-"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
+}
+
+defm auxiliary: Eq<"auxiliary", "Set DT_AUXILIARY field to the specified name">;
def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
def Bsymbolic_functions: F<"Bsymbolic-functions">,
HelpText<"Bind defined function symbols locally">;
-def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">;
+def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">;
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
-def build_id: F<"build-id">, HelpText<"Generate build ID note">;
+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>]">;
-def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">;
+defm check_sections: B<"check-sections",
+ "Check section addresses for overlaps (default)",
+ "Do not check section addresses for overlaps">;
-defm compress_debug_sections : Eq<"compress-debug-sections">,
- HelpText<"Compress DWARF debug sections">;
+defm compress_debug_sections:
+ Eq<"compress-debug-sections", "Compress DWARF debug sections">,
+ MetaVarName<"[none,zlib]">;
-defm defsym: Eq<"defsym">, HelpText<"Define a symbol alias">;
+defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
-defm library_path: Eq<"library-path">,
- HelpText<"Add a directory to the library search path">, MetaVarName<"<dir>">;
+defm library_path:
+ Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">;
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
-defm Tbss: Eq<"Tbss">,
- HelpText<"Same as --section-start with .bss as the sectionname">;
+defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">;
-defm Tdata: Eq<"Tdata">,
- HelpText<"Same as --section-start with .data as the sectionname">;
+defm Tdata: Eq<"Tdata", "Same as --section-start with .data as the sectionname">;
-defm Ttext: Eq<"Ttext">,
- HelpText<"Same as --section-start with .text as the sectionname">;
+defm Ttext: Eq<"Ttext", "Same as --section-start with .text as the sectionname">;
-def allow_multiple_definition: F<"allow-multiple-definition">,
- HelpText<"Allow multiple definitions">;
+defm allow_multiple_definition: B<"allow-multiple-definition",
+ "Allow multiple definitions",
+ "Do not allow multiple definitions (default)">;
-def as_needed: F<"as-needed">,
- HelpText<"Only set DT_NEEDED for shared libraries if used">;
+defm apply_dynamic_relocs: B<"apply-dynamic-relocs",
+ "Apply dynamic relocations to place",
+ "Do not apply dynamic relocations to place">;
+
+defm as_needed: B<"as-needed",
+ "Only set DT_NEEDED for shared libraries if used",
+ "Always set DT_NEEDED for shared libraries (default)">;
+
+defm call_graph_ordering_file:
+ Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
// -chroot doesn't have a help text because it is an internal option.
-def chroot: S<"chroot">;
+def chroot: Separate<["--", "-"], "chroot">;
def color_diagnostics: F<"color-diagnostics">,
- HelpText<"Use colors in diagnostics">;
+ HelpText<"Alias for --color-diagnostics=always">;
def color_diagnostics_eq: J<"color-diagnostics=">,
- HelpText<"Use colors in diagnostics">;
+ HelpText<"Use colors in diagnostics">,
+ MetaVarName<"[auto,always,never]">;
+
+defm cref: B<"cref",
+ "Output cross reference table",
+ "Do not output cross reference table">;
-def define_common: F<"define-common">,
- HelpText<"Assign space to common symbols">;
+defm define_common: B<"define-common",
+ "Assign space to common symbols",
+ "Do not assign space to common symbols">;
-def demangle: F<"demangle">, HelpText<"Demangle symbol names">;
+defm demangle: B<"demangle",
+ "Demangle symbol names (default)",
+ "Do not demangle symbol names">;
def disable_new_dtags: F<"disable-new-dtags">,
HelpText<"Disable new dynamic tags">;
@@ -76,158 +101,126 @@ def discard_locals: F<"discard-locals">,
def discard_none: F<"discard-none">,
HelpText<"Keep all symbols in the symbol table">;
-def dynamic_linker: S<"dynamic-linker">,
- HelpText<"Which dynamic linker to use">;
+defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
-defm dynamic_list: Eq<"dynamic-list">,
- HelpText<"Read a list of dynamic symbols">;
+defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">;
-def eh_frame_hdr: F<"eh-frame-hdr">,
- HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">;
+defm eh_frame_hdr: B<"eh-frame-hdr",
+ "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
+ "Do not create .eh_frame_hdr section">;
def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
def enable_new_dtags: F<"enable-new-dtags">,
- HelpText<"Enable new dynamic tags">;
+ HelpText<"Enable new dynamic tags (default)">;
+
+def end_group: F<"end-group">,
+ HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">;
def end_lib: F<"end-lib">,
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
-defm entry: Eq<"entry">, HelpText<"Name of entry point symbol">,
+defm entry: Eq<"entry", "Name of entry point symbol">,
MetaVarName<"<entry>">;
-defm error_limit: Eq<"error-limit">,
- HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
+defm error_limit:
+ Eq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
def error_unresolved_symbols: F<"error-unresolved-symbols">,
HelpText<"Report unresolved symbols as errors">;
-defm exclude_libs: Eq<"exclude-libs">,
- HelpText<"Exclude static libraries from automatic export">;
+defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
-def export_dynamic: F<"export-dynamic">,
- HelpText<"Put symbols in the dynamic symbol table">;
+defm export_dynamic: B<"export-dynamic",
+ "Put symbols in the dynamic symbol table",
+ "Do not put symbols in the dynamic symbol table (default)">;
-defm export_dynamic_symbol: Eq<"export-dynamic-symbol">,
- HelpText<"Put a symbol in the dynamic symbol table">;
+defm export_dynamic_symbol:
+ Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">;
-def fatal_warnings: F<"fatal-warnings">,
- HelpText<"Treat warnings as errors">;
+defm fatal_warnings: B<"fatal-warnings",
+ "Treat warnings as errors",
+ "Do not treat warnings as errors (default)">;
-defm filter: Eq<"filter">,
- HelpText<"Set DT_FILTER field to the specified name">;
+defm filter: Eq<"filter", "Set DT_FILTER field to the specified name">;
-defm fini: Eq<"fini">,
- HelpText<"Specify a finalizer function">, MetaVarName<"<symbol>">;
+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">;
-def full_shutdown : F<"full-shutdown">,
- HelpText<"Perform a full shutdown instead of calling _exit">;
+defm format: Eq<"format", "Change the input format of the inputs following this option">,
+ MetaVarName<"[default,elf,binary]">;
-defm format: Eq<"format">,
- HelpText<"Change the input format of the inputs following this option">,
- MetaVarName<"<input-format>">;
+defm gc_sections: B<"gc-sections",
+ "Enable garbage collection of unused sections",
+ "Disable garbage collection of unused sections (default)">;
-def gc_sections: F<"gc-sections">,
- HelpText<"Enable garbage collection of unused sections">;
+defm gdb_index: B<"gdb-index",
+ "Generate .gdb_index section",
+ "Do not generate .gdb_index section (default)">;
-def gdb_index: F<"gdb-index">,
- HelpText<"Generate .gdb_index section">;
+defm gnu_unique: B<"gnu-unique",
+ "Enable STB_GNU_UNIQUE symbol binding (default)",
+ "Disable STB_GNU_UNIQUE symbol binding">;
-defm hash_style: Eq<"hash-style">,
- HelpText<"Specify hash style (sysv, gnu or both)">;
+defm hash_style: Eq<"hash-style", "Specify hash style (sysv, gnu or both)">;
def help: F<"help">, HelpText<"Print option help">;
def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">;
-def icf_data: F<"icf-data">,
- HelpText<"Enable ICF to also fold identical read only data">;
+def icf_safe: F<"icf=safe">, HelpText<"Enable safe identical code folding">;
+
+def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">;
+
+def ignore_function_address_equality: F<"ignore-function-address-equality">,
+ HelpText<"lld can break the address equality of functions">;
-def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">;
+def ignore_data_address_equality: F<"ignore-data-address-equality">,
+ HelpText<"lld can break the address equality of data">;
-defm image_base : Eq<"image-base">, HelpText<"Set the base address">;
+defm image_base: Eq<"image-base", "Set the base address">;
-defm init: Eq<"init">, HelpText<"Specify an initializer function">,
+defm init: Eq<"init", "Specify an initializer function">,
MetaVarName<"<symbol>">;
-defm library: Eq<"library">, HelpText<"Root name of library to use">,
- MetaVarName<"<libName>">;
+defm just_symbols: Eq<"just-symbols", "Just link symbols">;
-def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
- HelpText<"Optimization level for LTO">;
+defm keep_unique: Eq<"keep-unique", "Do not fold this symbol during ICF">;
+
+defm library: Eq<"library", "Root name of library to use">,
+ MetaVarName<"<libName>">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
-defm Map: Eq<"Map">, HelpText<"Print a link map to the specified file">;
+defm Map: Eq<"Map", "Print a link map to the specified file">;
-def merge_exidx_entries: F<"merge-exidx-entries">,
- HelpText<"Enable merging .ARM.exidx entries">;
+defm merge_exidx_entries: B<"merge-exidx-entries",
+ "Enable merging .ARM.exidx entries (default)",
+ "Disable merging .ARM.exidx entries">;
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
-def no_as_needed: F<"no-as-needed">,
- HelpText<"Always DT_NEEDED for shared libraries">;
-
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
-def no_define_common: F<"no-define-common">,
- HelpText<"Do not assign space to common symbols">;
-
-def no_demangle: F<"no-demangle">,
- HelpText<"Do not demangle symbol names">;
-
def no_dynamic_linker: F<"no-dynamic-linker">,
HelpText<"Inhibit output of .interp section">;
-def no_eh_frame_hdr: F<"no-eh-frame-hdr">,
- HelpText<"Do not create .eh_frame_hdr section">;
-
-def no_export_dynamic: F<"no-export-dynamic">;
-def no_fatal_warnings: F<"no-fatal-warnings">;
-
-def no_gc_sections: F<"no-gc-sections">,
- HelpText<"Disable garbage collection of unused sections">;
-
-def no_gdb_index: F<"no-gdb-index">,
- HelpText<"Do not generate .gdb_index section">;
-
-def no_gnu_unique: F<"no-gnu-unique">,
- HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
-
-def no_merge_exidx_entries: F<"no-merge-exidx-entries">,
- HelpText<"Disable merging .ARM.exidx entries">;
-
-def no_threads: F<"no-threads">,
- HelpText<"Do not run the linker multi-threaded">;
-
-def no_whole_archive: F<"no-whole-archive">,
- HelpText<"Restores the default behavior of loading archive members">;
-
def noinhibit_exec: F<"noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
-def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">;
-
-def no_omagic: Flag<["--"], "no-omagic">, MetaVarName<"<magic>">,
+def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
HelpText<"Do not set the text data sections to be writable">;
-def no_print_gc_sections: F<"no-print-gc-sections">,
- HelpText<"Do not list removed unused sections">;
-
def no_rosegment: F<"no-rosegment">,
HelpText<"Do not put read-only non-executable sections in their own segment">;
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
-def no_undefined_version: F<"no-undefined-version">,
- HelpText<"Report version scripts that refer undefined symbols">;
-
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
@@ -237,42 +230,62 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
HelpText<"Set the text and data sections to be readable and writable">;
-defm orphan_handling: Eq<"orphan-handling">,
- HelpText<"Control how orphan sections are handled when linker script used">;
+defm orphan_handling:
+ Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">;
+
+defm pack_dyn_relocs:
+ Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
+ MetaVarName<"[none,android,relr,android+relr]">;
+
+defm use_android_relr_tags: B<"use-android-relr-tags",
+ "Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
+ "Use SHT_RELR / DT_RELR* tags (default)">;
+
+defm pie: B<"pie",
+ "Create a position independent executable",
+ "Do not create a position independent executable (default)">;
+
+defm print_gc_sections: B<"print-gc-sections",
+ "List removed unused sections",
+ "Do not list removed unused sections (default)">;
-def pack_dyn_relocs_eq: J<"pack-dyn-relocs=">, MetaVarName<"<format>">,
- HelpText<"Pack dynamic relocations in the given format (none or android)">;
+defm print_icf_sections: B<"print-icf-sections",
+ "List identical folded sections",
+ "Do not list identical folded sections (default)">;
-def pie: F<"pie">, HelpText<"Create a position independent executable">;
+def pop_state: F<"pop-state">,
+ HelpText<"Undo the effect of -push-state">;
-def print_gc_sections: F<"print-gc-sections">,
- HelpText<"List removed unused sections">;
+def push_state: F<"push-state">,
+ HelpText<"Save the current state of -as-needed, -static and -whole-archive">;
def print_map: F<"print-map">,
HelpText<"Print a link map to the standard output">;
-defm reproduce: Eq<"reproduce">,
- HelpText<"Dump linker invocation and input files for debugging">;
+defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
-defm rpath: Eq<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
+defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
-defm retain_symbols_file: Eq<"retain-symbols-file">,
- HelpText<"Retain only the symbols listed in the file">,
+defm retain_symbols_file:
+ Eq<"retain-symbols-file", "Retain only the symbols listed in the file">,
MetaVarName<"<file>">;
-defm script: Eq<"script">, HelpText<"Read linker script">;
+defm script: Eq<"script", "Read linker script">;
-def section_start: S<"section-start">, MetaVarName<"<address>">,
- HelpText<"Set address of section">;
+defm section_start: Eq<"section-start", "Set address of section">,
+ MetaVarName<"<address>">;
def shared: F<"shared">, HelpText<"Build a shared object">;
-defm soname: Eq<"soname">, HelpText<"Set DT_SONAME">;
+defm soname: Eq<"soname", "Set DT_SONAME">;
-defm sort_section: Eq<"sort-section">,
- HelpText<"Specifies sections sorting rule when linkerscript is used">;
+defm sort_section:
+ Eq<"sort-section", "Specifies sections sorting rule when linkerscript is used">;
+
+def start_group: F<"start-group">,
+ HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">;
def start_lib: F<"start-lib">,
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
@@ -281,33 +294,39 @@ def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
-def symbol_ordering_file: S<"symbol-ordering-file">,
- HelpText<"Layout sections in the order specified by symbol file">;
+defm symbol_ordering_file:
+ Eq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">;
-defm sysroot: Eq<"sysroot">, HelpText<"Set the system root">;
+defm sysroot: Eq<"sysroot", "Set the system root">;
def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
-def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">;
+def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32 (default)">;
-defm target2: Eq<"target2">,
- HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
+defm target2:
+ Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
MetaVarName<"<type>">;
-def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
+defm threads: B<"threads",
+ "Run the linker multi-threaded (default)",
+ "Do not run the linker multi-threaded">;
def trace: F<"trace">, HelpText<"Print the names of the input files">;
-defm trace_symbol : Eq<"trace-symbol">, HelpText<"Trace references to symbols">;
+defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
+
+defm undefined: Eq<"undefined", "Force undefined symbol during linking">,
+ MetaVarName<"<symbol>">;
-defm undefined: Eq<"undefined">,
- HelpText<"Force undefined symbol during linking">;
+defm unresolved_symbols:
+ Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
-defm unresolved_symbols: Eq<"unresolved-symbols">,
- HelpText<"Determine how to handle unresolved symbols">;
+defm undefined_version: B<"undefined-version",
+ "Allow unused version in version script (default)",
+ "Report version scripts that refer undefined symbols">;
-defm rsp_quoting: Eq<"rsp-quoting">,
- HelpText<"Quoting style for response files. Values supported: windows|posix">;
+defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">,
+ MetaVarName<"[posix,windows]">;
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
@@ -315,91 +334,123 @@ def verbose: F<"verbose">, HelpText<"Verbose mode">;
def version: F<"version">, HelpText<"Display the version number and exit">;
-defm version_script: Eq<"version-script">, HelpText<"Read a version script">;
+defm version_script: Eq<"version-script", "Read a version script">;
+
+defm warn_backrefs: B<"warn-backrefs",
+ "Warn about backward symbol references to fetch archive members",
+ "Do not warn about backward symbol references to fetch archive members (default)">;
+
+defm warn_common: B<"warn-common",
+ "Warn about duplicate common symbols",
+ "Do not warn about duplicate common symbols (default)">;
-def warn_common: F<"warn-common">,
- HelpText<"Warn about duplicate common symbols">;
+defm warn_symbol_ordering: B<"warn-symbol-ordering",
+ "Warn about problems with the symbol ordering file (default)",
+ "Do not warn about problems with the symbol ordering file">;
def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
HelpText<"Report unresolved symbols as warnings">;
-def whole_archive: F<"whole-archive">,
- HelpText<"Force load of all members in a static library">;
+defm whole_archive: B<"whole-archive",
+ "Force load of all members in a static library",
+ "Do not force load of all members in a static library (default)">;
-defm wrap: Eq<"wrap">, HelpText<"Use wrapper functions for symbol">,
- MetaVarName<"<symbol>">;
+defm wrap: Eq<"wrap", "Use wrapper functions for symbol">,
+ MetaVarName<"<symbol>=<symbol>">;
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
// Aliases
-def alias_auxiliary: Separate<["-"], "f">, Alias<auxiliary>;
-def alias_Bdynamic_call_shared: F<"call_shared">, Alias<Bdynamic>;
-def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
-def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
-def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>;
-def alias_Bstatic_static: F<"static">, Alias<Bstatic>;
-def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
-def alias_define_common_dc: F<"dc">, Alias<define_common>;
-def alias_define_common_dp: F<"dp">, Alias<define_common>;
-def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
-def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
-def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>;
-def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
-def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
-def alias_filter: Separate<["-"], "F">, Alias<filter>;
-def alias_format_b: S<"b">, Alias<format>;
-def alias_library: JoinedOrSeparate<["-"], "l">, Alias<library>;
-def alias_library_path: JoinedOrSeparate<["-"], "L">, Alias<library_path>;
-def alias_omagic: Flag<["-"], "N">, Alias<omagic>;
-def alias_o_output: Joined<["--"], "output=">, Alias<o>;
-def alias_o_output2 : Separate<["--"], "output">, Alias<o>;
-def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
-def alias_print_map_M: Flag<["-"], "M">, Alias<print_map>;
-def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
-def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>;
-def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>;
-def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>;
-def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>;
-def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
-def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
-def alias_trace: Flag<["-"], "t">, Alias<trace>;
-def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
-def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>;
-def alias_Ttext_segment_eq: J<"Ttext-segment=">, Alias<Ttext>;
-def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
-def alias_version_V: Flag<["-"], "V">, Alias<version>;
-
-// Our symbol resolution algorithm handles symbols in archive files differently
-// than traditional linkers, so we don't need --start-group and --end-group.
-// These options are recongized for compatibility but ignored.
-def end_group: F<"end-group">;
-def end_group_paren: Flag<["-"], ")">;
-def start_group: F<"start-group">;
-def start_group_paren: Flag<["-"], "(">;
+def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
+def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
+def: F<"dy">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
+def: F<"dn">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
+def: F<"non_shared">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
+def: F<"static">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
+def: Flag<["-"], "d">, Alias<define_common>, HelpText<"Alias for --define-common">;
+def: F<"dc">, Alias<define_common>, HelpText<"Alias for --define-common">;
+def: F<"dp">, Alias<define_common>, HelpText<"Alias for --define-common">;
+def: Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">;
+def: Flag<["-"], "X">, Alias<discard_locals>, HelpText<"Alias for --discard-locals">;
+def: Flag<["-"], "q">, Alias<emit_relocs>, HelpText<"Alias for --emit-relocs">;
+def: Flag<["-"], ")">, Alias<end_group>, HelpText<"Alias for --end-group">;
+def: JoinedOrSeparate<["-"], "e">, Alias<entry>, HelpText<"Alias for --entry">;
+def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
+def: Separate<["-"], "F">, Alias<filter>, HelpText<"Alias for --filter">;
+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<omagic>, HelpText<"Alias for --omagic">;
+def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
+def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
+def: F<"pic-executable">, Alias<pie>, HelpText<"Alias for --pie">;
+def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
+def: Flag<["-"], "r">, Alias<relocatable>, HelpText<"Alias for --relocatable">;
+def: JoinedOrSeparate<["-"], "R">, Alias<rpath>, HelpText<"Alias for --rpath">;
+def: JoinedOrSeparate<["-"], "T">, Alias<script>, HelpText<"Alias for --script">;
+def: F<"Bshareable">, Alias<shared>, HelpText<"Alias for --shared">;
+def: JoinedOrSeparate<["-"], "h">, Alias<soname>, HelpText<"Alias for --soname">;
+def: Flag<["-"], "(">, Alias<start_group>, HelpText<"Alias for --start-group">;
+def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;
+def: Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">;
+def: Flag<["-"], "t">, Alias<trace>, HelpText<"Alias for --trace">;
+def: JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>, HelpText<"Alias for --trace-symbol">;
+def: Separate<["-", "--"], "Ttext-segment">, Alias<Ttext>, HelpText<"Alias for --Ttext">;
+def: Joined<["-", "--"], "Ttext-segment=">, Alias<Ttext>, HelpText<"Alias for --Ttext">;
+def: JoinedOrSeparate<["-"], "u">, Alias<undefined>, HelpText<"Alias for --undefined">;
+def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">;
// LTO-related options.
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
+def lto_debug_pass_manager: F<"lto-debug-pass-manager">,
+ HelpText<"Debug new pass manager">;
+def lto_new_pass_manager: F<"lto-new-pass-manager">,
+ HelpText<"Use new pass manager">;
def lto_newpm_passes: J<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
+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_sample_profile: J<"lto-sample-profile=">,
+ HelpText<"Sample profile file path">;
def disable_verify: F<"disable-verify">;
-def mllvm: S<"mllvm">;
+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_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
- HelpText<"Include hotness informations in the optimization remarks file">;
-defm plugin_opt: Eq<"plugin-opt">,
- HelpText<"specifies LTO options for compatibility with GNU linkers">;
+ HelpText<"Include hotness information in the optimization remarks file">;
+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=">,
HelpText<"Path to ThinLTO cached object file directory">;
-def thinlto_cache_policy: S<"thinlto-cache-policy">,
- HelpText<"Pruning policy for the ThinLTO cache">;
+defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
+def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
+def: F<"plugin-opt=debug-pass-manager">,
+ Alias<lto_debug_pass_manager>, HelpText<"Alias for -lto-debug-pass-manager">;
+def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">;
+def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">,
+ HelpText<"Directory to store .dwo files when LTO and debug fission are used">;
+def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">;
+def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">;
+def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
+def: F<"plugin-opt=new-pass-manager">,
+ Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
+def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
+def: J<"plugin-opt=sample-profile=">,
+ Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
+def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
+def plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">;
+def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">;
+def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">;
+def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">;
+def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">;
+
// Ignore LTO plugin-related options.
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
// for ld.gold and ld.bfd to get LTO working. But it's not for lld which doesn't
@@ -407,31 +458,38 @@ def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
// just ignore the option on lld side as it's easier. In fact, the linker could
// be called 'ld' and understanding which linker is used would require parsing of
// --version output.
-def plugin: S<"plugin">;
-def plugin_eq: J<"plugin=">;
+defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">;
+
+def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">;
+def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">;
+def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
+def plugin_opt_slash: J<"plugin-opt=/">;
// Options listed below are silently ignored for now for compatibility.
-def allow_shlib_undefined: F<"allow-shlib-undefined">;
-def cref: F<"cref">;
-def detect_odr_violations: F<"detect-odr-violations">;
-def g: Flag<["-"], "g">;
-def long_plt: F<"long-plt">;
-def no_add_needed: F<"no-add-needed">;
-def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
-def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">;
-def no_ctors_in_init_array: F<"no-ctors-in-init-array">;
-def no_keep_memory: F<"no-keep-memory">;
-def no_mmap_output_file: F<"no-mmap-output-file">;
-def no_warn_common: F<"no-warn-common">;
-def no_warn_mismatch: F<"no-warn-mismatch">;
-def rpath_link: S<"rpath-link">;
-def rpath_link_eq: J<"rpath-link=">;
-def sort_common: F<"sort-common">;
-def stats: F<"stats">;
-def warn_execstack: F<"warn-execstack">;
-def warn_once: F<"warn-once">;
-def warn_shared_textrel: F<"warn-shared-textrel">;
-def EB : F<"EB">;
-def EL : F<"EL">;
-def G: JoinedOrSeparate<["-"], "G">;
-def Qy : F<"Qy">;
+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-warn-mismatch">;
+def: Separate<["--", "-"], "rpath-link">;
+def: J<"rpath-link=">;
+def: F<"sort-common">;
+def: F<"stats">;
+def: F<"warn-execstack">;
+def: F<"warn-once">;
+def: F<"warn-shared-textrel">;
+def: F<"EB">;
+def: F<"EL">;
+def: JoinedOrSeparate<["-"], "G">;
+def: F<"Qy">;
+
+// Hidden option used for testing MIPS multi-GOT implementation.
+defm mips_got_size:
+ Eq<"mips-got-size", "Max size of a single MIPS GOT. 0x10000 by default.">,
+ Flags<[HelpHidden]>;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index f0677f7e1ca5..8253b18b486c 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -10,11 +10,11 @@
#include "OutputSections.h"
#include "Config.h"
#include "LinkerScript.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/Compression.h"
@@ -25,15 +25,12 @@
using namespace llvm;
using namespace llvm::dwarf;
using namespace llvm::object;
-using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
uint8_t Out::First;
-OutputSection *Out::Opd;
-uint8_t *Out::OpdBuf;
PhdrEntry *Out::TlsPhdr;
OutputSection *Out::DebugInfo;
OutputSection *Out::ElfHeader;
@@ -45,7 +42,9 @@ OutputSection *Out::FiniArray;
std::vector<OutputSection *> elf::OutputSections;
uint32_t OutputSection::getPhdrFlags() const {
- uint32_t Ret = PF_R;
+ 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)
@@ -70,9 +69,7 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
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),
- SectionIndex(INT_MAX) {
+ /*Info*/ 0, /*Link*/ 0) {
Live = false;
}
@@ -91,13 +88,15 @@ static bool canMergeToProgbits(unsigned Type) {
void OutputSection::addSection(InputSection *IS) {
if (!Live) {
// If IS is the first section to be added to this section,
- // initialize Type and Entsize from IS.
+ // initialize Type, Entsize and flags from IS.
Live = true;
Type = IS->Type;
Entsize = IS->Entsize;
+ Flags = IS->Flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
- if ((Flags & (SHF_ALLOC | SHF_TLS)) != (IS->Flags & (SHF_ALLOC | SHF_TLS)))
+ unsigned Mask = SHF_ALLOC | 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));
@@ -114,9 +113,14 @@ void OutputSection::addSection(InputSection *IS) {
}
IS->Parent = this;
- Flags |= IS->Flags;
+ 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;
+
Alignment = std::max(Alignment, IS->Alignment);
- IS->OutSecOff = Size++;
// If this section contains a table of fixed-size entries, sh_entsize
// holds the element size. If it contains elements of different size we
@@ -134,8 +138,8 @@ void OutputSection::addSection(InputSection *IS) {
}
}
-void elf::sortByOrder(MutableArrayRef<InputSection *> In,
- std::function<int(InputSectionBase *S)> Order) {
+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; };
@@ -158,11 +162,11 @@ bool OutputSection::classof(const BaseCommand *C) {
return C->Kind == OutputSectionKind;
}
-void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) {
+void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) {
assert(Live);
- assert(SectionCommands.size() == 1);
- sortByOrder(cast<InputSectionDescription>(SectionCommands[0])->Sections,
- Order);
+ for (BaseCommand *B : SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(B))
+ sortByOrder(ISD->Sections, Order);
}
// Fill [Buf, Buf + Size) with Filler.
@@ -183,15 +187,6 @@ template <class ELFT> void OutputSection::maybeCompress() {
!Name.startswith(".debug_"))
return;
- // Calculate the section offsets and size pre-compression.
- Size = 0;
- for (BaseCommand *Cmd : SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
- for (InputSection *IS : ISD->Sections) {
- IS->OutSecOff = alignTo(Size, IS->Alignment);
- this->Size = IS->OutSecOff + IS->getSize();
- }
-
// Create a section header.
ZDebugHeader.resize(sizeof(Elf_Chdr));
auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
@@ -214,11 +209,11 @@ static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
if (Size == 1)
*Buf = Data;
else if (Size == 2)
- write16(Buf, Data, Config->Endianness);
+ write16(Buf, Data);
else if (Size == 4)
- write32(Buf, Data, Config->Endianness);
+ write32(Buf, Data);
else if (Size == 8)
- write64(Buf, Data, Config->Endianness);
+ write64(Buf, Data);
else
llvm_unreachable("unsupported Size argument");
}
@@ -240,12 +235,7 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
}
// Write leading padding.
- std::vector<InputSection *> Sections;
- for (BaseCommand *Cmd : SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
- for (InputSection *IS : ISD->Sections)
- if (IS->Live)
- Sections.push_back(IS);
+ std::vector<InputSection *> Sections = getInputSections(this);
uint32_t Filler = getFiller();
if (Filler)
fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
@@ -290,17 +280,13 @@ static void finalizeShtGroup(OutputSection *OS,
}
template <class ELFT> void OutputSection::finalize() {
- InputSection *First = nullptr;
- for (BaseCommand *Base : SectionCommands) {
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
- if (ISD->Sections.empty())
- continue;
- if (First == nullptr)
- First = ISD->Sections.front();
- }
- if (isa<ByteCommand>(Base) && Type == SHT_NOBITS)
- Type = SHT_PROGBITS;
- }
+ 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];
if (Flags & SHF_LINK_ORDER) {
// We must preserve the link order dependency of sections with the
@@ -376,8 +362,6 @@ static bool compCtors(const InputSection *A, const InputSection *B) {
assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
X = X.substr(6);
Y = Y.substr(6);
- if (X.empty() && Y.empty())
- return false;
return X < Y;
}
@@ -403,6 +387,14 @@ int elf::getPriority(StringRef S) {
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;
+}
+
// Sorts input sections by section name suffixes, so that .foo.N comes
// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
// We want to keep the original order if the priorities are the same
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index b2845773e9af..efb6aabe9743 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -14,7 +14,6 @@
#include "InputSection.h"
#include "LinkerScript.h"
#include "Relocations.h"
-
#include "lld/Common/LLVM.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELF.h"
@@ -49,10 +48,10 @@ public:
static bool classof(const BaseCommand *C);
- uint64_t getLMA() const { return Addr + LMAOffset; }
+ uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; }
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
- unsigned SectionIndex;
+ uint32_t SectionIndex = UINT32_MAX;
unsigned SortRank;
uint32_t getPhdrFlags() const;
@@ -78,7 +77,6 @@ public:
// The following fields correspond to Elf_Shdr members.
uint64_t Offset = 0;
- uint64_t LMAOffset = 0;
uint64_t Addr = 0;
uint32_t ShName = 0;
@@ -89,6 +87,7 @@ public:
// The following members are normally only used in linker scripts.
MemoryRegion *MemRegion = nullptr;
+ MemoryRegion *LMARegion = nullptr;
Expr AddrExpr;
Expr AlignExpr;
Expr LMAExpr;
@@ -99,13 +98,17 @@ public:
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);
template <class ELFT> void maybeCompress();
- void sort(std::function<int(InputSectionBase *S)> Order);
+ void sort(llvm::function_ref<int(InputSectionBase *S)> Order);
void sortInitFini();
void sortCtorsDtors();
@@ -119,13 +122,13 @@ private:
int getPriority(StringRef S);
+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 OutputSection *Opd;
- static uint8_t *OpdBuf;
static PhdrEntry *TlsPhdr;
static OutputSection *DebugInfo;
static OutputSection *ElfHeader;
@@ -142,8 +145,6 @@ namespace lld {
namespace elf {
uint64_t getHeaderSize();
-void sortByOrder(llvm::MutableArrayRef<InputSection *> In,
- std::function<int(InputSectionBase *S)> Order);
extern std::vector<OutputSection *> OutputSections;
} // namespace elf
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index 1aa0957b1d01..467219ad0542 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -45,14 +45,14 @@
#include "Config.h"
#include "LinkerScript.h"
#include "OutputSections.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
#include "lld/Common/Memory.h"
-
+#include "lld/Common/Strings.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -80,55 +80,22 @@ static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
return Msg + S.getObjMsg(Off);
}
-// This is a MIPS-specific rule.
-//
-// In case of MIPS GP-relative relocations always resolve to a definition
-// in a regular input file, ignoring the one-definition rule. So we,
-// for example, should not attempt to create a dynamic relocation even
-// if the target symbol is preemptible. There are two two MIPS GP-relative
-// relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16
-// can be against a preemptible symbol.
-//
-// To get MIPS relocation type we apply 0xff mask. In case of O32 ABI all
-// relocation types occupy eight bit. In case of N64 ABI we extract first
-// relocation from 3-in-1 packet because only the first relocation can
-// be against a real symbol.
-static bool isMipsGprel(RelType Type) {
- if (Config->EMachine != EM_MIPS)
- return false;
- Type &= 0xff;
- return Type == R_MIPS_GPREL16 || Type == R_MICROMIPS_GPREL16 ||
- Type == R_MICROMIPS_GPREL7_S2;
-}
-
// This function is similar to the `handleTlsRelocation`. MIPS does not
// support any relaxations for TLS relocations so by factoring out MIPS
// handling in to the separate function we can simplify the code and do not
// pollute other `handleTlsRelocation` by MIPS `ifs` statements.
// Mips has a custom MipsGotSection that handles the writing of GOT entries
// without dynamic relocations.
-template <class ELFT>
static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym,
InputSectionBase &C, uint64_t Offset,
int64_t Addend, RelExpr Expr) {
if (Expr == R_MIPS_TLSLD) {
- if (InX::MipsGot->addTlsIndex() && Config->Pic)
- InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot,
- InX::MipsGot->getTlsIndexOff(), false, nullptr,
- 0});
+ InX::MipsGot->addTlsIndex(*C.File);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
-
if (Expr == R_MIPS_TLSGD) {
- if (InX::MipsGot->addDynTlsEntry(Sym) && Sym.IsPreemptible) {
- uint64_t Off = InX::MipsGot->getGlobalDynOffset(Sym);
- InX::RelaDyn->addReloc(
- {Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Sym, 0});
- if (Sym.IsPreemptible)
- InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot,
- Off + Config->Wordsize, false, &Sym, 0});
- }
+ InX::MipsGot->addDynTlsEntry(*C.File, Sym);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
@@ -161,7 +128,7 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) {
if (Dyn)
- InX::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0});
+ InX::RelaDyn->addReloc(Type, InX::Got, Off, Dest);
else
InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
};
@@ -207,7 +174,7 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
if (Config->EMachine == EM_ARM)
return handleARMTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr);
if (Config->EMachine == EM_MIPS)
- return handleMipsTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr);
+ return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr);
if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
Config->Shared) {
@@ -221,40 +188,62 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
return 1;
}
- if (isRelExprOneOf<R_TLSLD_PC, R_TLSLD>(Expr)) {
+ if (isRelExprOneOf<R_TLSLD_GOT, R_TLSLD_GOT_FROM_END, R_TLSLD_PC,
+ R_TLSLD_HINT>(Expr)) {
// 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 2;
+ {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type,
+ Offset, Addend, &Sym});
+ return Target->TlsGdRelaxSkip;
}
+ if (Expr == R_TLSLD_HINT)
+ return 1;
if (InX::Got->addTlsIndex())
- InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got,
- InX::Got->getTlsIndexOff(), false, nullptr, 0});
+ InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got,
+ InX::Got->getTlsIndexOff(), nullptr);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (isRelExprOneOf<R_ABS, R_TLSLD, R_TLSLD_PC>(Expr) && !Config->Shared) {
- C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym});
+ if (Expr == R_ABS && !Config->Shared) {
+ C.Relocations.push_back(
+ {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type,
+ Offset, Addend, &Sym});
return 1;
}
- if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD,
- R_TLSGD_PC>(Expr)) {
+ // 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;
+ }
+ if (!Sym.isInGot()) {
+ InX::Got->addEntry(Sym);
+ uint64_t Off = Sym.getGotOffset();
+ InX::Got->Relocations.push_back({R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
+ }
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ return 1;
+ }
+
+ if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD_GOT,
+ R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
if (Config->Shared) {
if (InX::Got->addDynTlsEntry(Sym)) {
uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
- InX::RelaDyn->addReloc(
- {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0});
+ InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::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)
- InX::RelaDyn->addReloc(
- {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Sym, 0});
+ InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::Got, OffsetOff,
+ &Sym);
else
InX::Got->Relocations.push_back(
{R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym});
@@ -271,8 +260,8 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
Offset, Addend, &Sym});
if (!Sym.isInGot()) {
InX::Got->addEntry(Sym);
- InX::RelaDyn->addReloc(
- {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0});
+ InX::RelaDyn->addReloc(Target->TlsGotRel, InX::Got, Sym.getGotOffset(),
+ &Sym);
}
} else {
C.Relocations.push_back(
@@ -336,7 +325,7 @@ static bool isAbsoluteValue(const Symbol &Sym) {
// Returns true if Expr refers a PLT entry.
static bool needsPlt(RelExpr Expr) {
- return isRelExprOneOf<R_PLT_PC, R_PPC_PLT_OPD, R_PLT, R_PLT_PAGE_PC>(Expr);
+ return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_PLT_PAGE_PC>(Expr);
}
// Returns true if Expr refers a GOT entry. Note that this function
@@ -352,7 +341,8 @@ static bool needsGot(RelExpr Expr) {
// 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_PAGE_PC, R_RELAX_GOT_PC>(Expr);
+ R_PPC_CALL, R_PPC_CALL_PLT, R_PAGE_PC,
+ R_RELAX_GOT_PC>(Expr);
}
// Returns true if a given relocation can be computed at link-time.
@@ -367,11 +357,13 @@ static bool isRelExpr(RelExpr Expr) {
static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
InputSectionBase &S, uint64_t RelOff) {
// These expressions always compute a constant
- if (isRelExprOneOf<R_SIZE, R_GOT_FROM_END, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE,
- R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
- R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
- R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_PC, R_TLSGD,
- R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT>(E))
+ if (isRelExprOneOf<
+ R_GOT_FROM_END, R_GOT_OFF, 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_GOT_PAGE_PC, 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_TLSDESC_PAGE, R_HINT,
+ R_TLSLD_HINT>(E))
return true;
// These never do, except if the entire file is position dependent or if
@@ -384,6 +376,10 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
if (!Config->Pic)
return true;
+ // The size of a non preemptible symbol is a constant.
+ 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);
@@ -413,39 +409,45 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
}
static RelExpr toPlt(RelExpr Expr) {
- if (Expr == R_PPC_OPD)
- return R_PPC_PLT_OPD;
- if (Expr == R_PC)
+ switch (Expr) {
+ case R_PPC_CALL:
+ return R_PPC_CALL_PLT;
+ case R_PC:
return R_PLT_PC;
- if (Expr == R_PAGE_PC)
+ case R_PAGE_PC:
return R_PLT_PAGE_PC;
- if (Expr == R_ABS)
+ case R_ABS:
return R_PLT;
- return Expr;
+ default:
+ return 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.
- if (Expr == R_PLT_PC)
+ switch (Expr) {
+ case R_PLT_PC:
return R_PC;
- if (Expr == R_PPC_PLT_OPD)
- return R_PPC_OPD;
- if (Expr == R_PLT)
+ case R_PPC_CALL_PLT:
+ return R_PPC_CALL;
+ case R_PLT:
return R_ABS;
- return Expr;
+ default:
+ 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) {
+template <class ELFT> static bool isReadOnly(SharedSymbol &SS) {
typedef typename ELFT::Phdr Elf_Phdr;
// Determine if the symbol is read-only by scanning the DSO's program headers.
- const SharedFile<ELFT> &File = SS->getFile<ELFT>();
+ 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)
+ !(Phdr.p_flags & ELF::PF_W) && SS.Value >= Phdr.p_vaddr &&
+ SS.Value < Phdr.p_vaddr + Phdr.p_memsz)
return true;
return false;
}
@@ -454,26 +456,45 @@ template <class ELFT> static bool isReadOnly(SharedSymbol *SS) {
//
// If two or more symbols are at the same offset, and at least one of
// them are copied by a copy relocation, all of them need to be copied.
-// Otherwise, they would refer different places at runtime.
+// Otherwise, they would refer to different places at runtime.
template <class ELFT>
-static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) {
+static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
typedef typename ELFT::Sym Elf_Sym;
- SharedFile<ELFT> &File = SS->getFile<ELFT>();
+ SharedFile<ELFT> &File = SS.getFile<ELFT>();
- std::vector<SharedSymbol *> Ret;
+ SmallSet<SharedSymbol *, 4> Ret;
for (const Elf_Sym &S : File.getGlobalELFSyms()) {
if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
- S.st_value != SS->Value)
+ 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.push_back(Alias);
+ Ret.insert(Alias);
}
return Ret;
}
+// When a symbol is copy relocated or we create a canonical plt entry, it is
+// effectively a defined symbol. In the case of copy relocation the symbol is
+// 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.IsPreemptible = true;
+ Sym.ExportDynamic = true;
+ Sym.IsUsedInRegularObj = true;
+ Sym.Used = true;
+}
+
// Reserve space in .bss or .bss.rel.ro for copy relocation.
//
// The copy relocation is pretty much a hack. If you use a copy relocation
@@ -516,17 +537,17 @@ static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) {
// 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)
- 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);
+ SymSize, SS.Alignment);
if (IsReadOnly)
InX::BssRelRo->getParent()->addSection(Sec);
else
@@ -535,125 +556,10 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol *SS) {
// 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)) {
- Sym->CopyRelSec = Sec;
- Sym->IsPreemptible = false;
- Sym->IsUsedInRegularObj = true;
- Sym->Used = true;
- }
-
- InX::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, SS, 0});
-}
-
-static void errorOrWarn(const Twine &Msg) {
- if (!Config->NoinhibitExec)
- error(Msg);
- else
- warn(Msg);
-}
-
-// Returns PLT relocation expression.
-//
-// 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
-// R_X86_64_PC32 pointing to libc.so.
-//
-// The general idea on how to handle such cases is to create a PLT entry and
-// use that as the function value.
-//
-// For the static linking part, we just return a plt expr and everything
-// else will use the the PLT entry as the address.
-//
-// The remaining problem is making sure pointer equality still works. We
-// need the help of the dynamic linker for that. We let it know that we have
-// a direct reference to a so symbol by creating an undefined symbol with a
-// non zero st_value. Seeing that, the dynamic linker resolves the symbol to
-// the value of the symbol we created. This is true even for got entries, so
-// pointer equality is maintained. To avoid an infinite loop, the only entry
-// that points to the real function is a dedicated got entry used by the
-// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
-// R_386_JMP_SLOT, etc).
-static RelExpr getPltExpr(Symbol &Sym, RelExpr Expr, bool &IsConstant) {
- Sym.NeedsPltAddr = true;
- Sym.IsPreemptible = false;
- IsConstant = true;
- return toPlt(Expr);
-}
-
-// This modifies the expression if we can use a copy relocation or point the
-// symbol to the PLT.
-template <class ELFT>
-static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type,
- InputSectionBase &S, uint64_t RelOff,
- bool &IsConstant) {
- // If a relocation can be applied at link-time, we don't need to
- // create a dynamic relocation in the first place.
- if (IsConstant)
- return Expr;
-
- // 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()) {
- Sym.IsPreemptible = false;
- IsConstant = true;
- return Expr;
- }
-
- // We can create any dynamic relocation supported by the dynamic linker if a
- // section is writable or we are passed -z notext.
- bool CanWrite = (S.Flags & SHF_WRITE) || !Config->ZText;
- if (CanWrite && Target->isPicRel(Type))
- return Expr;
-
- // If we got here we know that this relocation would require the dynamic
- // linker to write a value to read only memory or use an unsupported
- // relocation.
-
- // We can hack around it if we are producing an executable and
- // the refered symbol can be preemepted to refer to the executable.
- if (!CanWrite && (Config->Shared || (Config->Pic && !isRelExpr(Expr)))) {
- error(
- "can't create dynamic relocation " + toString(Type) + " against " +
- (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) +
- " in readonly segment; recompile object files with -fPIC" +
- getLocation(S, Sym, RelOff));
- return Expr;
- }
-
- // Copy relocations are only possible if we are creating an executable and the
- // symbol is shared.
- if (!Sym.isShared() || Config->Shared)
- return Expr;
-
- if (Sym.getVisibility() != STV_DEFAULT) {
- error("cannot preempt symbol: " + toString(Sym) +
- getLocation(S, Sym, RelOff));
- return Expr;
- }
-
- if (Sym.isObject()) {
- // Produce a copy relocation.
- auto *B = dyn_cast<SharedSymbol>(&Sym);
- if (B && !B->CopyRelSec) {
- if (Config->ZNocopyreloc)
- error("unresolvable relocation " + toString(Type) +
- " against symbol '" + toString(*B) +
- "'; recompile with -fPIC or remove '-z nocopyreloc'" +
- getLocation(S, Sym, RelOff));
-
- addCopyRelSymbol<ELFT>(B);
- }
- IsConstant = true;
- return Expr;
- }
-
- if (Sym.isFunc())
- return getPltExpr(Sym, Expr, IsConstant);
+ for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS))
+ replaceWithDefined(*Sym, Sec, 0, Sym->Size);
- errorOrWarn("symbol '" + toString(Sym) + "' defined in " +
- toString(Sym.File) + " has no type");
- return Expr;
+ InX::RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
}
// MIPS has an odd notion of "paired" relocations to calculate addends.
@@ -728,7 +634,7 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
return false;
bool CanBeExternal =
- Sym.computeBinding() != STB_LOCAL && Sym.getVisibility() == STV_DEFAULT;
+ Sym.computeBinding() != STB_LOCAL && Sym.Visibility == STV_DEFAULT;
if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal)
return false;
@@ -756,12 +662,12 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
// 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 = Rel->getType(Config->IsMips64EL);
+ RelType Type = 0;
uint64_t Offset = Rel->r_offset;
int N = 0;
- while (Rel + 1 != End && (Rel + 1)->r_offset == Offset)
- Type |= (++Rel)->getType(Config->IsMips64EL) << (8 * ++N);
+ while (Rel != End && Rel->r_offset == Offset)
+ Type |= (Rel++)->getType(Config->IsMips64EL) << (8 * N++);
return Type;
}
@@ -811,16 +717,34 @@ private:
};
} // 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
+ // 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
+ // don't store the addend values, so we must write it to the relocated
+ // address.
+ if (InX::RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
+ IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
+ InX::RelrDyn->Relocs.push_back({IS, OffsetInSec});
+ return;
+ }
+ InX::RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend,
+ Expr, Type);
+}
+
template <class ELFT, class GotPltSection>
static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
- RelocationBaseSection *Rel, RelType Type, Symbol &Sym,
- bool UseSymVA) {
+ RelocationBaseSection *Rel, RelType Type, Symbol &Sym) {
Plt->addEntry<ELFT>(Sym);
GotPlt->addEntry(Sym);
- Rel->addReloc({Type, GotPlt, Sym.getGotPltOffset(), UseSymVA, &Sym, 0});
+ Rel->addReloc(
+ {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0});
}
-template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) {
+template <class ELFT> static void addGotEntry(Symbol &Sym) {
InX::Got->addEntry(Sym);
RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS;
@@ -833,7 +757,8 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) {
// 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 = !Preemptible && (!Config->Pic || isAbsolute(Sym));
+ bool IsLinkTimeConstant =
+ !Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym));
if (IsLinkTimeConstant) {
InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
return;
@@ -841,23 +766,33 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) {
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
// the GOT slot will be fixed at load-time.
- RelType Type;
- if (Sym.isTls())
- Type = Target->TlsGotRel;
- else if (!Preemptible && Config->Pic && !isAbsolute(Sym))
- Type = Target->RelativeRel;
- else
- Type = Target->GotRel;
- InX::RelaDyn->addReloc({Type, InX::Got, Off, !Preemptible, &Sym, 0});
+ if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) {
+ addRelativeReloc(InX::Got, Off, &Sym, 0, R_ABS, Target->GotRel);
+ return;
+ }
+ InX::RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel,
+ InX::Got, Off, &Sym, 0,
+ Sym.IsPreemptible ? R_ADDEND : R_ABS, Target->GotRel);
+}
- // REL type relocations don't have addend fields unlike RELAs, and
- // their addends are stored to the section to which they are applied.
- // So, store addends if we need to.
- //
- // This is ugly -- the difference between REL and RELA should be
- // handled in a better way. It's a TODO.
- if (!Config->IsRela && !Preemptible)
- InX::Got->Relocations.push_back({R_ABS, Target->GotRel, Off, 0, &Sym});
+// 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) {
+ // 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)
+ 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));
}
// The reason we have to do this early scan is as follows
@@ -874,129 +809,23 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) {
// 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 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; ++I) {
- const RelTy &Rel = *I;
- Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
- RelType Type = Rel.getType(Config->IsMips64EL);
-
- // Deal with MIPS oddity.
- if (Config->MipsN32Abi)
- Type = getMipsN32RelType(I, End);
-
- // 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))
- continue;
-
- // Skip if the target symbol is an erroneous undefined symbol.
- if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
- continue;
-
- RelExpr Expr =
- Target->getRelExpr(Type, Sym, Sec.Data.begin() + Rel.r_offset);
-
- // Ignore "hint" relocations because they are only markers for relaxation.
- if (isRelExprOneOf<R_HINT, R_NONE>(Expr))
- continue;
-
- // Handle yet another MIPS-ness.
- if (isMipsGprel(Type)) {
- int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
- Sec.Relocations.push_back({R_MIPS_GOTREL, Type, Offset, Addend, &Sym});
- continue;
- }
-
- bool Preemptible = Sym.IsPreemptible;
-
- // Strenghten or relax a PLT access.
- //
- // GNU ifunc symbols must be accessed via PLT because their addresses
- // are determined by runtime.
- //
- // 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())
- Expr = toPlt(Expr);
- else if (!Preemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym))
- Expr =
- Target->adjustRelaxExpr(Type, Sec.Data.data() + Rel.r_offset, Expr);
- else if (!Preemptible)
- Expr = fromPlt(Expr);
-
- bool IsConstant =
- isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Rel.r_offset);
-
- Expr = adjustExpr<ELFT>(Sym, Expr, Type, Sec, Rel.r_offset, IsConstant);
- if (errorCount())
- continue;
-
- // 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))
- InX::Got->HasGotOffRel = true;
-
- // Read an addend.
- int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
-
- // 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);
- continue;
- }
-
- // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
- if (needsPlt(Expr) && !Sym.isInPlt()) {
- if (Sym.isGnuIFunc() && !Preemptible)
- addPltEntry<ELFT>(InX::Iplt, InX::IgotPlt, InX::RelaIplt,
- Target->IRelativeRel, Sym, true);
- else
- addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
- Sym, !Preemptible);
- }
-
- // 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
- InX::MipsGot->addEntry(Sym, Addend, Expr);
- if (Sym.isTls() && Sym.IsPreemptible)
- InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot,
- Sym.getGotOffset(), false, &Sym, 0});
- } else if (!Sym.isInGot()) {
- addGotEntry<ELFT>(Sym, Preemptible);
- }
- }
-
- if (!needsPlt(Expr) && !needsGot(Expr) && Sym.IsPreemptible) {
- // We don't know anything about the finaly symbol. Just ask the dynamic
- // linker to handle the relocation for us.
- if (!Target->isPicRel(Type))
- errorOrWarn(
- "relocation " + toString(Type) +
- " cannot be used against shared object; recompile with -fPIC" +
- getLocation(Sec, Sym, Offset));
+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});
+ 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;
- InX::RelaDyn->addReloc(
- {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend});
+ if (!IsPreemptibleValue) {
+ addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type);
+ return;
+ } else if (RelType Rel = Target->getDynRel(Type)) {
+ InX::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
@@ -1014,37 +843,210 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
if (Config->EMachine == EM_MIPS)
- InX::MipsGot->addEntry(Sym, Addend, Expr);
- continue;
+ InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
+ return;
}
+ }
- // The size is not going to change, so we fold it in here.
- if (Expr == R_SIZE)
- Addend += Sym.getSize();
+ // 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 the produced value is a constant, we just remember to write it
- // when outputting this section. We also have to do it if the format
- // uses Elf_Rel, since in that case the written value is the addend.
- if (IsConstant) {
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
- continue;
+ if (!CanWrite && (Config->Pic && !isRelExpr(Expr))) {
+ error(
+ "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));
+ 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));
+ return;
+ }
+
+ // If the symbol is undefined we already reported any relevant errors.
+ if (Sym.isUndefined())
+ return;
+
+ if (!canDefineSymbolInExecutable(Sym)) {
+ error("cannot preempt symbol: " + toString(Sym) +
+ getLocation(Sec, Sym, Offset));
+ return;
+ }
+
+ 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) +
+ "'; recompile with -fPIC or remove '-z nocopyreloc'" +
+ getLocation(Sec, Sym, Offset));
+ addCopyRelSymbol<ELFT>(*SS);
}
+ Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ return;
+ }
- // If the output being produced is position independent, the final value
- // is still not known. In that case we still need some help from the
- // dynamic linker. We can however do better than just copying the incoming
- // relocation. We can process some of it and and just ask the dynamic
- // linker to add the load address.
- if (Config->IsRela) {
- InX::RelaDyn->addReloc(
- {Target->RelativeRel, &Sec, Offset, true, &Sym, Addend});
- } else {
- // In REL, addends are stored to the target section.
- InX::RelaDyn->addReloc(
- {Target->RelativeRel, &Sec, Offset, true, &Sym, 0});
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ 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
+ // R_X86_64_PC32 pointing to libc.so.
+ //
+ // The general idea on how to handle such cases is to create a PLT entry and
+ // use that as the function value.
+ //
+ // For the static linking part, we just return a plt expr and everything
+ // else will use the PLT entry as the address.
+ //
+ // The remaining problem is making sure pointer equality still works. We
+ // need the help of the dynamic linker for that. We let it know that we have
+ // a direct reference to a so symbol by creating an undefined symbol with a
+ // non zero st_value. Seeing that, the dynamic linker resolves the symbol to
+ // the value of the symbol we created. This is true even for got entries, so
+ // pointer equality is maintained. To avoid an infinite loop, the only entry
+ // that points to the real function is a dedicated got entry used by the
+ // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
+ // R_386_JMP_SLOT, etc).
+
+ // For position independent executable on i386, the plt entry requires ebx
+ // to be set. This causes two problems:
+ // * If some code has a direct reference to a function, it was probably
+ // 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) +
+ "' cannot be preempted; recompile with -fPIE" +
+ getLocation(Sec, Sym, Offset));
+ if (!Sym.isInPlt())
+ addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
+ Sym);
+ if (!Sym.isDefined())
+ replaceWithDefined(Sym, InX::Plt, Sym.getPltOffset(), 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));
+}
+
+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;
+
+ // Deal with MIPS oddity.
+ if (Config->MipsN32Abi) {
+ Type = getMipsN32RelType(I, End);
+ } else {
+ 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))
+ return;
+
+ // Skip if the target symbol is an erroneous undefined symbol.
+ if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
+ return;
+
+ 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))
+ return;
+
+ // Strenghten or relax relocations.
+ //
+ // GNU ifunc symbols must be accessed via PLT because their addresses
+ // are determined by runtime.
+ //
+ // 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())
+ 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))
+ InX::Got->HasGotOffRel = true;
+
+ // Read an addend.
+ int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
+
+ // 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);
+ 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>(InX::Iplt, InX::IgotPlt, InX::RelaIplt,
+ Target->IRelativeRel, Sym);
+ else
+ addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::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
+ InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
+ } else if (!Sym.isInGot()) {
+ addGotEntry<ELFT>(Sym);
}
}
+
+ 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);
+
+ // 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);
}
template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
@@ -1259,17 +1261,30 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
//
// We follow a simple but conservative heuristic to place ThunkSections at
// offsets that are multiples of a Target specific branch range.
-// For an InputSectionRange that is smaller than the range, a single
+// For an InputSectionDescription that is smaller than the range, a single
// ThunkSection at the end of the range will do.
+//
+// For an InputSectionDescription that is more than twice the size of the range,
+// we place the last ThunkSection at range bytes from the end of the
+// InputSectionDescription in order to increase the likelihood that the
+// 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) {
forEachInputSectionDescription(
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 > Target->ThunkSectionSpacing * 2)
+ LastThunkLowerBound = ISDEnd - Target->ThunkSectionSpacing;
+
uint32_t ISLimit;
- uint32_t PrevISLimit = ISD->Sections.front()->OutSecOff;
- uint32_t ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ uint32_t PrevISLimit = ISDBegin;
+ uint32_t ThunkUpperBound = ISDBegin + Target->ThunkSectionSpacing;
for (const InputSection *IS : ISD->Sections) {
ISLimit = IS->OutSecOff + IS->getSize();
@@ -1277,6 +1292,8 @@ void ThunkCreator::createInitialThunkSections(
addThunkSection(OS, ISD, PrevISLimit);
ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
}
+ if (ISLimit > LastThunkLowerBound)
+ break;
PrevISLimit = ISLimit;
}
addThunkSection(OS, ISD, ISLimit);
@@ -1293,17 +1310,22 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
uint64_t Src) {
- auto Res = ThunkedSymbols.insert({&Sym, std::vector<Thunk *>()});
- if (!Res.second) {
- // Check existing Thunks for Sym to see if they can be reused
- for (Thunk *ET : Res.first->second)
- if (ET->isCompatibleWith(Type) &&
- Target->inBranchRange(Type, Src, ET->ThunkSym->getVA()))
- return std::make_pair(ET, false);
- }
+ 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];
+ // Check existing Thunks for Sym to see if they can be reused
+ for (Thunk *ET : *ThunkVec)
+ if (ET->isCompatibleWith(Type) &&
+ Target->inBranchRange(Type, Src, ET->getThunkTargetSym()->getVA()))
+ return std::make_pair(ET, false);
// No existing compatible Thunk in range, create a new one
Thunk *T = addThunk(Type, Sym);
- Res.first->second.push_back(T);
+ ThunkVec->push_back(T);
return std::make_pair(T, true);
}
@@ -1311,7 +1333,7 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
// InputSectionDescription::Sections.
void ThunkCreator::forEachInputSectionDescription(
ArrayRef<OutputSection *> OutputSections,
- std::function<void(OutputSection *, InputSectionDescription *)> Fn) {
+ llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
for (OutputSection *OS : OutputSections) {
if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
continue;
@@ -1379,7 +1401,7 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
for (InputSection *IS : ISD->Sections)
for (Relocation &Rel : IS->Relocations) {
- uint64_t Src = OS->Addr + IS->OutSecOff + Rel.Offset;
+ uint64_t Src = IS->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
@@ -1394,7 +1416,6 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
bool IsNew;
std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src);
if (IsNew) {
- AddressesChanged = true;
// Find or create a ThunkSection for the new Thunk
ThunkSection *TS;
if (auto *TIS = T->getTargetInputSection())
@@ -1402,13 +1423,18 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
else
TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src);
TS->addThunk(T);
- Thunks[T->ThunkSym] = T;
+ Thunks[T->getThunkTargetSym()] = T;
}
// Redirect relocation to Thunk, we never go via the PLT to a Thunk
- Rel.Sym = T->ThunkSym;
+ Rel.Sym = T->getThunkTargetSym();
Rel.Expr = fromPlt(Rel.Expr);
}
+ for (auto &P : ISD->ThunkSections)
+ AddressesChanged |= P.first->assignOffsets();
});
+ for (auto &P : ThunkedSections)
+ AddressesChanged |= P.second->assignOffsets();
+
// Merge all created synthetic ThunkSections back into OutputSection
mergeThunks(OutputSections);
++Pass;
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index 2cc8adfa5985..a4125111c4fe 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -21,7 +21,7 @@ class Symbol;
class InputSection;
class InputSectionBase;
class OutputSection;
-class OutputSection;
+class SectionBase;
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
typedef uint32_t RelType;
@@ -32,6 +32,7 @@ typedef uint32_t RelType;
enum RelExpr {
R_INVALID,
R_ABS,
+ R_ADDEND,
R_ARM_SBREL,
R_GOT,
R_GOTONLY_PC,
@@ -58,27 +59,33 @@ enum RelExpr {
R_PLT,
R_PLT_PAGE_PC,
R_PLT_PC,
- R_PPC_OPD,
- R_PPC_PLT_OPD,
+ 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_PAGE_PC,
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_SIZE,
R_TLS,
R_TLSDESC,
R_TLSDESC_CALL,
R_TLSDESC_PAGE,
- R_TLSGD,
+ R_TLSGD_GOT,
+ R_TLSGD_GOT_FROM_END,
R_TLSGD_PC,
- R_TLSLD,
+ R_TLSLD_GOT,
+ R_TLSLD_GOT_FROM_END,
+ R_TLSLD_GOT_OFF,
+ R_TLSLD_HINT,
R_TLSLD_PC,
};
@@ -150,7 +157,7 @@ private:
void forEachInputSectionDescription(
ArrayRef<OutputSection *> OutputSections,
- std::function<void(OutputSection *, InputSectionDescription *)> Fn);
+ llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn);
std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
@@ -160,6 +167,8 @@ private:
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;
// Find a Thunk from the Thunks symbol definition, we can use this to find
diff --git a/ELF/ScriptLexer.cpp b/ELF/ScriptLexer.cpp
index ef5a1cff7590..d4b1f6d99cc1 100644
--- a/ELF/ScriptLexer.cpp
+++ b/ELF/ScriptLexer.cpp
@@ -66,8 +66,6 @@ size_t ScriptLexer::getColumnNumber() {
std::string ScriptLexer::getCurrentLocation() {
std::string Filename = getCurrentMB().getBufferIdentifier();
- if (!Pos)
- return Filename;
return (Filename + ":" + Twine(getLineNumber())).str();
}
@@ -116,8 +114,9 @@ void ScriptLexer::tokenize(MemoryBufferRef MB) {
}
// ">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("||") || S.startswith("&&")) {
Vec.push_back(S.substr(0, 2));
S = S.substr(2);
continue;
@@ -282,10 +281,7 @@ static bool encloses(StringRef S, StringRef T) {
MemoryBufferRef ScriptLexer::getCurrentMB() {
// Find input buffer containing the current token.
- assert(!MBs.empty());
- if (!Pos)
- return MBs[0];
-
+ assert(!MBs.empty() && Pos > 0);
for (MemoryBufferRef MB : MBs)
if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
return MB;
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index 4263944981f2..ddb4a49a3e5e 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -63,6 +63,7 @@ private:
void readExtern();
void readGroup();
void readInclude();
+ void readInput();
void readMemory();
void readOutput();
void readOutputArch();
@@ -74,12 +75,14 @@ private:
void readVersion();
void readVersionScriptCommand();
- SymbolAssignment *readAssignment(StringRef Name);
+ SymbolAssignment *readSymbolAssignment(StringRef Name);
ByteCommand *readByteCommand(StringRef Tok);
uint32_t readFill();
uint32_t parseFill(StringRef Tok);
void readSectionAddressType(OutputSection *Cmd);
+ OutputSection *readOverlaySectionDescription();
OutputSection *readOutputSectionDescription(StringRef OutSec);
+ std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
InputSectionDescription *readInputSectionDescription(StringRef Tok);
StringMatcher readFilePatterns();
@@ -88,16 +91,16 @@ private:
unsigned readPhdrType();
SortSectionPolicy readSortKind();
SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
- SymbolAssignment *readProvideOrAssignment(StringRef Tok);
+ SymbolAssignment *readAssignment(StringRef Tok);
void readSort();
- AssertCommand *readAssert();
- Expr readAssertExpr();
+ Expr readAssert();
Expr readConstant();
Expr getPageSize();
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
+ Expr combine(StringRef Op, Expr L, Expr R);
Expr readExpr();
Expr readExpr1(Expr Lhs, int MinPrec);
StringRef readParenLiteral();
@@ -157,17 +160,6 @@ static ExprValue sub(ExprValue A, ExprValue B) {
return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
}
-static ExprValue mul(ExprValue A, ExprValue B) {
- return A.getValue() * B.getValue();
-}
-
-static ExprValue div(ExprValue A, ExprValue B) {
- if (uint64_t BV = B.getValue())
- return A.getValue() / BV;
- error("division by zero");
- return 0;
-}
-
static ExprValue bitAnd(ExprValue A, ExprValue B) {
moveAbsRight(A, B);
return {A.Sec, A.ForceAbsolute,
@@ -237,16 +229,16 @@ void ScriptParser::readLinkerScript() {
if (Tok == ";")
continue;
- if (Tok == "ASSERT") {
- Script->SectionCommands.push_back(readAssert());
- } else if (Tok == "ENTRY") {
+ if (Tok == "ENTRY") {
readEntry();
} else if (Tok == "EXTERN") {
readExtern();
- } else if (Tok == "GROUP" || Tok == "INPUT") {
+ } else if (Tok == "GROUP") {
readGroup();
} else if (Tok == "INCLUDE") {
readInclude();
+ } else if (Tok == "INPUT") {
+ readInput();
} else if (Tok == "MEMORY") {
readMemory();
} else if (Tok == "OUTPUT") {
@@ -265,7 +257,7 @@ void ScriptParser::readLinkerScript() {
readSections();
} else if (Tok == "VERSION") {
readVersion();
- } else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) {
+ } else if (SymbolAssignment *Cmd = readAssignment(Tok)) {
Script->SectionCommands.push_back(Cmd);
} else {
setError("unknown directive: " + Tok);
@@ -336,13 +328,12 @@ void ScriptParser::readExtern() {
}
void ScriptParser::readGroup() {
- expect("(");
- while (!errorCount() && !consume(")")) {
- if (consume("AS_NEEDED"))
- readAsNeeded();
- else
- addFile(unquote(next()));
- }
+ bool Orig = InputFile::IsInGroup;
+ InputFile::IsInGroup = true;
+ readInput();
+ InputFile::IsInGroup = Orig;
+ if (!Orig)
+ ++InputFile::NextGroupId;
}
void ScriptParser::readInclude() {
@@ -353,7 +344,7 @@ void ScriptParser::readInclude() {
return;
}
- if (Optional<std::string> Path = searchLinkerScript(Tok)) {
+ if (Optional<std::string> Path = searchScript(Tok)) {
if (Optional<MemoryBufferRef> MB = readFile(*Path))
tokenize(*MB);
return;
@@ -361,6 +352,16 @@ void ScriptParser::readInclude() {
setError("cannot find linker script " + Tok);
}
+void ScriptParser::readInput() {
+ expect("(");
+ while (!errorCount() && !consume(")")) {
+ if (consume("AS_NEEDED"))
+ readAsNeeded();
+ else
+ addFile(unquote(next()));
+ }
+}
+
void ScriptParser::readOutput() {
// -o <file> takes predecence over OUTPUT(<file>).
expect("(");
@@ -437,6 +438,49 @@ void ScriptParser::readSearchDir() {
expect(")");
}
+// This reads an overlay description. Overlays are used to describe output
+// sections that use the same virtual memory range and normally would trigger
+// linker's sections sanity check failures.
+// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description
+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();
+ expect(":");
+ expect("AT");
+ Expr LMAExpr = readParenExpr();
+ expect("{");
+
+ 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; };
+ else
+ 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;
+ };
+ V.push_back(make<SymbolAssignment>(".", MoveDot, getCurrentLocation()));
+ return V;
+}
+
void ScriptParser::readSections() {
Script->HasSectionsCommand = true;
@@ -446,26 +490,48 @@ void ScriptParser::readSections() {
Config->SingleRoRx = true;
expect("{");
+ std::vector<BaseCommand *> V;
while (!errorCount() && !consume("}")) {
StringRef Tok = next();
- BaseCommand *Cmd = readProvideOrAssignment(Tok);
- if (!Cmd) {
- if (Tok == "ASSERT")
- Cmd = readAssert();
- else
- Cmd = readOutputSectionDescription(Tok);
+ if (Tok == "OVERLAY") {
+ for (BaseCommand *Cmd : readOverlay())
+ V.push_back(Cmd);
+ continue;
}
- Script->SectionCommands.push_back(Cmd);
+
+ if (BaseCommand *Cmd = readAssignment(Tok))
+ V.push_back(Cmd);
+ else
+ V.push_back(readOutputSectionDescription(Tok));
+ }
+
+ if (!atEOF() && consume("INSERT")) {
+ std::vector<BaseCommand *> *Dest = nullptr;
+ if (consume("AFTER"))
+ Dest = &Script->InsertAfterCommands[next()];
+ else if (consume("BEFORE"))
+ Dest = &Script->InsertBeforeCommands[next()];
+ else
+ setError("expected AFTER/BEFORE, but got '" + next() + "'");
+ if (Dest)
+ Dest->insert(Dest->end(), V.begin(), V.end());
+ return;
}
+
+ Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(),
+ V.end());
}
static int precedence(StringRef Op) {
return StringSwitch<int>(Op)
- .Cases("*", "/", 5)
- .Cases("+", "-", 4)
- .Cases("<<", ">>", 3)
- .Cases("<", "<=", ">", ">=", "==", "!=", 2)
- .Cases("&", "|", 1)
+ .Cases("*", "/", "%", 8)
+ .Cases("+", "-", 7)
+ .Cases("<<", ">>", 6)
+ .Cases("<", "<=", ">", ">=", "==", "!=", 5)
+ .Case("&", 4)
+ .Case("|", 3)
+ .Case("&&", 2)
+ .Case("||", 1)
.Default(-1);
}
@@ -588,11 +654,7 @@ void ScriptParser::readSort() {
expect(")");
}
-AssertCommand *ScriptParser::readAssert() {
- return make<AssertCommand>(readAssertExpr());
-}
-
-Expr ScriptParser::readAssertExpr() {
+Expr ScriptParser::readAssert() {
expect("(");
Expr E = readExpr();
expect(",");
@@ -617,12 +679,14 @@ uint32_t ScriptParser::readFill() {
return V;
}
-// Reads an expression and/or the special directive "(NOLOAD)" for an
-// output section definition.
+// Reads an expression and/or the special directive for an output
+// section definition. Directive is one of following: "(NOLOAD)",
+// "(COPY)", "(INFO)" or "(OVERLAY)".
//
// An output section name can be followed by an address expression
-// and/or by "(NOLOAD)". This grammar is not LL(1) because "(" can be
-// interpreted as either the beginning of some expression or "(NOLOAD)".
+// and/or directive. This grammar is not LL(1) because "(" can be
+// interpreted as either the beginning of some expression or beginning
+// of directive.
//
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
@@ -633,6 +697,11 @@ void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
Cmd->Noload = true;
return;
}
+ if (consume("COPY") || consume("INFO") || consume("OVERLAY")) {
+ expect(")");
+ Cmd->NonAlloc = true;
+ return;
+ }
Cmd->AddrExpr = readExpr();
expect(")");
} else {
@@ -657,10 +726,23 @@ static Expr checkAlignment(Expr E, std::string &Loc) {
};
}
+OutputSection *ScriptParser::readOverlaySectionDescription() {
+ OutputSection *Cmd =
+ Script->createOutputSection(next(), getCurrentLocation());
+ Cmd->InOverlay = true;
+ expect("{");
+ while (!errorCount() && !consume("}"))
+ Cmd->SectionCommands.push_back(readInputSectionRules(next()));
+ Cmd->Phdrs = readOutputSectionPhdrs();
+ return Cmd;
+}
+
OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
OutputSection *Cmd =
Script->createOutputSection(OutSec, getCurrentLocation());
+ size_t SymbolsReferenced = Script->ReferencedSymbols.size();
+
if (peek() != ":")
readSectionAddressType(Cmd);
expect(":");
@@ -684,13 +766,10 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
StringRef Tok = next();
if (Tok == ";") {
// Empty commands are allowed. Do nothing here.
- } else if (SymbolAssignment *Assign = readProvideOrAssignment(Tok)) {
+ } 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 == "ASSERT") {
- Cmd->SectionCommands.push_back(readAssert());
- expect(";");
} 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.
@@ -709,6 +788,14 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
if (consume(">"))
Cmd->MemoryRegionName = next();
+ if (consume("AT")) {
+ expect(">");
+ Cmd->LMARegionName = next();
+ }
+
+ if (Cmd->LMAExpr && !Cmd->LMARegionName.empty())
+ error("section can't have both LMA and a load region");
+
Cmd->Phdrs = readOutputSectionPhdrs();
if (consume("="))
@@ -719,6 +806,8 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
// Consume optional comma following output section command.
consume(",");
+ if (Script->ReferencedSymbols.size() > SymbolsReferenced)
+ Cmd->ExpressionsUseSymbols = true;
return Cmd;
}
@@ -741,30 +830,39 @@ uint32_t ScriptParser::parseFill(StringRef Tok) {
SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) {
expect("(");
- SymbolAssignment *Cmd = readAssignment(next());
+ SymbolAssignment *Cmd = readSymbolAssignment(next());
Cmd->Provide = Provide;
Cmd->Hidden = Hidden;
expect(")");
- expect(";");
return Cmd;
}
-SymbolAssignment *ScriptParser::readProvideOrAssignment(StringRef Tok) {
+SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) {
+ // Assert expression returns Dot, so this is equal to ".=."
+ if (Tok == "ASSERT")
+ return make<SymbolAssignment>(".", readAssert(), getCurrentLocation());
+
+ size_t OldPos = Pos;
SymbolAssignment *Cmd = nullptr;
- if (peek() == "=" || peek() == "+=") {
- Cmd = readAssignment(Tok);
- expect(";");
- } else if (Tok == "PROVIDE") {
+ if (peek() == "=" || peek() == "+=")
+ Cmd = readSymbolAssignment(Tok);
+ else if (Tok == "PROVIDE")
Cmd = readProvideHidden(true, false);
- } else if (Tok == "HIDDEN") {
+ else if (Tok == "HIDDEN")
Cmd = readProvideHidden(false, true);
- } else if (Tok == "PROVIDE_HIDDEN") {
+ 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;
}
-SymbolAssignment *ScriptParser::readAssignment(StringRef Name) {
+SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
StringRef Op = next();
assert(Op == "=" || Op == "+=");
Expr E = readExpr();
@@ -787,15 +885,31 @@ Expr ScriptParser::readExpr() {
return E;
}
-static Expr combine(StringRef Op, Expr L, Expr R) {
+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 mul(L(), R()); };
- if (Op == "/")
- return [=] { return div(L(), R()); };
+ 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");
+ return 0;
+ };
+ }
+ if (Op == "%") {
+ std::string Loc = getCurrentLocation();
+ return [=]() -> uint64_t {
+ 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 == ">>")
@@ -812,6 +926,10 @@ static Expr combine(StringRef Op, Expr L, Expr R) {
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 == "|")
@@ -865,20 +983,13 @@ Expr ScriptParser::readConstant() {
if (S == "MAXPAGESIZE")
return [] { return Config->MaxPageSize; };
setError("unknown constant: " + S);
- return {};
+ 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) {
- // Negative number
- if (Tok.startswith("-")) {
- if (Optional<uint64_t> Val = parseInt(Tok.substr(1)))
- return -*Val;
- return None;
- }
-
// Hexadecimal
uint64_t Val;
if (Tok.startswith_lower("0x")) {
@@ -917,12 +1028,21 @@ ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
.Default(-1);
if (Size == -1)
return nullptr;
- return make<ByteCommand>(readParenExpr(), Size);
+
+ 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;
expect(")");
return Tok;
}
@@ -995,7 +1115,7 @@ Expr ScriptParser::readPrimary() {
};
}
if (Tok == "ASSERT")
- return readAssertExpr();
+ return readAssert();
if (Tok == "CONSTANT")
return readConstant();
if (Tok == "DATA_SEGMENT_ALIGN") {
@@ -1032,8 +1152,10 @@ Expr ScriptParser::readPrimary() {
}
if (Tok == "LENGTH") {
StringRef Name = readParenLiteral();
- if (Script->MemoryRegions.count(Name) == 0)
+ if (Script->MemoryRegions.count(Name) == 0) {
setError("memory region not defined: " + Name);
+ return [] { return 0; };
+ }
return [=] { return Script->MemoryRegions[Name]->Length; };
}
if (Tok == "LOADADDR") {
@@ -1044,10 +1166,22 @@ Expr ScriptParser::readPrimary() {
return Cmd->getLMA();
};
}
+ if (Tok == "MAX" || Tok == "MIN") {
+ expect("(");
+ Expr A = readExpr();
+ expect(",");
+ 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 == "ORIGIN") {
StringRef Name = readParenLiteral();
- if (Script->MemoryRegions.count(Name) == 0)
+ if (Script->MemoryRegions.count(Name) == 0) {
setError("memory region not defined: " + Name);
+ return [] { return 0; };
+ }
return [=] { return Script->MemoryRegions[Name]->Origin; };
}
if (Tok == "SEGMENT_START") {
@@ -1229,6 +1363,9 @@ ScriptParser::readSymbols() {
// Reads an "extern C++" directive, e.g.,
// "extern "C++" { ns::*; "f(int, double)"; };"
+//
+// 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++\"";
@@ -1241,6 +1378,8 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
StringRef Tok = next();
bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
+ if (consume("}"))
+ return Ret;
expect(";");
}
@@ -1280,11 +1419,10 @@ void ScriptParser::readMemory() {
uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
- if (Script->MemoryRegions.count(Name))
+ MemoryRegion *MR =
+ make<MemoryRegion>(Name, Origin, Length, Flags, NegFlags);
+ if (!Script->MemoryRegions.insert({Name, MR}).second)
setError("region '" + Name + "' already defined");
- MemoryRegion *MR = make<MemoryRegion>();
- *MR = {Name, Origin, Length, Flags, NegFlags};
- Script->MemoryRegions[Name] = MR;
}
}
diff --git a/ELF/Strings.cpp b/ELF/Strings.cpp
deleted file mode 100644
index 0ef33a14bc3d..000000000000
--- a/ELF/Strings.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//===- Strings.cpp -------------------------------------------------------===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Strings.h"
-#include "Config.h"
-#include "lld/Common/ErrorHandler.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Demangle/Demangle.h"
-#include <algorithm>
-#include <cstring>
-
-using namespace llvm;
-using namespace lld;
-using namespace lld::elf;
-
-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);
- }
-}
-
-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> elf::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);
- }
- return Hex;
-}
-
-// Returns true if S is valid as a C language identifier.
-bool elf::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); });
-}
diff --git a/ELF/Strings.h b/ELF/Strings.h
deleted file mode 100644
index 5009df65f4c1..000000000000
--- a/ELF/Strings.h
+++ /dev/null
@@ -1,75 +0,0 @@
-//===- Strings.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_STRINGS_H
-#define LLD_ELF_STRINGS_H
-
-#include "lld/Common/LLVM.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/BitVector.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/GlobPattern.h"
-#include <vector>
-
-namespace lld {
-namespace elf {
-
-std::vector<uint8_t> parseHex(StringRef S);
-bool isValidCIdentifier(StringRef S);
-
-// This is a lazy version of StringRef. String size is computed lazily
-// when it is needed. It is more efficient than StringRef to instantiate
-// if you have a string whose size is unknown.
-//
-// ELF string tables contain a lot of null-terminated strings.
-// Most of them are not necessary for the linker because they are names
-// of local symbols and the linker doesn't use local symbol names for
-// name resolution. So, we use this class to represents strings read
-// from string tables.
-class StringRefZ {
-public:
- StringRefZ() : Start(nullptr), Size(0) {}
- StringRefZ(const char *S, size_t Size) : Start(S), Size(Size) {}
-
- /*implicit*/ StringRefZ(const char *S) : Start(S), Size(-1) {}
-
- /*implicit*/ StringRefZ(llvm::StringRef S)
- : Start(S.data()), Size(S.size()) {}
-
- operator llvm::StringRef() const {
- if (Size == (size_t)-1)
- Size = strlen(Start);
- return {Start, Size};
- }
-
-private:
- const char *Start;
- mutable size_t Size;
-};
-
-// This class represents multiple glob patterns.
-class StringMatcher {
-public:
- StringMatcher() = default;
- explicit StringMatcher(ArrayRef<StringRef> Pat);
-
- bool match(StringRef S) const;
-
-private:
- std::vector<llvm::GlobPattern> Patterns;
-};
-
-inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
- return {(const uint8_t *)S.data(), S.size()};
-}
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index b6bf21998863..1f5a84ec2c7d 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -38,7 +38,7 @@ static InputFile *getFirstElf() {
return ObjectFiles[0];
if (!SharedFiles.empty())
return SharedFiles[0];
- return nullptr;
+ return BitcodeFiles[0];
}
// All input object files must be for the same architecture
@@ -82,6 +82,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) {
// Lazy object file
if (auto *F = dyn_cast<LazyObjFile>(File)) {
+ LazyObjFiles.push_back(F);
F->parse<ELFT>();
return;
}
@@ -117,7 +118,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) {
// 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 consist of a program are passed
+// 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())
@@ -130,7 +131,10 @@ template <class ELFT> void SymbolTable::addCombinedLTOObject() {
for (InputFile *File : LTO->compile()) {
DenseSet<CachedHashStringRef> DummyGroups;
- cast<ObjFile<ELFT>>(File)->parse(DummyGroups);
+ auto *Obj = cast<ObjFile<ELFT>>(File);
+ Obj->parse(DummyGroups);
+ for (Symbol *Sym : Obj->getGlobalSymbols())
+ Sym->parseSymbolVersion();
ObjectFiles.push_back(File);
}
}
@@ -154,6 +158,12 @@ template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) {
Symbol *Sym = find(Name);
if (!Sym)
return;
+
+ // Do not wrap the same symbol twice.
+ for (const WrappedSymbol &S : WrappedSymbols)
+ if (S.Sym == Sym)
+ return;
+
Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
WrappedSymbols.push_back({Sym, Real, Wrap});
@@ -186,7 +196,7 @@ void SymbolTable::applySymbolWrap() {
// First, make a copy of __real_sym.
Symbol *Real = nullptr;
if (W.Real->isDefined()) {
- Real = (Symbol *)make<SymbolUnion>();
+ Real = reinterpret_cast<Symbol *>(make<SymbolUnion>());
memcpy(Real, W.Real, sizeof(SymbolUnion));
}
@@ -234,8 +244,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
Symbol *Sym;
if (IsNew) {
- Sym = (Symbol *)make<SymbolUnion>();
- Sym->InVersionScript = false;
+ Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
@@ -294,26 +303,88 @@ Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
uint8_t Visibility = getVisibility(StOther);
std::tie(S, WasInserted) =
insert(Name, Type, 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 (Binding != STB_WEAK) {
+
+ if (!Config->GcSections && Binding != STB_WEAK)
if (auto *SS = dyn_cast<SharedSymbol>(S))
- if (!Config->GcSections)
- SS->getFile<ELFT>().IsNeeded = true;
- }
- if (auto *L = dyn_cast<Lazy>(S)) {
+ SS->getFile<ELFT>().IsNeeded = true;
+
+ 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)
- L->Type = Type;
- else if (InputFile *F = L->fetch())
- addFile<ELFT>(F);
+ 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;
}
@@ -381,7 +452,11 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
bool WasInserted;
std::tie(S, WasInserted) = insert(N, Type, 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;
@@ -389,45 +464,43 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
InputSections.push_back(Bss);
replaceSymbol<Defined>(S, &File, N, Binding, StOther, Type, 0, Size, Bss);
- } else if (Cmp == 0) {
- 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;
- }
+ 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("multiple common of " + D->getName());
+ warn("common " + S->getName() + " is overridden");
+ return S;
+ }
- Bss->Alignment = std::max(Bss->Alignment, Alignment);
- if (Size > Bss->Size) {
- D->File = Bss->File = &File;
- D->Size = Bss->Size = Size;
- }
+ 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 warnOrError(const Twine &Msg) {
- if (Config->AllowMultipleDefinition)
- warn(Msg);
- else
- error(Msg);
-}
-
static void reportDuplicate(Symbol *Sym, InputFile *NewFile) {
- warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
- toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
+ if (!Config->AllowMultipleDefinition)
+ error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
+ toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
}
-static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec,
- uint64_t ErrOffset) {
+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) {
- reportDuplicate(Sym, ErrSec ? ErrSec->File : nullptr);
+ reportDuplicate(Sym, NewFile);
return;
}
@@ -451,7 +524,7 @@ static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec,
if (!Src2.empty())
Msg += Src2 + "\n>>> ";
Msg += Obj2;
- warnOrError(Msg);
+ error(Msg);
}
Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
@@ -467,7 +540,8 @@ Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size,
Section);
else if (Cmp == 0)
- reportDuplicate(S, dyn_cast_or_null<InputSectionBase>(Section), Value);
+ reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section),
+ Value);
return S;
}
@@ -488,15 +562,16 @@ void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File,
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
- if (WasInserted || ((S->isUndefined() || S->isLazy()) &&
- S->getVisibility() == STV_DEFAULT)) {
+ if (WasInserted ||
+ ((S->isUndefined() || S->isLazy()) && S->Visibility == STV_DEFAULT)) {
uint8_t Binding = S->Binding;
+ bool WasUndefined = S->isUndefined();
replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other,
Sym.getType(), Sym.st_value, Sym.st_size,
Alignment, VerdefIndex);
if (!WasInserted) {
S->Binding = Binding;
- if (!S->isWeak() && !Config->GcSections)
+ if (!S->isWeak() && !Config->GcSections && WasUndefined)
File.IsNeeded = true;
}
}
@@ -527,85 +602,58 @@ Symbol *SymbolTable::find(StringRef Name) {
return SymVector[It->second];
}
-template <class ELFT>
-Symbol *SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &F,
- const object::Archive::Symbol Sym) {
+// This is used to handle lazy symbols. May replace existent
+// symbol with lazy version or request to Fetch it.
+template <class ELFT, typename LazyT, typename... ArgT>
+static void replaceOrFetchLazy(StringRef Name, InputFile &File,
+ llvm::function_ref<InputFile *()> Fetch,
+ ArgT &&... Arg) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
+ std::tie(S, WasInserted) = Symtab->insert(Name);
if (WasInserted) {
- replaceSymbol<LazyArchive>(S, F, Sym, Symbol::UnknownType);
- return S;
+ replaceSymbol<LazyT>(S, File, Symbol::UnknownType,
+ std::forward<ArgT>(Arg)...);
+ return;
}
if (!S->isUndefined())
- return S;
+ 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, F, Sym, S->Type);
+ replaceSymbol<LazyT>(S, File, S->Type, std::forward<ArgT>(Arg)...);
S->Binding = STB_WEAK;
- return S;
+ return;
}
- std::pair<MemoryBufferRef, uint64_t> MBInfo = F.getMember(&Sym);
- if (!MBInfo.first.getBuffer().empty())
- addFile<ELFT>(createObjectFile(MBInfo.first, F.getName(), MBInfo.second));
- return S;
+
+ if (InputFile *F = Fetch())
+ Symtab->addFile<ELFT>(F);
}
template <class ELFT>
-void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
- if (WasInserted) {
- replaceSymbol<LazyObject>(S, Obj, Name, Symbol::UnknownType);
- return;
- }
- if (!S->isUndefined())
- return;
-
- // See comment for addLazyArchive above.
- if (S->isWeak())
- replaceSymbol<LazyObject>(S, Obj, Name, S->Type);
- else if (InputFile *F = Obj.fetch())
- addFile<ELFT>(F);
-}
-
-// If we already saw this symbol, force loading its file.
-template <class ELFT> void SymbolTable::fetchIfLazy(StringRef Name) {
- if (Symbol *B = find(Name)) {
- // Mark the symbol not to be eliminated by LTO
- // even if it is a bitcode symbol.
- B->IsUsedInRegularObj = true;
- if (auto *L = dyn_cast<Lazy>(B))
- if (InputFile *File = L->fetch())
- addFile<ELFT>(File);
- }
+void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &F,
+ const object::Archive::Symbol Sym) {
+ replaceOrFetchLazy<ELFT, LazyArchive>(Name, F, [&]() { return F.fetch(Sym); },
+ Sym);
}
-// This function takes care of the case in which shared libraries depend on
-// the user program (not the other way, which is usual). Shared libraries
-// may have undefined symbols, expecting that the user program provides
-// the definitions for them. An example is BSD's __progname symbol.
-// We need to put such symbols to the main program's .dynsym so that
-// shared libraries can find them.
-// Except this, we ignore undefined symbols in DSOs.
-template <class ELFT> void SymbolTable::scanShlibUndefined() {
- for (InputFile *F : SharedFiles) {
- for (StringRef U : cast<SharedFile<ELFT>>(F)->getUndefinedSymbols()) {
- Symbol *Sym = find(U);
- if (!Sym || !Sym->isDefined())
- continue;
- Sym->ExportDynamic = true;
+template <class ELFT>
+void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) {
+ replaceOrFetchLazy<ELFT, LazyObject>(Name, Obj, [&]() { return Obj.fetch(); },
+ Name);
+}
- // If -dynamic-list is given, the default version is set to
- // VER_NDX_LOCAL, which prevents a symbol to be exported via .dynsym.
- // Set to VER_NDX_GLOBAL so the symbol will be handled as if it were
- // specified by -dynamic-list.
- Sym->VersionId = VER_NDX_GLOBAL;
- }
+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
@@ -704,7 +752,7 @@ void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
// Get a list of symbols which we need to assign the version to.
std::vector<Symbol *> Syms = findByVersion(Ver);
if (Syms.empty()) {
- if (Config->NoUndefinedVersion)
+ if (!Config->UndefinedVersion)
error("version script assignment of '" + VersionName + "' to symbol '" +
Ver.Name + "' failed: symbol not defined");
return;
@@ -718,10 +766,10 @@ void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
if (Sym->getName().contains('@'))
continue;
- if (Sym->InVersionScript)
- warn("duplicate symbol '" + Ver.Name + "' in version script");
+ if (Sym->VersionId != Config->DefaultSymbolVersion &&
+ Sym->VersionId != VersionId)
+ error("duplicate symbol '" + Ver.Name + "' in version script");
Sym->VersionId = VersionId;
- Sym->InVersionScript = true;
}
}
@@ -769,6 +817,11 @@ void SymbolTable::scanVersionScript() {
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 void SymbolTable::addSymbolWrap<ELF32LE>(StringRef);
template void SymbolTable::addSymbolWrap<ELF32BE>(StringRef);
template void SymbolTable::addSymbolWrap<ELF64LE>(StringRef);
@@ -793,16 +846,16 @@ template void SymbolTable::addCombinedLTOObject<ELF32BE>();
template void SymbolTable::addCombinedLTOObject<ELF64LE>();
template void SymbolTable::addCombinedLTOObject<ELF64BE>();
-template Symbol *
+template void
SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
-template Symbol *
+template void
SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
-template Symbol *
+template void
SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
-template Symbol *
+template void
SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
@@ -811,6 +864,11 @@ 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);
@@ -823,13 +881,3 @@ template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> &,
template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> &,
const typename ELF64BE::Sym &,
uint32_t Alignment, uint32_t);
-
-template void SymbolTable::fetchIfLazy<ELF32LE>(StringRef);
-template void SymbolTable::fetchIfLazy<ELF32BE>(StringRef);
-template void SymbolTable::fetchIfLazy<ELF64LE>(StringRef);
-template void SymbolTable::fetchIfLazy<ELF64BE>(StringRef);
-
-template void SymbolTable::scanShlibUndefined<ELF32LE>();
-template void SymbolTable::scanShlibUndefined<ELF32BE>();
-template void SymbolTable::scanShlibUndefined<ELF64LE>();
-template void SymbolTable::scanShlibUndefined<ELF64BE>();
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
index e7341b05baf5..5e6d44dfe4f9 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -12,7 +12,7 @@
#include "InputFiles.h"
#include "LTO.h"
-#include "Strings.h"
+#include "lld/Common/Strings.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
@@ -60,8 +60,8 @@ public:
uint32_t VerdefIndex);
template <class ELFT>
- Symbol *addLazyArchive(StringRef Name, ArchiveFile &F,
- const llvm::object::Archive::Symbol S);
+ void addLazyArchive(StringRef Name, ArchiveFile &F,
+ const llvm::object::Archive::Symbol S);
template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj);
@@ -77,8 +77,8 @@ public:
uint8_t Visibility, bool CanOmitFromDynSym,
InputFile *File);
- template <class ELFT> void fetchIfLazy(StringRef Name);
- template <class ELFT> void scanShlibUndefined();
+ template <class ELFT> void fetchLazy(Symbol *Sym);
+
void scanVersionScript();
Symbol *find(StringRef Name);
@@ -90,7 +90,6 @@ public:
private:
std::vector<Symbol *> findByVersion(SymbolVersion Ver);
std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
- void defsym(Symbol *Dst, Symbol *Src);
llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
void handleAnonymousVersion();
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 13a91aab80bb..4243cb1e80ef 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -14,7 +14,6 @@
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
-
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
@@ -39,6 +38,7 @@ Defined *ElfSym::GlobalOffsetTable;
Defined *ElfSym::MipsGp;
Defined *ElfSym::MipsGpDisp;
Defined *ElfSym::MipsLocalGp;
+Defined *ElfSym::RelaIpltEnd;
static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
switch (Sym.kind()) {
@@ -58,6 +58,7 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
return D.Value;
IS = IS->Repl;
+
uint64_t Offset = D.Value;
// An object in an SHF_MERGE section might be referenced via a
@@ -76,8 +77,6 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
Addend = 0;
}
- const OutputSection *OutSec = IS->getOutputSection();
-
// In the typical case, this is actually very simple and boils
// down to adding together 3 numbers:
// 1. The address of the output section.
@@ -88,7 +87,7 @@ 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 = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset);
+ uint64_t VA = IS->getVA(Offset);
if (D.isTls() && !Config->Relocatable) {
if (!Out::TlsPhdr)
@@ -98,20 +97,12 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
}
return VA;
}
- case Symbol::SharedKind: {
- auto &SS = cast<SharedSymbol>(Sym);
- if (SS.CopyRelSec)
- return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff;
- if (SS.NeedsPltAddr)
- return Sym.getPltVA();
- return 0;
- }
+ case Symbol::SharedKind:
case Symbol::UndefinedKind:
return 0;
case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
- assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
- return 0;
+ llvm_unreachable("lazy symbol reached writer");
}
llvm_unreachable("invalid symbol kind");
}
@@ -134,22 +125,26 @@ uint64_t Symbol::getGotPltVA() const {
}
uint64_t Symbol::getGotPltOffset() const {
- return GotPltIndex * Target->GotPltEntrySize;
+ if (IsInIgot)
+ return PltIndex * Target->GotPltEntrySize;
+ return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize;
}
uint64_t Symbol::getPltVA() const {
if (this->IsInIplt)
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
- return InX::Plt->getVA() + Target->PltHeaderSize +
- PltIndex * Target->PltEntrySize;
+ return InX::Plt->getVA() + Target->getPltEntryOffset(PltIndex);
+}
+
+uint64_t Symbol::getPltOffset() const {
+ assert(!this->IsInIplt);
+ return Target->getPltEntryOffset(PltIndex);
}
uint64_t Symbol::getSize() const {
if (const auto *DR = dyn_cast<Defined>(this))
return DR->Size;
- if (const auto *S = dyn_cast<SharedSymbol>(this))
- return S->Size;
- return 0;
+ return cast<SharedSymbol>(this)->Size;
}
OutputSection *Symbol::getOutputSection() const {
@@ -158,13 +153,6 @@ OutputSection *Symbol::getOutputSection() const {
return Sec->Repl->getOutputSection();
return nullptr;
}
-
- if (auto *S = dyn_cast<SharedSymbol>(this)) {
- if (S->CopyRelSec)
- return S->CopyRelSec->getParent();
- return nullptr;
- }
-
return nullptr;
}
@@ -180,7 +168,7 @@ void Symbol::parseSymbolVersion() {
return;
// Truncate the symbol name so that it doesn't include the version string.
- Name = {S.data(), Pos};
+ NameSize = Pos;
// If this is not in this DSO, it is not a definition.
if (!isDefined())
@@ -206,33 +194,15 @@ void Symbol::parseSymbolVersion() {
// It is an error if the specified version is not defined.
// Usually version script is not provided when linking executable,
// but we may still want to override a versioned symbol from DSO,
- // so we do not report error in this case.
- if (Config->Shared)
+ // 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);
}
-InputFile *Lazy::fetch() {
- if (auto *S = dyn_cast<LazyArchive>(this))
- return S->fetch();
- return cast<LazyObject>(this)->fetch();
-}
-
-ArchiveFile &LazyArchive::getFile() { return *cast<ArchiveFile>(File); }
-
-InputFile *LazyArchive::fetch() {
- std::pair<MemoryBufferRef, uint64_t> MBInfo = getFile().getMember(&Sym);
-
- // getMember returns an empty buffer if the member was already
- // read from the library.
- if (MBInfo.first.getBuffer().empty())
- return nullptr;
- return createObjectFile(MBInfo.first, getFile().getName(), MBInfo.second);
-}
-
-LazyObjFile &LazyObject::getFile() { return *cast<LazyObjFile>(File); }
-
-InputFile *LazyObject::fetch() { return getFile().fetch(); }
+InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }
uint8_t Symbol::computeBinding() const {
if (Config->Relocatable)
@@ -241,7 +211,7 @@ uint8_t Symbol::computeBinding() const {
return STB_LOCAL;
if (VersionId == VER_NDX_LOCAL && isDefined())
return STB_LOCAL;
- if (Config->NoGnuUnique && Binding == STB_GNU_UNIQUE)
+ if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
return Binding;
}
@@ -273,6 +243,27 @@ void elf::printTraceSymbol(Symbol *Sym) {
message(toString(Sym->File) + S + Sym->getName());
}
+void elf::warnUnorderableSymbol(const Symbol *Sym) {
+ if (!Config->WarnSymbolOrdering)
+ 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: ");
+}
+
// Returns a symbol for an error message.
std::string lld::toString(const Symbol &B) {
if (Config->Demangle)
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 9b7207383345..8c9513b9368b 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -7,8 +7,7 @@
//
//===----------------------------------------------------------------------===//
//
-// All symbols are handled as SymbolBodies regardless of their types.
-// This file defines various types of SymbolBodies.
+// This file defines various types of Symbols.
//
//===----------------------------------------------------------------------===//
@@ -16,9 +15,8 @@
#define LLD_ELF_SYMBOLS_H
#include "InputSection.h"
-#include "Strings.h"
-
#include "lld/Common/LLVM.h"
+#include "lld/Common/Strings.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
@@ -34,6 +32,20 @@ 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
+// are not necessary for the linker because they are names of local symbols,
+// 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()) {}
+
+ const char *Data;
+ const uint32_t Size;
+};
+
// The base class for real symbol classes.
class Symbol {
public:
@@ -47,6 +59,25 @@ public:
Kind kind() const { return static_cast<Kind>(SymbolKind); }
+ // The file from which this symbol was created.
+ InputFile *File;
+
+protected:
+ const char *NameData;
+ mutable uint32_t NameSize;
+
+public:
+ uint32_t DynsymIndex = 0;
+ uint32_t GotIndex = -1;
+ uint32_t PltIndex = -1;
+ uint32_t GlobalDynIndex = -1;
+
+ // This field is a index to the symbol's version definition.
+ uint32_t VerdefIndex = -1;
+
+ // Version definition index.
+ uint16_t VersionId;
+
// Symbol binding. This is not overwritten by replaceSymbol to track
// changes during resolution. In particular:
// - An undefined weak is still weak when it resolves to a shared library.
@@ -54,8 +85,11 @@ public:
// remember it is weak.
uint8_t Binding;
- // Version definition index.
- uint16_t VersionId;
+ // The following fields have the same meaning as the ELF symbol attributes.
+ uint8_t Type; // symbol type
+ uint8_t StOther; // st_other field value
+
+ const uint8_t SymbolKind;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
@@ -81,12 +115,6 @@ public:
// True if this symbol is specified by --trace-symbol option.
unsigned Traced : 1;
- // This symbol version was found in a version script.
- unsigned InVersionScript : 1;
-
- // The file from which this symbol was created.
- InputFile *File;
-
bool includeInDynsym() const;
uint8_t computeBinding() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
@@ -100,15 +128,15 @@ public:
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
- // True is this is an undefined weak symbol. This only works once
- // all input files have been added.
- bool isUndefWeak() const {
- // See comment on Lazy the details.
- return isWeak() && (isUndefined() || isLazy());
+ // True if this is an undefined weak symbol.
+ bool isUndefWeak() const { return isWeak() && isUndefined(); }
+
+ StringRef getName() const {
+ if (NameSize == (uint32_t)-1)
+ NameSize = strlen(NameData);
+ return {NameData, NameSize};
}
- StringRef getName() const { return Name; }
- uint8_t getVisibility() const { return StOther & 0x3; }
void parseSymbolVersion();
bool isInGot() const { return GotIndex != -1U; }
@@ -121,34 +149,22 @@ public:
uint64_t getGotPltOffset() const;
uint64_t getGotPltVA() const;
uint64_t getPltVA() const;
+ uint64_t getPltOffset() const;
uint64_t getSize() const;
OutputSection *getOutputSection() const;
- uint32_t DynsymIndex = 0;
- uint32_t GotIndex = -1;
- uint32_t GotPltIndex = -1;
- uint32_t PltIndex = -1;
- uint32_t GlobalDynIndex = -1;
-
protected:
Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
uint8_t StOther, uint8_t Type)
- : Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false),
- IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
- IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections),
- Type(Type), StOther(StOther), Name(Name) {}
-
- const unsigned SymbolKind : 8;
+ : 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) {}
public:
// True the symbol should point to its PLT entry.
// For SharedSymbol only.
unsigned NeedsPltAddr : 1;
- // True if this symbol has an entry in the global part of MIPS GOT.
- unsigned IsInGlobalMipsGot : 1;
-
- // True if this symbol is referenced by 32-bit GOT relocations.
- unsigned Is32BitMipsGot : 1;
// True if this symbol is in the Iplt sub-section of the Plt.
unsigned IsInIplt : 1;
@@ -156,14 +172,15 @@ public:
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
unsigned IsInIgot : 1;
+ // True if this symbol is preemptible at load time.
unsigned IsPreemptible : 1;
// True if an undefined or shared symbol is used from a live section.
unsigned Used : 1;
- // The following fields have the same meaning as the ELF symbol attributes.
- uint8_t Type; // symbol type
- uint8_t StOther; // st_other field value
+ // True if a call to this symbol needs to be followed by a restore of the
+ // PPC64 toc pointer.
+ unsigned NeedsTocRestore : 1;
// The Type field may also have this value. It means that we have not yet seen
// a non-Lazy symbol with this name, so we don't know what its type is. The
@@ -178,9 +195,6 @@ public:
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; }
-
-protected:
- StringRefZ Name;
};
// Represents a symbol that is defined in the current output file.
@@ -214,8 +228,9 @@ public:
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), VerdefIndex(VerdefIndex), Alignment(Alignment) {
+ : Symbol(SharedKind, &File, Name, Binding, StOther, Type),
+ Alignment(Alignment), Value(Value), Size(Size) {
+ 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
@@ -240,54 +255,36 @@ public:
return *cast<SharedFile<ELFT>>(File);
}
- // If not null, there is a copy relocation to this section.
- InputSection *CopyRelSec = nullptr;
+ uint32_t Alignment;
uint64_t Value; // st_value
uint64_t Size; // st_size
-
- // This field is a index to the symbol's version definition.
- uint32_t VerdefIndex;
-
- uint32_t Alignment;
};
-// This represents a symbol that is not yet in the link, but we know where to
-// find it if needed. If the resolver finds both Undefined and Lazy for the same
-// name, it will ask the Lazy to load a file.
+// LazyArchive and LazyObject represent a symbols that is not yet in the link,
+// but we know where to find it if needed. If the resolver finds both Undefined
+// and Lazy for the same name, it will ask the Lazy to load a file.
//
// A special complication is the handling of weak undefined symbols. They should
// not load a file, but we have to remember we have seen both the weak undefined
// and the lazy. We represent that with a lazy symbol with a weak binding. This
// means that code looking for undefined symbols normally also has to take lazy
// symbols into consideration.
-class Lazy : public Symbol {
-public:
- static bool classof(const Symbol *S) { return S->isLazy(); }
-
- // Returns an object file for this symbol, or a nullptr if the file
- // was already returned.
- InputFile *fetch();
-
-protected:
- Lazy(Kind K, InputFile &File, StringRef Name, uint8_t Type)
- : Symbol(K, &File, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT,
- Type) {}
-};
// This class represents a symbol defined in an archive file. It is
// created from an archive file header, and it knows how to load an
// object file from an archive to replace itself with a defined
// symbol.
-class LazyArchive : public Lazy {
+class LazyArchive : public Symbol {
public:
- LazyArchive(InputFile &File, const llvm::object::Archive::Symbol S,
- uint8_t Type)
- : Lazy(LazyArchiveKind, File, S.getName(), Type), Sym(S) {}
+ 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) {}
static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
- ArchiveFile &getFile();
InputFile *fetch();
private:
@@ -296,15 +293,13 @@ private:
// LazyObject symbols represents symbols in object files between
// --start-lib and --end-lib options.
-class LazyObject : public Lazy {
+class LazyObject : public Symbol {
public:
- LazyObject(InputFile &File, StringRef Name, uint8_t Type)
- : Lazy(LazyObjectKind, File, Name, Type) {}
+ LazyObject(InputFile &File, uint8_t Type, StringRef Name)
+ : Symbol(LazyObjectKind, &File, Name, llvm::ELF::STB_GLOBAL,
+ llvm::ELF::STV_DEFAULT, Type) {}
static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
-
- LazyObjFile &getFile();
- InputFile *fetch();
};
// Some linker-generated symbols need to be created as
@@ -334,6 +329,9 @@ struct ElfSym {
static Defined *MipsGp;
static Defined *MipsGpDisp;
static Defined *MipsLocalGp;
+
+ // __rela_iplt_end or __rel_iplt_end
+ static Defined *RelaIpltEnd;
};
// A buffer class that is large enough to hold any Symbol-derived
@@ -351,6 +349,8 @@ void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
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), "SymbolUnion too small");
static_assert(alignof(T) <= alignof(SymbolUnion),
"SymbolUnion not aligned enough");
@@ -367,13 +367,14 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) {
S->ExportDynamic = Sym.ExportDynamic;
S->CanInline = Sym.CanInline;
S->Traced = Sym.Traced;
- S->InVersionScript = Sym.InVersionScript;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
if (S->Traced)
printTraceSymbol(S);
}
+
+void warnUnorderableSymbol(const Symbol *Sym);
} // namespace elf
std::string toString(const elf::Symbol &B);
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index a5e291b79a4d..38859e1650bf 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -20,15 +20,16 @@
#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
+#include "llvm/ADT/SetOperations.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
#include "llvm/Object/Decompressor.h"
@@ -47,27 +48,20 @@ using namespace llvm::dwarf;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
-using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-constexpr size_t MergeNoTailSection::NumShards;
-
-static void write32(void *Buf, uint32_t Val) {
- endian::write32(Buf, Val, Config->Endianness);
-}
+using llvm::support::endian::read32le;
+using llvm::support::endian::write32le;
+using llvm::support::endian::write64le;
-uint64_t SyntheticSection::getVA() const {
- if (OutputSection *Sec = getParent())
- return Sec->Addr + OutSecOff;
- return 0;
-}
+constexpr size_t MergeNoTailSection::NumShards;
// Returns an LLD version string.
static ArrayRef<uint8_t> getVersion() {
// Check LLD_VERSION first for ease of testing.
- // You can get consitent output by using the environment variable.
+ // You can get consistent output by using the environment variable.
// This is only for testing.
StringRef S = getenv("LLD_VERSION");
if (S.empty())
@@ -192,8 +186,6 @@ MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
auto *Opt = reinterpret_cast<const Elf_Mips_Options *>(D.data());
if (Opt->kind == ODK_REGINFO) {
- if (Config->Relocatable && Opt->getRegInfo().ri_gp_value)
- error(Filename + ": unsupported non-zero ri_gp_value");
Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask;
Sec->getFile<ELFT>()->MipsGp0 = Opt->getRegInfo().ri_gp_value;
break;
@@ -244,10 +236,8 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
error(toString(Sec->File) + ": invalid size of .reginfo section");
return nullptr;
}
- auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->Data.data());
- if (Config->Relocatable && R->ri_gp_value)
- error(toString(Sec->File) + ": unsupported non-zero 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;
};
@@ -266,8 +256,8 @@ InputSection *elf::createInterpSection() {
return Sec;
}
-Symbol *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section) {
+Defined *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
+ uint64_t Size, InputSectionBase &Section) {
auto *S = make<Defined>(Section.File, Name, STB_LOCAL, STV_DEFAULT, Type,
Value, Size, &Section);
if (InX::SymTab)
@@ -338,8 +328,6 @@ void BuildIdSection::computeHash(
BssSection::BssSection(StringRef Name, uint64_t Size, uint32_t Alignment)
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, Alignment, Name) {
this->Bss = true;
- if (OutputSection *Sec = getParent())
- Sec->Alignment = std::max(Sec->Alignment, Alignment);
this->Size = Size;
}
@@ -380,15 +368,11 @@ EhFrameSection::EhFrameSection()
// and where their relocations point to.
template <class ELFT, class RelTy>
CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Cie.Sec);
- if (read32(Cie.data().data() + 4, Config->Endianness) != 0)
- fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame");
-
Symbol *Personality = nullptr;
unsigned FirstRelI = Cie.FirstRelocation;
if (FirstRelI != (unsigned)-1)
Personality =
- &Sec->template getFile<ELFT>()->getRelocTargetSym(Rels[FirstRelI]);
+ &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}];
@@ -433,14 +417,14 @@ bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) {
// one and associates FDEs to the CIE.
template <class ELFT, class RelTy>
void EhFrameSection::addSectionAux(EhInputSection *Sec, ArrayRef<RelTy> Rels) {
- DenseMap<size_t, CieRecord *> OffsetToCie;
+ OffsetToCie.clear();
for (EhSectionPiece &Piece : Sec->Pieces) {
// The empty record is the end marker.
if (Piece.Size == 4)
return;
size_t Offset = Piece.InputOff;
- uint32_t ID = read32(Piece.data().data() + 4, Config->Endianness);
+ uint32_t ID = read32(Piece.data().data() + 4);
if (ID == 0) {
OffsetToCie[Offset] = addCie<ELFT>(Piece, Rels);
continue;
@@ -468,10 +452,6 @@ template <class ELFT> void EhFrameSection::addSection(InputSectionBase *C) {
for (auto *DS : Sec->DependentSections)
DependentSections.push_back(DS);
- // .eh_frame is a sequence of CIE or FDE records. This function
- // splits it into pieces so that we can call
- // SplitInputSection::getSectionPiece on the section.
- Sec->split<ELFT>();
if (Sec->Pieces.empty())
return;
@@ -494,9 +474,7 @@ static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) {
}
void EhFrameSection::finalizeContents() {
- if (this->Size)
- return; // Already finalized.
-
+ assert(!this->Size); // Not finalized.
size_t Off = 0;
for (CieRecord *Rec : CieRecords) {
Rec->Cie->OutputOff = Off;
@@ -509,10 +487,10 @@ void EhFrameSection::finalizeContents() {
}
// The LSB standard does not allow a .eh_frame section with zero
- // Call Frame Information records. Therefore add a CIE record length
- // 0 as a terminator if this .eh_frame section is empty.
- if (Off == 0)
- Off = 4;
+ // 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;
this->Size = Off;
}
@@ -524,25 +502,47 @@ std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const {
uint8_t *Buf = getParent()->Loc + OutSecOff;
std::vector<FdeData> Ret;
+ uint64_t VA = InX::EhFrameHdr->getVA();
for (CieRecord *Rec : CieRecords) {
uint8_t Enc = getFdeEncoding(Rec->Cie);
for (EhSectionPiece *Fde : Rec->Fdes) {
- uint32_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
- uint32_t FdeVA = getParent()->Addr + Fde->OutputOff;
- Ret.push_back({Pc, FdeVA});
+ 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;
+ };
+ std::stable_sort(Ret.begin(), Ret.end(), 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());
+
return Ret;
}
static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
switch (Size) {
case DW_EH_PE_udata2:
- return read16(Buf, Config->Endianness);
+ return read16(Buf);
+ case DW_EH_PE_sdata2:
+ return (int16_t)read16(Buf);
case DW_EH_PE_udata4:
- return read32(Buf, Config->Endianness);
+ return read32(Buf);
+ case DW_EH_PE_sdata4:
+ return (int32_t)read32(Buf);
case DW_EH_PE_udata8:
- return read64(Buf, Config->Endianness);
+ case DW_EH_PE_sdata8:
+ return read64(Buf);
case DW_EH_PE_absptr:
return readUint(Buf);
}
@@ -556,7 +556,7 @@ uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff,
// 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 & 0x7);
+ uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0xf);
if ((Enc & 0x70) == DW_EH_PE_absptr)
return Addr;
if ((Enc & 0x70) == DW_EH_PE_pcrel)
@@ -589,7 +589,15 @@ void EhFrameSection::writeTo(uint8_t *Buf) {
GotSection::GotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotEntrySize, ".got") {}
+ 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;
+}
void GotSection::addEntry(Symbol &Sym) {
Sym.GotIndex = NumEntries;
@@ -623,196 +631,383 @@ uint64_t GotSection::getGlobalDynOffset(const Symbol &B) const {
return B.GlobalDynIndex * Config->Wordsize;
}
-void GotSection::finalizeContents() { Size = NumEntries * Config->Wordsize; }
+void GotSection::finalizeContents() {
+ Size = NumEntries * Config->Wordsize;
+}
bool GotSection::empty() 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_).
- return NumEntries == 0 && !HasGotOffRel && !ElfSym::GlobalOffsetTable;
+ // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got.
+ return NumEntries == 0 && !HasGotOffRel &&
+ !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt);
}
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);
}
+static uint64_t getMipsPageAddr(uint64_t Addr) {
+ return (Addr + 0x8000) & ~0xffff;
+}
+
+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(Symbol &Sym, int64_t Addend, RelExpr Expr) {
- // For "true" local symbols which can be referenced from the same module
- // only compiler creates two instructions for address loading:
- //
- // lw $8, 0($gp) # R_MIPS_GOT16
- // addi $8, $8, 0 # R_MIPS_LO16
- //
- // The first instruction loads high 16 bits of the symbol address while
- // the second adds an offset. That allows to reduce number of required
- // GOT entries because only one global offset table entry is necessary
- // for every 64 KBytes of local data. So for local symbols we need to
- // allocate number of GOT entries to hold all required "page" addresses.
- //
- // All global symbols (hidden and regular) considered by compiler uniformly.
- // It always generates a single `lw` instruction and R_MIPS_GOT16 relocation
- // to load address of the symbol. So for each such symbol we need to
- // allocate dedicated GOT entry to store its address.
- //
- // If a symbol is preemptible we need help of dynamic linker to get its
- // final address. The corresponding GOT entries are allocated in the
- // "global" part of GOT. Entries for non preemptible global symbol allocated
- // in the "local" part of GOT.
- //
- // See "Global Offset Table" in Chapter 5:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+void MipsGotSection::addEntry(InputFile &File, Symbol &Sym, int64_t Addend,
+ RelExpr Expr) {
+ FileGot &G = getGot(File);
if (Expr == R_MIPS_GOT_LOCAL_PAGE) {
- // At this point we do not know final symbol value so to reduce number
- // of allocated GOT entries do the following trick. Save all output
- // sections referenced by GOT relocations. Then later in the `finalize`
- // method calculate number of "pages" required to cover all saved output
- // section and allocate appropriate number of GOT entries.
- PageIndexMap.insert({Sym.getOutputSection(), 0});
- return;
- }
- if (Sym.isTls()) {
- // GOT entries created for MIPS TLS relocations behave like
- // almost GOT entries from other ABIs. They go to the end
- // of the global offset table.
- Sym.GotIndex = TlsEntries.size();
- TlsEntries.push_back(&Sym);
- return;
- }
- auto AddEntry = [&](Symbol &S, uint64_t A, GotEntries &Items) {
- if (S.isInGot() && !A)
- return;
- size_t NewIndex = Items.size();
- if (!EntryIndexMap.insert({{&S, A}, NewIndex}).second)
- return;
- Items.emplace_back(&S, A);
- if (!A)
- S.GotIndex = NewIndex;
- };
- if (Sym.IsPreemptible) {
- // Ignore addends for preemptible symbols. They got single GOT entry anyway.
- AddEntry(Sym, 0, GlobalEntries);
- Sym.IsInGlobalMipsGot = true;
- } else if (Expr == R_MIPS_GOT_OFF32) {
- AddEntry(Sym, Addend, LocalEntries32);
- Sym.Is32BitMipsGot = true;
- } else {
- // Hold local GOT entries accessed via a 16-bit index separately.
- // That allows to write them in the beginning of the GOT and keep
- // their indexes as less as possible to escape relocation's overflow.
- AddEntry(Sym, Addend, LocalEntries);
- }
+ 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});
+ else
+ G.Local16.insert({{&Sym, Addend}, 0});
}
-bool MipsGotSection::addDynTlsEntry(Symbol &Sym) {
- if (Sym.GlobalDynIndex != -1U)
- return false;
- Sym.GlobalDynIndex = TlsEntries.size();
- // Global Dynamic TLS entries take two GOT slots.
- TlsEntries.push_back(nullptr);
- TlsEntries.push_back(&Sym);
- return true;
+void MipsGotSection::addDynTlsEntry(InputFile &File, Symbol &Sym) {
+ getGot(File).DynTlsSymbols.insert({&Sym, 0});
}
-// Reserves TLS entries for a TLS module ID and a TLS block offset.
-// In total it takes two GOT slots.
-bool MipsGotSection::addTlsIndex() {
- if (TlsIndexOff != uint32_t(-1))
- return false;
- TlsIndexOff = TlsEntries.size() * Config->Wordsize;
- TlsEntries.push_back(nullptr);
- TlsEntries.push_back(nullptr);
- return true;
+void MipsGotSection::addTlsIndex(InputFile &File) {
+ getGot(File).DynTlsSymbols.insert({nullptr, 0});
}
-static uint64_t getMipsPageAddr(uint64_t Addr) {
- return (Addr + 0x8000) & ~0xffff;
+size_t MipsGotSection::FileGot::getEntriesNum() const {
+ return getPageEntriesNum() + Local16.size() + Global.size() + Relocs.size() +
+ Tls.size() + DynTlsSymbols.size() * 2;
}
-static uint64_t getMipsPageCount(uint64_t Size) {
- return (Size + 0xfffe) / 0xffff + 1;
+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;
}
-uint64_t MipsGotSection::getPageEntryOffset(const Symbol &B,
- int64_t Addend) const {
- const OutputSection *OutSec = B.getOutputSection();
- uint64_t SecAddr = getMipsPageAddr(OutSec->Addr);
- uint64_t SymAddr = getMipsPageAddr(B.getVA(Addend));
- uint64_t Index = PageIndexMap.lookup(OutSec) + (SymAddr - SecAddr) / 0xffff;
- assert(Index < PageEntriesNum);
- return (HeaderEntriesNum + Index) * Config->Wordsize;
+size_t MipsGotSection::FileGot::getIndexedEntriesNum() const {
+ 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;
}
-uint64_t MipsGotSection::getSymEntryOffset(const Symbol &B,
- int64_t Addend) const {
- // Calculate offset of the GOT entries block: TLS, global, local.
- uint64_t Index = HeaderEntriesNum + PageEntriesNum;
- if (B.isTls())
- Index += LocalEntries.size() + LocalEntries32.size() + GlobalEntries.size();
- else if (B.IsInGlobalMipsGot)
- Index += LocalEntries.size() + LocalEntries32.size();
- else if (B.Is32BitMipsGot)
- Index += LocalEntries.size();
- // Calculate offset of the GOT entry in the block.
- if (B.isInGot())
- Index += B.GotIndex;
- else {
- auto It = EntryIndexMap.find({&B, Addend});
- assert(It != EntryIndexMap.end());
- Index += It->second;
+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];
+}
+
+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))});
}
return Index * Config->Wordsize;
}
-uint64_t MipsGotSection::getTlsOffset() const {
- return (getLocalEntriesNum() + GlobalEntries.size()) * 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::getGlobalDynOffset(const Symbol &B) const {
- return B.GlobalDynIndex * 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;
}
const Symbol *MipsGotSection::getFirstGlobalEntry() const {
- return GlobalEntries.empty() ? nullptr : GlobalEntries.front().first;
+ 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;
+ return nullptr;
}
unsigned MipsGotSection::getLocalEntriesNum() const {
- return HeaderEntriesNum + PageEntriesNum + LocalEntries.size() +
- LocalEntries32.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);
+
+ size_t Count = IsPrimary ? HeaderEntriesNum : 0;
+ Count += Tmp.getIndexedEntriesNum();
+
+ if (Count * Config->Wordsize > Config->MipsGotSize)
+ return false;
+
+ std::swap(Tmp, Dst);
+ return true;
}
void MipsGotSection::finalizeContents() { updateAllocSize(); }
bool MipsGotSection::updateAllocSize() {
- PageEntriesNum = 0;
- for (std::pair<const OutputSection *, size_t> &P : PageIndexMap) {
- // For each output section referenced by GOT page relocations calculate
- // and save into PageIndexMap 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 = PageEntriesNum;
- PageEntriesNum += getMipsPageCount(P.first->Size);
- }
- Size = (getLocalEntriesNum() + GlobalEntries.size() + TlsEntries.size()) *
- 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())
+ return;
+
+ 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 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);
+ });
+ 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();
+ }
+
+ // 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();
+ }
+ }
+ P.second.Count = getMipsPageCount(SecSize);
+ }
+ }
+
+ // Merge GOTs. Try to join as much as possible GOTs but do not exceed
+ // maximum GOT size. At first, try to fill the primary GOT because
+ // 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;
+ } 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
+ // 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);
+ }
+ File->MipsGotIndex = MergedGots.size() - 1;
+ }
+ }
+ 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);
+ });
+
+ // 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) {
+ // For each output section referenced by GOT page relocations calculate
+ // 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;
+ }
+ 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
+ // 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;
+
+ // Create dynamic relocations.
+ 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)
+ InX::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)
+ continue;
+ InX::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
+ // thread-locals that have been marked as local through a linker script)
+ if (!S->IsPreemptible && !Config->Pic)
+ continue;
+ InX::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)
+ continue;
+ Offset += Config->Wordsize;
+ InX::RelaDyn->addReloc(Target->TlsOffsetRel, this, Offset, S);
+ }
+ }
+
+ // Do not create dynamic relocations for non-TLS
+ // entries in the primary GOT.
+ 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;
+ InX::RelaDyn->addReloc(Target->RelativeRel, this, Offset, P.first);
+ }
+ if (!Config->Pic)
+ 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;
+ InX::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;
+ InX::RelaDyn->addReloc({Target->RelativeRel, this, Offset, true,
+ P.first.first, P.first.second});
+ }
+ }
+}
+
bool MipsGotSection::empty() 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;
}
-uint64_t MipsGotSection::getGp() const { return ElfSym::MipsGp->getVA(0); }
+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 +
+ 0x7ff0;
+}
void MipsGotSection::writeTo(uint8_t *Buf) {
// Set the MSB of the second GOT slot. This is not required by any
@@ -830,59 +1025,67 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
// 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));
- Buf += HeaderEntriesNum * Config->Wordsize;
- // Write 'page address' entries to the local part of the GOT.
- for (std::pair<const OutputSection *, size_t> &L : PageIndexMap) {
- size_t PageCount = getMipsPageCount(L.first->Size);
- uint64_t FirstPageAddr = getMipsPageAddr(L.first->Addr);
- for (size_t PI = 0; PI < PageCount; ++PI) {
- uint8_t *Entry = Buf + (L.second + PI) * Config->Wordsize;
- writeUint(Entry, FirstPageAddr + PI * 0x10000);
- }
- }
- Buf += PageEntriesNum * Config->Wordsize;
- auto AddEntry = [&](const GotEntry &SA) {
- uint8_t *Entry = Buf;
- Buf += Config->Wordsize;
- const Symbol *Sym = SA.first;
- uint64_t VA = Sym->getVA(SA.second);
- if (Sym->StOther & STO_MIPS_MICROMIPS)
- VA |= 1;
- writeUint(Entry, VA);
- };
- std::for_each(std::begin(LocalEntries), std::end(LocalEntries), AddEntry);
- std::for_each(std::begin(LocalEntries32), std::end(LocalEntries32), AddEntry);
- std::for_each(std::begin(GlobalEntries), std::end(GlobalEntries), AddEntry);
- // Initialize TLS-related GOT entries. If the 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
- if (TlsIndexOff != -1U && !Config->Pic)
- writeUint(Buf + TlsIndexOff, 1);
- for (const Symbol *B : TlsEntries) {
- if (!B || B->IsPreemptible)
- continue;
- uint64_t VA = B->getVA();
- if (B->GotIndex != -1U) {
- uint8_t *Entry = Buf + B->GotIndex * Config->Wordsize;
- writeUint(Entry, VA - 0x7000);
+ 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);
+ };
+ // 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);
}
- if (B->GlobalDynIndex != -1U) {
- uint8_t *Entry = Buf + B->GlobalDynIndex * Config->Wordsize;
- writeUint(Entry, 1);
- Entry += Config->Wordsize;
- writeUint(Entry, VA - 0x8000);
+ // 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);
+ // 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 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);
+ }
}
}
}
+// On PowerPC the .plt section is used to hold the table of function addresses
+// instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss
+// 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, SHT_PROGBITS,
- Target->GotPltEntrySize, ".got.plt") {}
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE,
+ Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
+ Target->GotPltEntrySize,
+ Config->EMachine == EM_PPC64 ? ".plt" : ".got.plt") {}
void GotPltSection::addEntry(Symbol &Sym) {
- Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size();
+ assert(Sym.PltIndex == Entries.size());
Entries.push_back(&Sym);
}
@@ -900,16 +1103,37 @@ void GotPltSection::writeTo(uint8_t *Buf) {
}
}
-// On ARM the IgotPltSection is part of the GotSection, on other Targets it is
-// part of the .got.plt
+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);
+}
+
+static StringRef getIgotPltName() {
+ // On ARM the IgotPltSection is part of the GotSection.
+ 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)
+ return ".plt";
+
+ return ".got.plt";
+}
+
+// On PowerPC64 the GotPltSection type is SHT_NOBITS so we have to follow suit
+// with the IgotPltSection.
IgotPltSection::IgotPltSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotPltEntrySize,
- Config->EMachine == EM_ARM ? ".got" : ".got.plt") {}
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE,
+ Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
+ Target->GotPltEntrySize, getIgotPltName()) {}
void IgotPltSection::addEntry(Symbol &Sym) {
Sym.IsInIgot = true;
- Sym.GotPltIndex = Entries.size();
+ assert(Sym.PltIndex == Entries.size());
Entries.push_back(&Sym);
}
@@ -1005,8 +1229,14 @@ void DynamicSection<ELFT>::addInt(int32_t Tag, uint64_t Val) {
template <class ELFT>
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->getParent()->Addr + Sec->OutSecOff; }});
+ {Tag, [=] { return Sec->getVA(0) - (getVA() + TagOffset); }});
}
template <class ELFT>
@@ -1034,6 +1264,8 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
uint32_t DtFlags1 = 0;
if (Config->Bsymbolic)
DtFlags |= DF_SYMBOLIC;
+ if (Config->ZInitfirst)
+ DtFlags1 |= DF_1_INITFIRST;
if (Config->ZNodelete)
DtFlags1 |= DF_1_NODELETE;
if (Config->ZNodlopen)
@@ -1046,6 +1278,8 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
DtFlags |= DF_ORIGIN;
DtFlags1 |= DF_1_ORIGIN;
}
+ if (!Config->ZText)
+ DtFlags |= DF_TEXTREL;
if (DtFlags)
addInt(DT_FLAGS, DtFlags);
@@ -1064,7 +1298,7 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
addInt(DT_DEBUG, 0);
this->Link = InX::DynStrTab->getParent()->SectionIndex;
- if (InX::RelaDyn->getParent() && !InX::RelaDyn->empty()) {
+ if (!InX::RelaDyn->empty()) {
addInSec(InX::RelaDyn->DynamicTag, InX::RelaDyn);
addSize(InX::RelaDyn->SizeDynamicTag, InX::RelaDyn->getParent());
@@ -1081,7 +1315,21 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels);
}
}
- if (InX::RelaPlt->getParent() && !InX::RelaPlt->empty()) {
+ if (InX::RelrDyn && !InX::RelrDyn->Relocs.empty()) {
+ addInSec(Config->UseAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
+ InX::RelrDyn);
+ addSize(Config->UseAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
+ InX::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
+ // .rel[a].plt section.
+ if (InX::RelaPlt->getParent()->Live) {
addInSec(DT_JMPREL, InX::RelaPlt);
addSize(DT_PLTRELSZ, InX::RelaPlt->getParent());
switch (Config->EMachine) {
@@ -1154,8 +1402,23 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
else
addInt(DT_MIPS_GOTSYM, InX::DynSymTab->getNumSymbols());
addInSec(DT_PLTGOT, InX::MipsGot);
- if (InX::MipsRldMap)
- addInSec(DT_MIPS_RLD_MAP, InX::MipsRldMap);
+ if (InX::MipsRldMap) {
+ if (!Config->Pie)
+ addInSec(DT_MIPS_RLD_MAP, InX::MipsRldMap);
+ // Store the offset to the .rld_map section
+ // relative to the address of the tag.
+ addInSecRelative(DT_MIPS_RLD_MAP_REL, InX::MipsRldMap);
+ }
+ }
+
+ // Glink dynamic tag is required by the V2 abi if the plt section isn't empty.
+ if (Config->EMachine == EM_PPC64 && !InX::Plt->empty()) {
+ // 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 InX::Plt->getVA(0) + Offset;
+ }});
}
addInt(DT_NULL, 0);
@@ -1175,13 +1438,16 @@ template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
}
uint64_t DynamicReloc::getOffset() const {
- return InputSec->getOutputSection()->Addr + InputSec->getOffset(OffsetInSec);
+ return InputSec->getVA(OffsetInSec);
}
-int64_t DynamicReloc::getAddend() const {
+int64_t DynamicReloc::computeAddend() const {
if (UseSymVA)
return Sym->getVA(Addend);
- return Addend;
+ if (!OutputSec)
+ return Addend;
+ // See the comment in the DynamicReloc ctor.
+ return getMipsPageAddr(OutputSec->Addr) + Addend;
}
uint32_t DynamicReloc::getSymIndex() const {
@@ -1196,6 +1462,23 @@ RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type,
: 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 *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});
+}
+
void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) {
if (Reloc.Type == Target->RelativeRel)
++NumRelativeRelocs;
@@ -1212,23 +1495,17 @@ void RelocationBaseSection::finalizeContents() {
getParent()->Link = Link;
}
+RelrBaseSection::RelrBaseSection()
+ : SyntheticSection(SHF_ALLOC,
+ 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.getAddend();
+ P->r_addend = Rel.computeAddend();
P->r_offset = Rel.getOffset();
- if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot)
- // The MIPS GOT section contains dynamic relocations that correspond to TLS
- // entries. These entries are placed after the global and local sections of
- // the GOT. At the point when we create these relocations, the size of the
- // global and local sections is unknown, so the offset that we store in the
- // TLS entry's DynamicReloc is relative to the start of the TLS section of
- // the GOT, rather than being relative to the start of the GOT. This line of
- // code adds the size of the global and local sections to the virtual
- // address computed by getOffset() in order to adjust it into the TLS
- // section.
- P->r_offset += InX::MipsGot->getTlsOffset();
P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
}
@@ -1241,32 +1518,22 @@ RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
-template <class ELFT, class RelTy>
-static bool compRelocations(const RelTy &A, const RelTy &B) {
- bool AIsRel = A.getType(Config->IsMips64EL) == Target->RelativeRel;
- bool BIsRel = B.getType(Config->IsMips64EL) == Target->RelativeRel;
+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.getSymbol(Config->IsMips64EL) < B.getSymbol(Config->IsMips64EL);
+ return A.getSymIndex() < B.getSymIndex();
}
template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
- uint8_t *BufBegin = Buf;
+ if (Sort)
+ std::stable_sort(Relocs.begin(), Relocs.end(), compRelocations);
+
for (const DynamicReloc &Rel : Relocs) {
encodeDynamicReloc<ELFT>(reinterpret_cast<Elf_Rela *>(Buf), Rel);
Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
-
- if (Sort) {
- if (Config->IsRela)
- std::stable_sort((Elf_Rela *)BufBegin,
- (Elf_Rela *)BufBegin + Relocs.size(),
- compRelocations<ELFT, Elf_Rela>);
- else
- std::stable_sort((Elf_Rel *)BufBegin, (Elf_Rel *)BufBegin + Relocs.size(),
- compRelocations<ELFT, Elf_Rel>);
- }
}
template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
@@ -1354,10 +1621,10 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
NonRelatives.push_back(R);
}
- std::sort(Relatives.begin(), Relatives.end(),
- [](const Elf_Rel &A, const Elf_Rel &B) {
- return A.r_offset < B.r_offset;
- });
+ llvm::sort(Relatives.begin(), Relatives.end(),
+ [](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
// apart from one another. These generally correspond to vtable entries. The
@@ -1435,10 +1702,10 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
}
// Finally the non-relative relocations.
- std::sort(NonRelatives.begin(), NonRelatives.end(),
- [](const Elf_Rela &A, const Elf_Rela &B) {
- return A.r_offset < B.r_offset;
- });
+ llvm::sort(NonRelatives.begin(), NonRelatives.end(),
+ [](const Elf_Rela &A, const Elf_Rela &B) {
+ return A.r_offset < B.r_offset;
+ });
if (!NonRelatives.empty()) {
Add(NonRelatives.size());
Add(HasAddendIfRela);
@@ -1461,6 +1728,97 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
return RelocData.size() != OldSize;
}
+template <class ELFT> RelrSection<ELFT>::RelrSection() {
+ this->Entsize = Config->Wordsize;
+}
+
+template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
+ // This function computes the contents of an SHT_RELR packed relocation
+ // section.
+ //
+ // Proposal for adding SHT_RELR sections to generic-abi is here:
+ // https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
+ //
+ // The encoded sequence of Elf64_Relr entries in a SHT_RELR section looks
+ // like [ AAAAAAAA BBBBBBB1 BBBBBBB1 ... AAAAAAAA BBBBBB1 ... ]
+ //
+ // i.e. start with an address, followed by any number of bitmaps. The address
+ // entry encodes 1 relocation. The subsequent bitmap entries encode up to 63
+ // relocations each, at subsequent offsets following the last address entry.
+ //
+ // The bitmap entries must have 1 in the least significant bit. The assumption
+ // here is that an address cannot have 1 in lsb. Odd addresses are not
+ // supported.
+ //
+ // Excluding the least significant bit in the bitmap, each non-zero bit in
+ // the bitmap represents a relocation to be applied to a corresponding machine
+ // word that follows the base address word. The second least significant bit
+ // represents the machine word immediately following the initial address, and
+ // each bit that follows represents the next word, in linear order. As such,
+ // a single bitmap can encode up to 31 relocations in a 32-bit object, and
+ // 63 relocations in a 64-bit object.
+ //
+ // This encoding has a couple of interesting properties:
+ // 1. Looking at any entry, it is clear whether it's an address or a bitmap:
+ // even means address, odd means bitmap.
+ // 2. Just a simple list of addresses is a valid encoding.
+
+ 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);
+
+ // Number of bits to use for the relocation offsets bitmap.
+ // Must be either 63 or 31.
+ 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());
+
+ // 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;) {
+ // Add a leading relocation.
+ 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 Delta = Offsets[I] - Base;
+
+ // If it is too far, it cannot be folded.
+ if (Delta >= NBits * Wordsize)
+ break;
+
+ // If it is not a multiple of wordsize away, it cannot be folded.
+ if (Delta % Wordsize)
+ break;
+
+ // Fold it.
+ Bitmap |= 1ULL << (Delta / Wordsize);
+ ++I;
+ }
+
+ if (!Bitmap)
+ break;
+
+ RelrRelocs.push_back(Elf_Relr((Bitmap << 1) | 1));
+ Base += NBits * Wordsize;
+ }
+ }
+
+ return RelrRelocs.size() != OldSize;
+}
+
SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec)
: SyntheticSection(StrTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0,
StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
@@ -1476,50 +1834,70 @@ SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec)
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 first part of GOT in arbitrary order.
- bool LIsInLocalGot = !L.Sym->IsInGlobalMipsGot;
- bool RIsInLocalGot = !R.Sym->IsInGlobalMipsGot;
- if (LIsInLocalGot || RIsInLocalGot)
- return !RIsInLocalGot;
- return L.Sym->GotIndex < R.Sym->GotIndex;
+ // 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())
+ return false;
+ return !L.Sym->isInGot();
}
void SymbolTableBaseSection::finalizeContents() {
getParent()->Link = StrTabSec.getParent()->SectionIndex;
+ if (this->Type != SHT_DYNSYM)
+ return;
+
// If it is a .dynsym, there should be no local symbols, but we need
// to do a few things for the dynamic linker.
- if (this->Type == SHT_DYNSYM) {
- // 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;
-
- if (InX::GnuHashTab) {
- // NB: It also sorts Symbols to meet the GNU hash table requirements.
- InX::GnuHashTab->addSymbols(Symbols);
- } else if (Config->EMachine == EM_MIPS) {
- std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols);
- }
- size_t I = 0;
- for (const SymbolTableEntry &S : Symbols) S.Sym->DynsymIndex = ++I;
- return;
+ // 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;
+
+ if (InX::GnuHashTab) {
+ // NB: It also sorts Symbols to meet the GNU hash table requirements.
+ InX::GnuHashTab->addSymbols(Symbols);
+ } else if (Config->EMachine == EM_MIPS) {
+ std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols);
}
+
+ 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
// sort symbol entries in this function. (For .dynsym, we don't do that because
// symbols for dynamic linking are inherently all globals.)
+//
+// Aside from above, we put local symbols in groups starting with the STT_FILE
+// symbol. That is convenient for purpose of identifying where are local symbols
+// coming from.
void SymbolTableBaseSection::postThunkContents() {
- if (this->Type == SHT_DYNSYM)
- return;
- // move all local symbols before global symbols.
- auto It = std::stable_partition(
+ assert(this->Type == SHT_SYMTAB);
+
+ // 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;
});
- size_t NumLocals = It - Symbols.begin();
+ 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);
+
+ 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) {
@@ -1586,6 +1964,8 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
CommonSec = dyn_cast_or_null<BssSection>(D->Section);
if (CommonSec)
ESym->st_shndx = SHN_COMMON;
+ else if (Sym->NeedsPltAddr)
+ ESym->st_shndx = SHN_UNDEF;
else if (const OutputSection *OutSec = Sym->getOutputSection())
ESym->st_shndx = OutSec->SectionIndex;
else if (isa<Defined>(Sym))
@@ -1627,9 +2007,11 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
ESym->st_other |= STO_MIPS_PLT;
if (isMicroMips()) {
// Set STO_MIPS_MICROMIPS flag and less-significant bit for
- // defined microMIPS symbols and shared symbols with PLT record.
- if ((Sym->isDefined() && (Sym->StOther & STO_MIPS_MICROMIPS)) ||
- (Sym->isShared() && Sym->NeedsPltAddr)) {
+ // 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;
@@ -1682,12 +2064,14 @@ GnuHashTableSection::GnuHashTableSection()
void GnuHashTableSection::finalizeContents() {
getParent()->Link = InX::DynSymTab->getParent()->SectionIndex;
- // Computes bloom filter size in word size. We want to allocate 8
+ // 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())
+ if (Symbols.empty()) {
MaskWords = 1;
- else
- MaskWords = NextPowerOf2((Symbols.size() - 1) / Config->Wordsize);
+ } else {
+ uint64_t NumBits = Symbols.size() * 12;
+ MaskWords = NextPowerOf2(NumBits / (Config->Wordsize * 8));
+ }
Size = 16; // Header
Size += Config->Wordsize * MaskWords; // Bloom filter
@@ -1705,7 +2089,7 @@ void GnuHashTableSection::writeTo(uint8_t *Buf) {
write32(Buf, NBuckets);
write32(Buf + 4, InX::DynSymTab->getNumSymbols() - Symbols.size());
write32(Buf + 8, MaskWords);
- write32(Buf + 12, getShift2());
+ write32(Buf + 12, Shift2);
Buf += 16;
// Write a bloom filter and a hash table.
@@ -1722,12 +2106,12 @@ 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) {
- const unsigned C = Config->Wordsize * 8;
+ unsigned C = Config->Is64 ? 64 : 32;
for (const Entry &Sym : Symbols) {
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 >> getShift2()) % C);
+ Val |= uint64_t(1) << ((Sym.Hash >> Shift2) % C);
writeUint(Buf + I * Config->Wordsize, Val);
}
}
@@ -1769,21 +2153,23 @@ void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
// its type correctly.
std::vector<SymbolTableEntry>::iterator Mid =
std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) {
- // Shared symbols that this executable preempts are special. The dynamic
- // linker has to look them up, so they have to be in the hash table.
- if (auto *SS = dyn_cast<SharedSymbol>(S.Sym))
- return SS->CopyRelSec == nullptr && !SS->NeedsPltAddr;
return !S.Sym->isDefined();
});
- if (Mid == V.end())
- return;
// We chose load factor 4 for the on-disk hash table. For each hash
// collision, the dynamic linker will compare a uint32_t hash value.
- // Since the integer comparison is quite fast, we believe we can make
- // the load factor even larger. 4 is just a conservative choice.
+ // Since the integer comparison is quite fast, we believe we can
+ // make the load factor even larger. 4 is just a conservative choice.
+ //
+ // Note that we don't want to create a zero-sized hash table because
+ // 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);
+ if (Mid == V.end())
+ return;
+
for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) {
Symbol *B = Ent.Sym;
uint32_t Hash = hashGnu(B->getName());
@@ -1817,6 +2203,9 @@ void HashTableSection::finalizeContents() {
}
void HashTableSection::writeTo(uint8_t *Buf) {
+ // See comment in GnuHashTableSection::writeTo.
+ memset(Buf, 0, Size);
+
unsigned NumSymbols = InX::DynSymTab->getNumSymbols();
uint32_t *P = reinterpret_cast<uint32_t *>(Buf);
@@ -1836,9 +2225,12 @@ void HashTableSection::writeTo(uint8_t *Buf) {
}
}
-PltSection::PltSection(size_t S)
- : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt"),
- HeaderSize(S) {
+// 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 ? 0 : Target->PltHeaderSize), 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)
@@ -1848,7 +2240,7 @@ PltSection::PltSection(size_t S)
void PltSection::writeTo(uint8_t *Buf) {
// At beginning of PLT but not the IPLT, we have code to call the dynamic
// linker to resolve dynsyms at runtime. Write such code.
- if (HeaderSize != 0)
+ if (!IsIplt)
Target->writePltHeader(Buf);
size_t Off = HeaderSize;
// The IPlt is immediately after the Plt, account for this in RelOff
@@ -1867,7 +2259,7 @@ void PltSection::writeTo(uint8_t *Buf) {
template <class ELFT> void PltSection::addEntry(Symbol &Sym) {
Sym.PltIndex = Entries.size();
RelocationBaseSection *PltRelocSection = InX::RelaPlt;
- if (HeaderSize == 0) {
+ if (IsIplt) {
PltRelocSection = InX::RelaIplt;
Sym.IsInIplt = true;
}
@@ -1884,7 +2276,7 @@ size_t PltSection::getSize() const {
// 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 (HeaderSize != 0)
+ if (!IsIplt)
Target->addPltHeaderSymbols(*this);
size_t Off = HeaderSize;
for (size_t I = 0; I < Entries.size(); ++I) {
@@ -1894,7 +2286,7 @@ void PltSection::addSymbols() {
}
unsigned PltSection::getPltRelocOff() const {
- return (HeaderSize == 0) ? InX::Plt->getSize() : 0;
+ return IsIplt ? InX::Plt->getSize() : 0;
}
// The string hash function for .gdb_index.
@@ -1905,16 +2297,48 @@ static uint32_t computeGdbHash(StringRef S) {
return H;
}
-static std::vector<GdbIndexChunk::CuEntry> readCuList(DWARFContext &Dwarf) {
- std::vector<GdbIndexChunk::CuEntry> Ret;
+GdbIndexSection::GdbIndexSection()
+ : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index") {}
+
+// 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);
+}
+
+// Compute the output section size.
+void GdbIndexSection::initOutputSize() {
+ Size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8;
+
+ 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;
+ }
+}
+
+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;
+}
+
+static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &Dwarf) {
+ std::vector<GdbIndexSection::CuEntry> Ret;
for (std::unique_ptr<DWARFCompileUnit> &Cu : Dwarf.compile_units())
Ret.push_back({Cu->getOffset(), Cu->getLength() + 4});
return Ret;
}
-static std::vector<GdbIndexChunk::AddressEntry>
+static std::vector<GdbIndexSection::AddressEntry>
readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) {
- std::vector<GdbIndexChunk::AddressEntry> Ret;
+ std::vector<GdbIndexSection::AddressEntry> Ret;
uint32_t CuIdx = 0;
for (std::unique_ptr<DWARFCompileUnit> &Cu : Dwarf.compile_units()) {
@@ -1938,218 +2362,192 @@ readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) {
return Ret;
}
-static std::vector<GdbIndexChunk::NameTypeEntry>
-readPubNamesAndTypes(DWARFContext &Dwarf) {
+static std::vector<GdbIndexSection::NameTypeEntry>
+readPubNamesAndTypes(DWARFContext &Dwarf, uint32_t Idx) {
StringRef Sec1 = Dwarf.getDWARFObj().getGnuPubNamesSection();
StringRef Sec2 = Dwarf.getDWARFObj().getGnuPubTypesSection();
- std::vector<GdbIndexChunk::NameTypeEntry> Ret;
+ std::vector<GdbIndexSection::NameTypeEntry> Ret;
for (StringRef Sec : {Sec1, Sec2}) {
DWARFDebugPubTable Table(Sec, Config->IsLE, true);
- for (const DWARFDebugPubTable::Set &Set : Table.getData()) {
- for (const DWARFDebugPubTable::Entry &Ent : Set.Entries) {
- CachedHashStringRef S(Ent.Name, computeGdbHash(Ent.Name));
- Ret.push_back({S, Ent.Descriptor.toBits()});
- }
- }
+ for (const DWARFDebugPubTable::Set &Set : Table.getData())
+ for (const DWARFDebugPubTable::Entry &Ent : Set.Entries)
+ Ret.push_back({{Ent.Name, computeGdbHash(Ent.Name)},
+ (Ent.Descriptor.toBits() << 24) | Idx});
}
return Ret;
}
-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;
-}
+// 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::NameTypeEntry>> NameTypes) {
+ typedef GdbIndexSection::GdbSymbol GdbSymbol;
+ typedef GdbIndexSection::NameTypeEntry NameTypeEntry;
-void GdbIndexSection::fixCuIndex() {
- uint32_t Idx = 0;
- for (GdbIndexChunk &Chunk : Chunks) {
- for (GdbIndexChunk::AddressEntry &Ent : Chunk.AddressAreas)
- Ent.CuIndex += Idx;
- Idx += Chunk.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);
-std::vector<std::vector<uint32_t>> GdbIndexSection::createCuVectors() {
- std::vector<std::vector<uint32_t>> Ret;
- uint32_t Idx = 0;
- uint32_t Off = 0;
+ // A sharded map to uniquify symbols by name.
+ std::vector<DenseMap<CachedHashStringRef, size_t>> Map(NumShards);
+ size_t Shift = 32 - countTrailingZeros(NumShards);
- for (GdbIndexChunk &Chunk : Chunks) {
- for (GdbIndexChunk::NameTypeEntry &Ent : Chunk.NamesAndTypes) {
- GdbSymbol *&Sym = Symbols[Ent.Name];
- if (!Sym) {
- Sym = make<GdbSymbol>(GdbSymbol{Ent.Name.hash(), Off, Ret.size()});
- Off += Ent.Name.size() + 1;
- Ret.push_back({});
- }
+ // Instantiate GdbSymbols while uniqufying them by name.
+ std::vector<std::vector<GdbSymbol>> Symbols(NumShards);
+ parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
+ for (ArrayRef<NameTypeEntry> Entries : NameTypes) {
+ for (const NameTypeEntry &Ent : Entries) {
+ size_t ShardId = Ent.Name.hash() >> Shift;
+ if ((ShardId & (Concurrency - 1)) != ThreadId)
+ continue;
- // gcc 5.4.1 produces a buggy .debug_gnu_pubnames that contains
- // duplicate entries, so we want to dedup them.
- std::vector<uint32_t> &Vec = Ret[Sym->CuVectorIndex];
- uint32_t Val = (Ent.Type << 24) | Idx;
- if (Vec.empty() || Vec.back() != Val)
- Vec.push_back(Val);
+ size_t &Idx = Map[ShardId][Ent.Name];
+ if (Idx) {
+ Symbols[ShardId][Idx - 1].CuVector.push_back(Ent.Type);
+ continue;
+ }
+
+ Idx = Symbols[ShardId].size() + 1;
+ Symbols[ShardId].push_back({Ent.Name, {Ent.Type}, 0, 0});
+ }
}
- Idx += Chunk.CompilationUnits.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));
+
+ // 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;
+ }
+ for (GdbSymbol &Sym : Ret) {
+ Sym.NameOff = Off;
+ Off += Sym.Name.size() + 1;
}
- StringPoolSize = Off;
return Ret;
}
-template <class ELFT> GdbIndexSection *elf::createGdbIndex() {
- // Gather debug info to create a .gdb_index section.
+// Returns a newly-created .gdb_index section.
+template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
std::vector<InputSection *> Sections = getDebugInfoSections();
- std::vector<GdbIndexChunk> Chunks(Sections.size());
-
- parallelForEachN(0, Chunks.size(), [&](size_t I) {
- ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>();
- DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File));
-
- Chunks[I].DebugInfoSec = Sections[I];
- Chunks[I].CompilationUnits = readCuList(Dwarf);
- Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]);
- Chunks[I].NamesAndTypes = readPubNamesAndTypes(Dwarf);
- });
// .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 it from the output.
+ // 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;
- // Create a .gdb_index and returns it.
- return make<GdbIndexSection>(std::move(Chunks));
-}
+ std::vector<GdbChunk> Chunks(Sections.size());
+ std::vector<std::vector<NameTypeEntry>> NameTypes(Sections.size());
-static size_t getCuSize(ArrayRef<GdbIndexChunk> Arr) {
- size_t Ret = 0;
- for (const GdbIndexChunk &D : Arr)
- Ret += D.CompilationUnits.size();
- return Ret;
-}
-
-static size_t getAddressAreaSize(ArrayRef<GdbIndexChunk> Arr) {
- size_t Ret = 0;
- for (const GdbIndexChunk &D : Arr)
- Ret += D.AddressAreas.size();
- return Ret;
-}
-
-std::vector<GdbSymbol *> GdbIndexSection::createGdbSymtab() {
- uint32_t Size = NextPowerOf2(Symbols.size() * 4 / 3);
- if (Size < 1024)
- Size = 1024;
-
- uint32_t Mask = Size - 1;
- std::vector<GdbSymbol *> Ret(Size);
+ parallelForEachN(0, Sections.size(), [&](size_t I) {
+ ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>();
+ DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File));
- for (auto &KV : Symbols) {
- GdbSymbol *Sym = KV.second;
- uint32_t I = Sym->NameHash & Mask;
- uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1;
+ Chunks[I].Sec = Sections[I];
+ Chunks[I].CompilationUnits = readCuList(Dwarf);
+ Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]);
+ NameTypes[I] = readPubNamesAndTypes(Dwarf, I);
+ });
- while (Ret[I])
- I = (I + Step) & Mask;
- Ret[I] = Sym;
- }
+ auto *Ret = make<GdbIndexSection>();
+ Ret->Chunks = std::move(Chunks);
+ Ret->Symbols = createSymbols(NameTypes);
+ Ret->initOutputSize();
return Ret;
}
-GdbIndexSection::GdbIndexSection(std::vector<GdbIndexChunk> &&C)
- : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index"), Chunks(std::move(C)) {
- fixCuIndex();
- CuVectors = createCuVectors();
- GdbSymtab = createGdbSymtab();
-
- // Compute offsets early to know the section size.
- // Each chunk size needs to be in sync with what we write in writeTo.
- CuTypesOffset = CuListOffset + getCuSize(Chunks) * 16;
- SymtabOffset = CuTypesOffset + getAddressAreaSize(Chunks) * 20;
- ConstantPoolOffset = SymtabOffset + GdbSymtab.size() * 8;
-
- size_t Off = 0;
- for (ArrayRef<uint32_t> Vec : CuVectors) {
- CuVectorOffsets.push_back(Off);
- Off += (Vec.size() + 1) * 4;
- }
- StringPoolOffset = ConstantPoolOffset + Off;
-}
-
-size_t GdbIndexSection::getSize() const {
- return StringPoolOffset + StringPoolSize;
-}
-
void GdbIndexSection::writeTo(uint8_t *Buf) {
- // Write the section header.
- write32le(Buf, 7);
- write32le(Buf + 4, CuListOffset);
- write32le(Buf + 8, CuTypesOffset);
- write32le(Buf + 12, CuTypesOffset);
- write32le(Buf + 16, SymtabOffset);
- write32le(Buf + 20, ConstantPoolOffset);
- Buf += 24;
+ // Write the header.
+ auto *Hdr = reinterpret_cast<GdbIndexHeader *>(Buf);
+ uint8_t *Start = Buf;
+ Hdr->Version = 7;
+ Buf += sizeof(*Hdr);
// Write the CU list.
- for (GdbIndexChunk &D : Chunks) {
- for (GdbIndexChunk::CuEntry &Cu : D.CompilationUnits) {
- write64le(Buf, D.DebugInfoSec->OutSecOff + Cu.CuOffset);
+ 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.
- for (GdbIndexChunk &D : Chunks) {
- for (GdbIndexChunk::AddressEntry &E : D.AddressAreas) {
- uint64_t BaseAddr =
- E.Section->getParent()->Addr + E.Section->getOffset(0);
+ 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);
+ write32le(Buf + 16, E.CuIndex + CuOff);
Buf += 20;
}
+ CuOff += Chunk.CompilationUnits.size();
}
- // Write the symbol table.
- for (GdbSymbol *Sym : GdbSymtab) {
- if (Sym) {
- write32le(Buf, Sym->NameOffset + StringPoolOffset - ConstantPoolOffset);
- write32le(Buf + 4, CuVectorOffsets[Sym->CuVectorIndex]);
- }
- Buf += 8;
+ // Write the on-disk open-addressing hash table containing symbols.
+ 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;
+
+ while (read32le(Buf + I * 8))
+ I = (I + Step) & Mask;
+
+ write32le(Buf + I * 8, Sym.NameOff);
+ write32le(Buf + I * 8 + 4, Sym.CuVectorOff);
}
+ Buf += SymtabSize * 8;
+
+ // Write the string pool.
+ Hdr->ConstantPoolOff = Buf - Start;
+ for (GdbSymbol &Sym : Symbols)
+ memcpy(Buf + Sym.NameOff, Sym.Name.data(), Sym.Name.size());
+
// Write the CU vectors.
- for (ArrayRef<uint32_t> Vec : CuVectors) {
- write32le(Buf, Vec.size());
+ for (GdbSymbol &Sym : Symbols) {
+ write32le(Buf, Sym.CuVector.size());
Buf += 4;
- for (uint32_t Val : Vec) {
+ for (uint32_t Val : Sym.CuVector) {
write32le(Buf, Val);
Buf += 4;
}
}
-
- // Write the string pool.
- for (auto &KV : Symbols) {
- CachedHashStringRef S = KV.first;
- GdbSymbol *Sym = KV.second;
- size_t Off = Sym->NameOffset;
- memcpy(Buf + Off, S.val().data(), S.size());
- Buf[Off + S.size()] = '\0';
- }
}
bool GdbIndexSection::empty() const { return !Out::DebugInfo; }
EhFrameHeader::EhFrameHeader()
- : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame_hdr") {}
+ : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {}
// .eh_frame_hdr contains a binary search table of pointers to FDEs.
// Each entry of the search table consists of two values,
@@ -2160,14 +2558,6 @@ void EhFrameHeader::writeTo(uint8_t *Buf) {
std::vector<FdeData> Fdes = InX::EhFrame->getFdeData();
- // 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.Pc < B.Pc; };
- std::stable_sort(Fdes.begin(), Fdes.end(), Less);
- auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; };
- Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end());
-
Buf[0] = 1;
Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
Buf[2] = DW_EH_PE_udata4;
@@ -2176,10 +2566,9 @@ void EhFrameHeader::writeTo(uint8_t *Buf) {
write32(Buf + 8, Fdes.size());
Buf += 12;
- uint64_t VA = this->getVA();
for (FdeData &Fde : Fdes) {
- write32(Buf, Fde.Pc - VA);
- write32(Buf + 4, Fde.FdeVA - VA);
+ write32(Buf, Fde.PcRel);
+ write32(Buf + 4, Fde.FdeVARel);
Buf += 8;
}
}
@@ -2289,11 +2678,9 @@ VersionNeedSection<ELFT>::VersionNeedSection()
NextIndex = getVerDefNum() + 1;
}
-template <class ELFT>
-void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) {
- SharedFile<ELFT> &File = SS->getFile<ELFT>();
- const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex];
- if (!Ver) {
+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;
}
@@ -2303,7 +2690,9 @@ void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) {
// for the soname.
if (File.VerdefMap.empty())
Needed.push_back({&File, InX::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.
@@ -2429,10 +2818,8 @@ void MergeNoTailSection::finalizeContents() {
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)
+ if ((ShardId & (Concurrency - 1)) == ThreadId && Sec->Pieces[I].Live)
Sec->Pieces[I].OutputOff = Shards[ShardId].add(Sec->getData(I));
}
}
@@ -2469,11 +2856,20 @@ static MergeSyntheticSection *createMergeSynthetic(StringRef Name,
return make<MergeNoTailSection>(Name, Type, Flags, Alignment);
}
-// Debug sections may be compressed by zlib. Uncompress if exists.
+// Debug sections may be compressed by zlib. Decompress if exists.
void elf::decompressSections() {
+ parallelForEach(InputSections,
+ [](InputSectionBase *Sec) { Sec->maybeDecompress(); });
+}
+
+template <class ELFT> void elf::splitSections() {
+ // splitIntoPieces needs to be called on each MergeInputSection
+ // before calling finalizeContents().
parallelForEach(InputSections, [](InputSectionBase *Sec) {
- if (Sec->Live)
- Sec->maybeUncompress();
+ if (auto *S = dyn_cast<MergeInputSection>(Sec))
+ S->splitIntoPieces();
+ else if (auto *Eh = dyn_cast<EhInputSection>(Sec))
+ Eh->split<ELFT>();
});
}
@@ -2485,14 +2881,6 @@ void elf::decompressSections() {
// 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() {
- // splitIntoPieces needs to be called on each MergeInputSection
- // before calling finalizeContents(). Do that first.
- parallelForEach(InputSections, [](InputSectionBase *Sec) {
- if (Sec->Live)
- if (auto *S = dyn_cast<MergeInputSection>(Sec))
- S->splitIntoPieces();
- });
-
std::vector<MergeSyntheticSection *> MergeSections;
for (InputSectionBase *&S : InputSections) {
MergeInputSection *MS = dyn_cast<MergeInputSection>(S);
@@ -2554,8 +2942,7 @@ ARMExidxSentinelSection::ARMExidxSentinelSection()
// address described by any other table entry.
void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
assert(Highest);
- uint64_t S =
- Highest->getParent()->Addr + Highest->getOffset(Highest->getSize());
+ uint64_t S = Highest->getVA(Highest->getSize());
uint64_t P = getVA();
Target->relocateOne(Buf, R_ARM_PREL31, S - P);
write32le(Buf + 4, 1);
@@ -2563,15 +2950,16 @@ void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
// The sentinel has to be removed if there are no other .ARM.exidx entries.
bool ARMExidxSentinelSection::empty() const {
- OutputSection *OS = getParent();
- for (auto *B : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(B))
- for (auto *S : ISD->Sections)
- if (!isa<ARMExidxSentinelSection>(S))
- return false;
+ for (InputSection *IS : getInputSections(getParent()))
+ if (!isa<ARMExidxSentinelSection>(IS))
+ return false;
return true;
}
+bool ARMExidxSentinelSection::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") {
@@ -2580,16 +2968,13 @@ ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off)
}
void ThunkSection::addThunk(Thunk *T) {
- uint64_t Off = alignTo(Size, T->Alignment);
- T->Offset = Off;
Thunks.push_back(T);
T->addSymbols(*this);
- Size = Off + T->size();
}
void ThunkSection::writeTo(uint8_t *Buf) {
- for (const Thunk *T : Thunks)
- T->writeTo(Buf + T->Offset, *this);
+ for (Thunk *T : Thunks)
+ T->writeTo(Buf + T->Offset);
}
InputSection *ThunkSection::getTargetInputSection() const {
@@ -2599,6 +2984,20 @@ InputSection *ThunkSection::getTargetInputSection() const {
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;
+}
+
InputSection *InX::ARMAttributes;
BssSection *InX::Bss;
BssSection *InX::BssRelRo;
@@ -2620,16 +3019,22 @@ MipsRldMapSection *InX::MipsRldMap;
PltSection *InX::Plt;
PltSection *InX::Iplt;
RelocationBaseSection *InX::RelaDyn;
+RelrBaseSection *InX::RelrDyn;
RelocationBaseSection *InX::RelaPlt;
RelocationBaseSection *InX::RelaIplt;
StringTableSection *InX::ShStrTab;
StringTableSection *InX::StrTab;
SymbolTableBaseSection *InX::SymTab;
-template GdbIndexSection *elf::createGdbIndex<ELF32LE>();
-template GdbIndexSection *elf::createGdbIndex<ELF32BE>();
-template GdbIndexSection *elf::createGdbIndex<ELF64LE>();
-template GdbIndexSection *elf::createGdbIndex<ELF64BE>();
+template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
+template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
+template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
+template GdbIndexSection *GdbIndexSection::create<ELF64BE>();
+
+template void elf::splitSections<ELF32LE>();
+template void elf::splitSections<ELF32BE>();
+template void elf::splitSections<ELF64LE>();
+template void elf::splitSections<ELF64BE>();
template void EhFrameSection::addSection<ELF32LE>(InputSectionBase *);
template void EhFrameSection::addSection<ELF32BE>(InputSectionBase *);
@@ -2641,6 +3046,11 @@ 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>;
@@ -2671,6 +3081,11 @@ template class elf::AndroidPackedRelocationSection<ELF32BE>;
template class elf::AndroidPackedRelocationSection<ELF64LE>;
template class elf::AndroidPackedRelocationSection<ELF64BE>;
+template class elf::RelrSection<ELF32LE>;
+template class elf::RelrSection<ELF32BE>;
+template class elf::RelrSection<ELF64LE>;
+template class elf::RelrSection<ELF64BE>;
+
template class elf::SymbolTableSection<ELF32LE>;
template class elf::SymbolTableSection<ELF32BE>;
template class elf::SymbolTableSection<ELF64LE>;
diff --git a/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
index a990590513bb..0366c6c3f8c7 100644
--- a/ELF/SyntheticSections.h
+++ b/ELF/SyntheticSections.h
@@ -26,10 +26,12 @@
#include "InputSection.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/Endian.h"
#include <functional>
namespace lld {
namespace elf {
+class Defined;
class SharedSymbol;
class SyntheticSection : public InputSection {
@@ -51,7 +53,6 @@ public:
// If any additional finalization of contents are needed post thunk creation.
virtual void postThunkContents() {}
virtual bool empty() const { return false; }
- uint64_t getVA() const;
static bool classof(const SectionBase *D) {
return D->kind() == InputSectionBase::Synthetic;
@@ -78,13 +79,18 @@ public:
size_t NumFdes = 0;
struct FdeData {
- uint32_t Pc;
- uint32_t FdeVA;
+ uint32_t PcRel;
+ uint32_t FdeVARel;
};
std::vector<FdeData> getFdeData() const;
+ 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;
+
uint64_t Size = 0;
template <class ELFT, class RelTy>
@@ -173,12 +179,21 @@ public:
bool updateAllocSize() override;
void finalizeContents() override;
bool empty() const override;
- void addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr);
- bool addDynTlsEntry(Symbol &Sym);
- bool addTlsIndex();
- uint64_t getPageEntryOffset(const Symbol &B, int64_t Addend) const;
- uint64_t getSymEntryOffset(const Symbol &B, int64_t Addend) const;
- uint64_t getGlobalDynOffset(const Symbol &B) const;
+
+ // Join separate GOTs built for each input file to generate
+ // primary and optional multiple secondary GOTs.
+ template <class ELFT> void build();
+
+ 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;
// 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
@@ -190,13 +205,8 @@ public:
// the number of reserved entries.
unsigned getLocalEntriesNum() const;
- // Returns offset of TLS part of the MIPS GOT table. This part goes
- // after 'local' and 'global' entries.
- uint64_t getTlsOffset() const;
-
- uint32_t getTlsIndexOff() const { return TlsIndexOff; }
-
- uint64_t getGp() const;
+ // Return _gp value for primary GOT (nullptr) or particular input file.
+ uint64_t getGp(const InputFile *F = nullptr) const;
private:
// MIPS GOT consists of three parts: local, global and tls. Each part
@@ -235,32 +245,110 @@ private:
// addressing, but MIPS ABI requires that these entries be present in GOT.
// TLS entries:
// Entries created by TLS relocations.
+ //
+ // If the sum of local, global and tls entries is less than 64K only single
+ // got is enough. Otherwise, multi-got is created. Series of primary and
+ // multiple secondary GOTs have the following layout:
+ // - Primary GOT
+ // Header
+ // Local entries
+ // Global entries
+ // Relocation only entries
+ // TLS entries
+ //
+ // - Secondary GOT
+ // Local entries
+ // Global entries
+ // TLS entries
+ // ...
+ //
+ // All GOT entries required by relocations from a single input file entirely
+ // belong to either primary or one of secondary GOTs. To reference GOT entries
+ // each GOT has its own _gp value points to the "middle" of the GOT.
+ // In the code this value loaded to the register which is used for GOT access.
+ //
+ // MIPS 32 function's prologue:
+ // lui v0,0x0
+ // 0: R_MIPS_HI16 _gp_disp
+ // addiu v0,v0,0
+ // 4: R_MIPS_LO16 _gp_disp
+ //
+ // MIPS 64:
+ // lui at,0x0
+ // 14: R_MIPS_GPREL16 main
+ //
+ // Dynamic linker does not know anything about secondary GOTs and cannot
+ // use a regular MIPS mechanism for GOT entries initialization. So we have
+ // to use an approach accepted by other architectures and create dynamic
+ // relocations R_MIPS_REL32 to initialize global entries (and local in case
+ // of PIC code) in secondary GOTs. But ironically MIPS dynamic linker
+ // requires GOT entries and correspondingly ordered dynamic symbol table
+ // entries to deal with dynamic relocations. To handle this problem
+ // relocation-only section in the primary GOT contains entries for all
+ // symbols referenced in global parts of secondary GOTs. Although the sum
+ // of local and normal global entries of the primary got should be less
+ // than 64K, the size of the primary got (including relocation-only entries
+ // can be greater than 64K, because parts of the primary got that overflow
+ // the 64K limit are used only by the dynamic linker at dynamic link-time
+ // and not by 16-bit gp-relative addressing at run-time.
+ //
+ // For complete multi-GOT description see the following link
+ // https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT
// Number of "Header" entries.
static const unsigned HeaderEntriesNum = 2;
- // Number of allocated "Page" entries.
- uint32_t PageEntriesNum = 0;
- // Map output sections referenced by MIPS GOT relocations
- // to the first index of "Page" entries allocated for this section.
- llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap;
-
- typedef std::pair<const Symbol *, uint64_t> GotEntry;
- typedef std::vector<GotEntry> GotEntries;
- // Map from Symbol-Addend pair to the GOT index.
- llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
- // Local entries (16-bit access).
- GotEntries LocalEntries;
- // Local entries (32-bit access).
- GotEntries LocalEntries32;
-
- // Normal and reloc-only global entries.
- GotEntries GlobalEntries;
-
- // TLS entries.
- std::vector<const Symbol *> TlsEntries;
- uint32_t TlsIndexOff = -1;
uint64_t Size = 0;
+
+ size_t LocalEntriesNum = 0;
+
+ // Symbol and addend.
+ typedef std::pair<Symbol *, int64_t> GotEntry;
+
+ struct FileGot {
+ InputFile *File = nullptr;
+ size_t StartIndex = 0;
+
+ struct PageBlock {
+ size_t FirstIndex = 0;
+ size_t 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;
+ // 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;
+ // Set of symbols referenced by dynamic TLS relocations.
+ llvm::MapVector<Symbol *, size_t> DynTlsSymbols;
+
+ // Total number of all entries.
+ size_t getEntriesNum() const;
+ // Number of "page" entries.
+ size_t getPageEntriesNum() const;
+ // Number of entries require 16-bit index to access.
+ size_t getIndexedEntriesNum() const;
+
+ bool isOverflow() const;
+ };
+
+ // 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;
+
+ // Return (and create if necessary) `FileGot`.
+ 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);
};
class GotPltSection final : public SyntheticSection {
@@ -269,7 +357,7 @@ public:
void addEntry(Symbol &Sym);
size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
- bool empty() const override { return Entries.empty(); }
+ bool empty() const override;
private:
std::vector<const Symbol *> Entries;
@@ -310,30 +398,48 @@ private:
class DynamicReloc {
public:
- DynamicReloc(uint32_t Type, const InputSectionBase *InputSec,
+ 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) {}
+ 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) {}
uint64_t getOffset() const;
- int64_t getAddend() const;
uint32_t getSymIndex() const;
const InputSectionBase *getInputSec() const { return InputSec; }
- uint32_t Type;
+ // 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.
+ int64_t computeAddend() const;
+
+ RelType Type;
private:
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;
};
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;
@@ -350,6 +456,7 @@ 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);
@@ -361,6 +468,13 @@ 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);
+ // 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; }
@@ -405,6 +519,39 @@ private:
SmallVector<char, 0> RelocData;
};
+struct RelativeReloc {
+ uint64_t getOffset() const { return InputSec->getVA(OffsetInSec); }
+
+ const InputSectionBase *InputSec;
+ uint64_t OffsetInSec;
+};
+
+class RelrBaseSection : public SyntheticSection {
+public:
+ RelrBaseSection();
+ std::vector<RelativeReloc> Relocs;
+};
+
+// RelrSection is used to encode offsets for relative relocations.
+// Proposal for adding SHT_RELR sections to generic-abi is here:
+// 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;
+
+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());
+ }
+
+private:
+ std::vector<Elf_Relr> RelrRelocs;
+};
+
struct SymbolTableEntry {
Symbol *Sym;
size_t StrTabOffset;
@@ -455,7 +602,7 @@ public:
void addSymbols(std::vector<SymbolTableEntry> &Symbols);
private:
- size_t getShift2() const { return Config->Is64 ? 6 : 5; }
+ enum { Shift2 = 6 };
void writeBloomFilter(uint8_t *Buf);
void writeHashTable(uint8_t *Buf);
@@ -484,13 +631,13 @@ private:
size_t Size = 0;
};
-// The PltSection is used for both the Plt and Iplt. The former always has a
+// The PltSection is used for both the Plt and Iplt. The former usually has a
// header as its first entry that is used at run-time to resolve lazy binding.
// The latter is used for GNU Ifunc symbols, that will be subject to a
// Target->IRelativeRel.
class PltSection : public SyntheticSection {
public:
- PltSection(size_t HeaderSize);
+ PltSection(bool IsIplt);
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
bool empty() const override { return Entries.empty(); }
@@ -501,13 +648,12 @@ public:
private:
unsigned getPltRelocOff() const;
std::vector<std::pair<const Symbol *, unsigned>> Entries;
- // Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero
size_t HeaderSize;
+ bool IsIplt;
};
-// GdbIndexChunk is created for each .debug_info section and contains
-// information to create a part of .gdb_index for a given input section.
-struct GdbIndexChunk {
+class GdbIndexSection final : public SyntheticSection {
+public:
struct AddressEntry {
InputSection *Section;
uint64_t LowAddress;
@@ -522,59 +668,51 @@ struct GdbIndexChunk {
struct NameTypeEntry {
llvm::CachedHashStringRef Name;
- uint8_t Type;
+ uint32_t Type;
};
- InputSection *DebugInfoSec;
- std::vector<AddressEntry> AddressAreas;
- std::vector<CuEntry> CompilationUnits;
- std::vector<NameTypeEntry> NamesAndTypes;
-};
+ struct GdbChunk {
+ InputSection *Sec;
+ std::vector<AddressEntry> AddressAreas;
+ std::vector<CuEntry> CompilationUnits;
+ };
-// The symbol type for the .gdb_index section.
-struct GdbSymbol {
- uint32_t NameHash;
- size_t NameOffset;
- size_t CuVectorIndex;
-};
+ struct GdbSymbol {
+ llvm::CachedHashStringRef Name;
+ std::vector<uint32_t> CuVector;
+ uint32_t NameOff;
+ uint32_t CuVectorOff;
+ };
-class GdbIndexSection final : public SyntheticSection {
-public:
- GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks);
+ GdbIndexSection();
+ template <typename ELFT> static GdbIndexSection *create();
void writeTo(uint8_t *Buf) override;
- size_t getSize() const override;
+ size_t getSize() const override { return Size; }
bool empty() const override;
private:
- void fixCuIndex();
- std::vector<std::vector<uint32_t>> createCuVectors();
- std::vector<GdbSymbol *> createGdbSymtab();
-
- // A symbol table for this .gdb_index section.
- std::vector<GdbSymbol *> GdbSymtab;
-
- // CU vector is a part of constant pool area of section.
- std::vector<std::vector<uint32_t>> CuVectors;
+ 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;
+ };
- // Symbol table contents.
- llvm::DenseMap<llvm::CachedHashStringRef, GdbSymbol *> Symbols;
+ void initOutputSize();
+ size_t computeSymtabSize() const;
- // Each chunk contains information gathered from a debug sections of single
- // object and used to build different areas of gdb index.
- std::vector<GdbIndexChunk> Chunks;
+ // Each chunk contains information gathered from debug sections of a
+ // single object file.
+ std::vector<GdbChunk> Chunks;
- static constexpr uint32_t CuListOffset = 24;
- uint32_t CuTypesOffset;
- uint32_t SymtabOffset;
- uint32_t ConstantPoolOffset;
- uint32_t StringPoolOffset;
- uint32_t StringPoolSize;
+ // A symbol table for this .gdb_index section.
+ std::vector<GdbSymbol> Symbols;
- std::vector<size_t> CuVectorOffsets;
+ size_t Size;
};
-template <class ELFT> GdbIndexSection *createGdbIndex();
-
// --eh-frame-hdr option tells linker to construct a header for all the
// .eh_frame sections. This header is placed to a section named .eh_frame_hdr
// and also to a PT_GNU_EH_FRAME segment.
@@ -653,7 +791,7 @@ template <class ELFT> class VersionNeedSection final : public SyntheticSection {
public:
VersionNeedSection();
- void addSymbol(SharedSymbol *SS);
+ void addSymbol(Symbol *Sym);
void finalizeContents() override;
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
@@ -668,13 +806,12 @@ public:
class MergeSyntheticSection : public SyntheticSection {
public:
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) {}
-
- std::vector<MergeInputSection *> Sections;
};
class MergeTailSection final : public MergeSyntheticSection {
@@ -787,7 +924,12 @@ public:
void writeTo(uint8_t *Buf) override;
bool empty() const override;
- InputSection *Highest = 0;
+ static bool classof(const SectionBase *D);
+
+ // 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;
};
// A container for one or more linker generated thunks. Instances of these
@@ -805,19 +947,21 @@ public:
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) override;
InputSection *getTargetInputSection() const;
+ bool assignOffsets();
private:
- std::vector<const Thunk *> Thunks;
+ std::vector<Thunk *> Thunks;
size_t Size = 0;
};
InputSection *createInterpSection();
MergeInputSection *createCommentSection();
void decompressSections();
+template <class ELFT> void splitSections();
void mergeSections();
-Symbol *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section);
+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 InX {
@@ -842,6 +986,7 @@ struct InX {
static PltSection *Plt;
static PltSection *Iplt;
static RelocationBaseSection *RelaDyn;
+ static RelrBaseSection *RelrDyn;
static RelocationBaseSection *RelaPlt;
static RelocationBaseSection *RelaIplt;
static StringTableSection *ShStrTab;
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index b528fd583c1a..815f3a045551 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -60,6 +60,8 @@ TargetInfo *elf::getTarget() {
return getARMTargetInfo();
case EM_AVR:
return getAVRTargetInfo();
+ case EM_HEXAGON:
+ return getHexagonTargetInfo();
case EM_MIPS:
switch (Config->EKind) {
case ELF32LEKind:
@@ -87,29 +89,29 @@ TargetInfo *elf::getTarget() {
fatal("unknown target machine");
}
-template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) {
+template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) {
for (InputSectionBase *D : InputSections) {
- auto *IS = dyn_cast<InputSection>(D);
- if (!IS || !IS->getParent())
+ auto *IS = cast<InputSection>(D);
+ if (!IS->getParent())
continue;
uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff;
if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
- return IS->template getLocation<ELFT>(Loc - ISLoc) + ": ";
+ return {IS, IS->template getLocation<ELFT>(Loc - ISLoc) + ": "};
}
- return "";
+ return {};
}
-std::string elf::getErrorLocation(const uint8_t *Loc) {
+ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
switch (Config->EKind) {
case ELF32LEKind:
- return getErrorLoc<ELF32LE>(Loc);
+ return getErrPlace<ELF32LE>(Loc);
case ELF32BEKind:
- return getErrorLoc<ELF32BE>(Loc);
+ return getErrPlace<ELF32BE>(Loc);
case ELF64LEKind:
- return getErrorLoc<ELF64LE>(Loc);
+ return getErrPlace<ELF64LE>(Loc);
case ELF64BEKind:
- return getErrorLoc<ELF64BE>(Loc);
+ return getErrPlace<ELF64BE>(Loc);
default:
llvm_unreachable("unknown ELF type");
}
@@ -128,6 +130,12 @@ bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
return false;
}
+bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc,
+ uint8_t *End) const {
+ llvm_unreachable("Target doesn't support split stacks.");
+}
+
+
bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
return true;
}
diff --git a/ELF/Target.h b/ELF/Target.h
index 1f58adba1817..82c7b8f7b6c5 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -25,9 +25,9 @@ class Symbol;
class TargetInfo {
public:
virtual uint32_t calcEFlags() const { return 0; }
- virtual bool isPicRel(RelType Type) const { return true; }
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;
@@ -43,8 +43,12 @@ public:
virtual void addPltHeaderSymbols(InputSection &IS) const {}
virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {}
+ unsigned getPltEntryOffset(unsigned Index) const {
+ return Index * PltEntrySize + PltHeaderSize;
+ }
+
// Returns true if a relocation only uses the low bits of a value such that
- // all those bits are in in the same page. For example, if the relocation
+ // 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.
@@ -55,6 +59,13 @@ public:
virtual bool needsThunk(RelExpr Expr, RelType RelocType,
const InputFile *File, uint64_t BranchAddr,
const Symbol &S) const;
+
+ // The function with a prologue starting at Loc was compiled with
+ // -fsplit-stack and it calls a function compiled without. Adjust the prologue
+ // to do the right thing. See https://gcc.gnu.org/wiki/SplitStacks.
+ virtual bool adjustPrologueForCrossSplitStack(uint8_t *Loc,
+ uint8_t *End) 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;
@@ -71,9 +82,10 @@ public:
uint64_t getImageBase();
- // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
- // end of .got
+ // 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;
// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.
@@ -97,9 +109,18 @@ public:
// to support lazy loading.
unsigned GotPltHeaderEntriesNum = 3;
- // Set to 0 for variant 2
+ // On PPC ELF V2 abi, the first entry in the .got is the .TOC.
+ unsigned GotHeaderEntriesNum = 0;
+
+ // For TLS variant 1, the TCB is a fixed size specified by the Target.
+ // For variant 2, the TCB is an unspecified size.
+ // Set to 0 for variant 2.
unsigned TcbSize = 0;
+ // Set to the offset (in bytes) that the thread pointer is initialized to
+ // point to, relative to the start of the thread local storage.
+ unsigned TlsTpOffset = 0;
+
bool NeedsThunks = false;
// A 4-byte field corresponding to one or more trap instructions, used to pad
@@ -126,6 +147,7 @@ TargetInfo *getAArch64TargetInfo();
TargetInfo *getAMDGPUTargetInfo();
TargetInfo *getARMTargetInfo();
TargetInfo *getAVRTargetInfo();
+TargetInfo *getHexagonTargetInfo();
TargetInfo *getPPC64TargetInfo();
TargetInfo *getPPCTargetInfo();
TargetInfo *getSPARCV9TargetInfo();
@@ -134,7 +156,17 @@ TargetInfo *getX86TargetInfo();
TargetInfo *getX86_64TargetInfo();
template <class ELFT> TargetInfo *getMipsTargetInfo();
-std::string getErrorLocation(const uint8_t *Loc);
+struct ErrorPlace {
+ InputSectionBase *IS;
+ std::string Loc;
+};
+
+// Returns input section and corresponding source string for the given location.
+ErrorPlace getErrorPlace(const uint8_t *Loc);
+
+static inline std::string getErrorLocation(const uint8_t *Loc) {
+ return getErrorPlace(Loc).Loc;
+}
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t Expr);
@@ -146,39 +178,74 @@ 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) {
- error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
- " out of range: " + V + " is not in [" + Twine(Min) + ", " +
- Twine(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 "
+ "of debug sections";
+
+ error(ErrPlace.Loc + "relocation " + lld::toString(Type) +
+ " out of range: " + V.str() + " is not in [" + Twine(Min).str() + ", " +
+ Twine(Max).str() + "]" + Hint);
}
-template <unsigned N>
-static void checkInt(uint8_t *Loc, int64_t V, RelType Type) {
- if (!llvm::isInt<N>(V))
+// Sign-extend Nth bit all the way to MSB.
+inline int64_t signExtend(uint64_t V, int N) {
+ return int64_t(V << (64 - N)) >> (64 - N);
+}
+
+// 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 != signExtend(V, N))
reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
}
-template <unsigned N>
-static void checkUInt(uint8_t *Loc, uint64_t V, RelType Type) {
- if (!llvm::isUInt<N>(V))
+// 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));
}
-template <unsigned N>
-static void checkIntUInt(uint8_t *Loc, uint64_t V, RelType Type) {
- if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V))
- // 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
+// 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) {
+ // 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)signExtend(V, N) && (V >> N) != 0)
reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
- llvm::maxUIntN(N));
+ llvm::maxIntN(N));
}
-template <unsigned N>
-static void checkAlignment(uint8_t *Loc, uint64_t V, RelType Type) {
+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 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 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 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 b0bbf6da705a..2cd7e51ae357 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -40,7 +40,6 @@
using namespace llvm;
using namespace llvm::object;
-using namespace llvm::support::endian;
using namespace llvm::ELF;
namespace lld {
@@ -52,59 +51,112 @@ namespace {
class AArch64ABSLongThunk final : public Thunk {
public:
AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 16; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
class AArch64ADRPThunk final : public Thunk {
public:
AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 12; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
+// Base class for ARM thunks.
+//
+// An ARM thunk may be either short or long. A short thunk is simply a branch
+// (B) instruction, and it may be used to call ARM functions when the distance
+// from the thunk to the target is less than 32MB. Long thunks can branch to any
+// virtual address and can switch between ARM and Thumb, and they are
+// implemented in the derived classes. This class tries to create a short thunk
+// if the target is in range, otherwise it creates a long thunk.
+class ARMThunk : public Thunk {
+public:
+ 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;
+
+ // 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;
+
+private:
+ // This field tracks whether all previously considered layouts would allow
+ // this thunk to be short. If we have ever needed a long thunk, we always
+ // create a long thunk, even if the thunk may be short given the current
+ // 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;
+};
+
+// Base class for Thumb-2 thunks.
+//
+// This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
+// which has a range of 16MB.
+class ThumbThunk : public Thunk {
+public:
+ 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;
+
+ // 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;
+
+private:
+ // See comment in ARMThunk above.
+ bool MayUseShortThunk = true;
+};
+
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
-class ARMV7ABSLongThunk final : public Thunk {
+class ARMV7ABSLongThunk final : public ARMThunk {
public:
- ARMV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
+ ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 12; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
-class ARMV7PILongThunk final : public Thunk {
+class ARMV7PILongThunk final : public ARMThunk {
public:
- ARMV7PILongThunk(Symbol &Dest) : Thunk(Dest) {}
+ ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
- uint32_t size() const override { return 16; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 16; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
-class ThumbV7ABSLongThunk final : public Thunk {
+class ThumbV7ABSLongThunk final : public ThumbThunk {
public:
- ThumbV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
- uint32_t size() const override { return 10; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 10; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
-class ThumbV7PILongThunk final : public Thunk {
+class ThumbV7PILongThunk final : public ThumbThunk {
public:
- ThumbV7PILongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 12; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
// MIPS LA25 thunk
@@ -112,8 +164,8 @@ class MipsThunk final : public Thunk {
public:
MipsThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 16; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
@@ -123,8 +175,8 @@ class MicroMipsThunk final : public Thunk {
public:
MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 14; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 14; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
@@ -134,14 +186,44 @@ class MicroMipsR6Thunk final : public Thunk {
public:
MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 12; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
+
+// PPC64 Plt call stubs.
+// Any call site that needs to call through a plt entry needs a call stub in
+// the .text section. The call stub is responsible for:
+// 1) Saving the toc-pointer to the stack.
+// 2) Loading the target functions address from the procedure linkage table into
+// r12 for use by the target functions global entry point, and into the count
+// register.
+// 3) Transfering control to the target function through an indirect branch.
+class PPC64PltCallStub final : public Thunk {
+public:
+ PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {}
+ uint32_t size() override { return 20; }
+ void writeTo(uint8_t *Buf) override;
+ void addSymbols(ThunkSection &IS) override;
+};
+
} // 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;
+}
+
+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) {
@@ -149,7 +231,7 @@ static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
return V;
}
-void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
const uint8_t Data[] = {
0x50, 0x00, 0x00, 0x58, // ldr x16, L0
0x00, 0x02, 0x1f, 0xd6, // br x16
@@ -162,11 +244,10 @@ void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
- addSyntheticLocal("$d", STT_NOTYPE, Offset + 8, 0, IS);
+ addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
+ STT_FUNC, 0, IS);
+ addSymbol("$x", STT_NOTYPE, 0, IS);
+ addSymbol("$d", STT_NOTYPE, 8, IS);
}
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
@@ -174,26 +255,24 @@ 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, ThunkSection &IS) const {
+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 = ThunkSym->getVA();
+ 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)
-{
- ThunkSym = addSyntheticLocal(
- Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
+void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
+ addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
+ 0, IS);
+ addSymbol("$x", STT_NOTYPE, 0, IS);
}
// ARM Target Thunks
@@ -202,7 +281,81 @@ static uint64_t getARMThunkDestVA(const Symbol &S) {
return SignExtend64<32>(V);
}
-void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+// 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)
+ return 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;
+}
+
+void ARMThunk::writeTo(uint8_t *Buf) {
+ if (!mayUseShortThunk()) {
+ writeLong(Buf);
+ return;
+ }
+
+ 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);
+}
+
+bool ARMThunk::isCompatibleWith(RelType Type) const {
+ // Thumb branch relocations can't use BLX
+ return Type != R_ARM_THM_JUMP19 && 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)
+ return 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;
+}
+
+void ThumbThunk::writeTo(uint8_t *Buf) {
+ if (!mayUseShortThunk()) {
+ writeLong(Buf);
+ return;
+ }
+
+ 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);
+}
+
+bool ThumbThunk::isCompatibleWith(RelType Type) const {
+ // ARM branch relocations can't use BLX
+ return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+}
+
+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
@@ -215,18 +368,12 @@ void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
-}
-
-bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const {
- // Thumb branch relocations can't use BLX
- return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
+ addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
+ STT_FUNC, 0, IS);
+ addSymbol("$a", STT_NOTYPE, 0, IS);
}
-void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+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
@@ -239,18 +386,12 @@ void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
- Offset | 0x1, size(), IS);
- addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
+ addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
+ STT_FUNC, 1, IS);
+ addSymbol("$t", STT_NOTYPE, 0, IS);
}
-bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const {
- // ARM branch relocations can't use BLX
- return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
-}
-
-void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+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)
@@ -258,7 +399,7 @@ void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
0x1c, 0xff, 0x2f, 0xe1, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = ThunkSym->getVA();
+ uint64_t P = getThunkTargetSym()->getVA();
uint64_t Offset = S - P - 16;
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
@@ -266,18 +407,12 @@ void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
+ addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
+ 0, IS);
+ addSymbol("$a", STT_NOTYPE, 0, IS);
}
-bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const {
- // Thumb branch relocations can't use BLX
- return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
-}
-
-void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+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)
@@ -285,7 +420,7 @@ void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
0x60, 0x47, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = ThunkSym->getVA() & ~0x1;
+ uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
uint64_t Offset = S - P - 12;
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
@@ -293,32 +428,25 @@ void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), STT_FUNC,
- Offset | 0x1, size(), IS);
- addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
-}
-
-bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const {
- // ARM branch relocations can't use BLX
- return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+ addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
+ STT_FUNC, 1, IS);
+ addSymbol("$t", STT_NOTYPE, 0, IS);
}
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
-void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+void MipsThunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA();
- write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func)
- write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func
- write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
- write32(Buf + 12, 0x00000000, Config->Endianness); // nop
+ 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) {
- ThunkSym =
- addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()),
- STT_FUNC, Offset, size(), IS);
+ addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
+ IS);
}
InputSection *MipsThunk::getTargetInputSection() const {
@@ -328,22 +456,21 @@ InputSection *MipsThunk::getTargetInputSection() const {
// Write microMIPS R2-R5 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+void MicroMipsThunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA() | 1;
- write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func)
- write16(Buf + 4, 0xd400, Config->Endianness); // j func
- write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
- write16(Buf + 12, 0x0c00, Config->Endianness); // nop
+ 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) {
- ThunkSym =
- addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
- STT_FUNC, Offset, size(), IS);
- ThunkSym->StOther |= STO_MIPS_MICROMIPS;
+ Defined *D = addSymbol(
+ Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
+ D->StOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsThunk::getTargetInputSection() const {
@@ -353,22 +480,21 @@ InputSection *MicroMipsThunk::getTargetInputSection() const {
// Write microMIPS R6 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA() | 1;
- uint64_t P = ThunkSym->getVA();
- write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func)
- write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
- write16(Buf + 8, 0x9400, Config->Endianness); // bc func
+ 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) {
- ThunkSym =
- addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
- STT_FUNC, Offset, size(), IS);
- ThunkSym->StOther |= STO_MIPS_MICROMIPS;
+ Defined *D = addSymbol(
+ Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
+ D->StOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
@@ -376,6 +502,25 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
return dyn_cast<InputSection>(DR.Section);
}
+void PPC64PltCallStub::writeTo(uint8_t *Buf) {
+ int64_t Off = Destination.getGotPltVA() - getPPC64TocBase();
+ // Need to add 0x8000 to offset to account for the low bits being signed.
+ uint16_t OffHa = (Off + 0x8000) >> 16;
+ uint16_t OffLo = Off;
+
+ write32(Buf + 0, 0xf8410018); // std r2,24(r1)
+ write32(Buf + 4, 0x3d820000 | OffHa); // addis r12,r2, X@plt@to@ha
+ write32(Buf + 8, 0xe98c0000 | OffLo); // ld r12,X@plt@toc@l(r12)
+ write32(Buf + 12, 0x7d8903a6); // mtctr r12
+ write32(Buf + 16, 0x4e800420); // bctr
+}
+
+void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
+ Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC,
+ 0, IS);
+ S->NeedsTocRestore = true;
+}
+
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
Thunk::~Thunk() = default;
@@ -419,15 +564,26 @@ static Thunk *addThunkMips(RelType Type, Symbol &S) {
return make<MipsThunk>(S);
}
+static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
+ if (Type == R_PPC64_REL24)
+ return make<PPC64PltCallStub>(S);
+ fatal("unexpected relocation type");
+}
+
Thunk *addThunk(RelType Type, Symbol &S) {
if (Config->EMachine == EM_AARCH64)
return addThunkAArch64(Type, S);
- else if (Config->EMachine == EM_ARM)
+
+ if (Config->EMachine == EM_ARM)
return addThunkArm(Type, S);
- else if (Config->EMachine == EM_MIPS)
+
+ if (Config->EMachine == EM_MIPS)
return addThunkMips(Type, S);
- llvm_unreachable("add Thunk only supported for ARM and Mips");
- return nullptr;
+
+ if (Config->EMachine == EM_PPC64)
+ return addThunkPPC64(Type, S);
+
+ llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
}
} // end namespace elf
diff --git a/ELF/Thunks.h b/ELF/Thunks.h
index 828fac0bf53b..ed82b4d946ac 100644
--- a/ELF/Thunks.h
+++ b/ELF/Thunks.h
@@ -14,6 +14,7 @@
namespace lld {
namespace elf {
+class Defined;
class Symbol;
class ThunkSection;
// Class to describe an instance of a Thunk.
@@ -30,12 +31,17 @@ public:
Thunk(Symbol &Destination);
virtual ~Thunk();
- virtual uint32_t size() const { return 0; }
- virtual void writeTo(uint8_t *Buf, ThunkSection &IS) const {}
+ virtual uint32_t size() = 0;
+ virtual void writeTo(uint8_t *Buf) = 0;
- // All Thunks must define at least one symbol ThunkSym so that we can
- // redirect relocations to it.
- virtual void addSymbols(ThunkSection &IS) {}
+ // 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;
+
+ 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.
@@ -45,10 +51,12 @@ public:
// compatible with it.
virtual bool isCompatibleWith(RelType Type) const { return true; }
+ Defined *getThunkTargetSym() const { return Syms[0]; }
+
// The alignment requirement for this Thunk, defaults to the size of the
// typical code section alignment.
Symbol &Destination;
- Symbol *ThunkSym;
+ llvm::SmallVector<Defined *, 3> Syms;
uint64_t Offset = 0;
uint32_t Alignment = 4;
};
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 15f382104756..533ac47f937f 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -9,18 +9,19 @@
#include "Writer.h"
#include "AArch64ErrataFix.h"
+#include "CallGraphSort.h"
#include "Config.h"
#include "Filesystem.h"
#include "LinkerScript.h"
#include "MapFile.h"
#include "OutputSections.h"
#include "Relocations.h"
-#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.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"
@@ -49,7 +50,7 @@ public:
private:
void copyLocalSymbols();
void addSectionSymbols();
- void forEachRelSec(std::function<void(InputSectionBase &)> Fn);
+ void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> Fn);
void sortSections();
void resolveShfLinkOrder();
void sortInputSections();
@@ -62,6 +63,7 @@ private:
void assignFileOffsets();
void assignFileOffsetsBinary();
void setPhdrs();
+ void checkSections();
void fixSectionAlignments();
void openFile();
void writeTrapInstr();
@@ -81,39 +83,48 @@ private:
uint64_t FileSize;
uint64_t SectionHeaderOff;
-
- bool HasGotBaseSym = false;
};
} // anonymous namespace
-StringRef elf::getOutputSectionName(InputSectionBase *S) {
- // ".zdebug_" is a prefix for ZLIB-compressed sections.
- // Because we decompressed input sections, we want to remove 'z'.
- if (S->Name.startswith(".zdebug_"))
- return Saver.save("." + S->Name.substr(2));
+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;
// 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 ((S->Type == SHT_REL || S->Type == SHT_RELA) &&
- !isa<SyntheticSection>(S)) {
- OutputSection *Out =
- cast<InputSection>(S)->getRelocatedSection()->getOutputSection();
- if (S->Type == SHT_RELA)
- return Saver.save(".rela" + Out->Name);
- return Saver.save(".rel" + Out->Name);
+ 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);
+ }
}
+ // This check is for -z keep-text-section-prefix. This option separates text
+ // sections with prefix ".text.hot", ".text.unlikely", ".text.startup" or
+ // ".text.exit".
+ // When enabled, this allows identifying the hot code region (.text.hot) in
+ // the final binary which can be selectively mapped to huge pages or mlocked,
+ // for instance.
+ if (Config->ZKeepTextSectionPrefix)
+ for (StringRef V :
+ {".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."}) {
+ if (isSectionPrefix(V, S->Name))
+ return V.drop_back();
+ }
+
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."}) {
- StringRef Prefix = V.drop_back();
- if (S->Name.startswith(V) || S->Name == Prefix)
- return Prefix;
+ if (isSectionPrefix(V, S->Name))
+ return V.drop_back();
}
// CommonSection is identified as "COMMON" in linker scripts.
@@ -194,21 +205,30 @@ void elf::addReservedSymbols() {
Symtab->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_GLOBAL);
}
+ // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which
+ // combines the typical ELF GOT with the small data sections. It commonly
+ // includes .got .toc .sdata .sbss. The .TOC. symbol replaces both
+ // _GLOBAL_OFFSET_TABLE_ and _SDA_BASE_ from the 32-bit ABI. It is used to
+ // represent the TOC base which is offset by 0x8000 bytes from the start of
+ // the .got section.
ElfSym::GlobalOffsetTable = addOptionalRegular(
- "_GLOBAL_OFFSET_TABLE_", Out::ElfHeader, Target->GotBaseSymOff);
+ (Config->EMachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_",
+ Out::ElfHeader, Target->GotBaseSymOff);
// __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);
+
// __executable_start is not documented, but the expectation of at
- // least the android libc is that it points to the elf header too.
+ // least the Android libc is that it points to the ELF header.
+ 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.
- for (const char *Name :
- {"__ehdr_start", "__executable_start", "__dso_handle"})
- addOptionalRegular(Name, 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)
@@ -280,10 +300,9 @@ template <class ELFT> static void createSyntheticSections() {
// 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");
- InX::BssRelRo = make<BssSection>(
- HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
+ bool HasDataRelRo = Script->HasSectionsCommand && findSection(".data.rel.ro");
+ InX::BssRelRo =
+ make<BssSection>(HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
Add(InX::BssRelRo);
// Add MIPS-specific sections.
@@ -330,6 +349,11 @@ template <class ELFT> static void createSyntheticSections() {
Add(InX::RelaDyn);
}
+ if (Config->RelrPackDynRelocs) {
+ InX::RelrDyn = make<RelrSection<ELFT>>();
+ Add(InX::RelrDyn);
+ }
+
// Add .got. MIPS' .got is so different from the other archs,
// it has its own class.
if (Config->EMachine == EM_MIPS) {
@@ -346,7 +370,7 @@ template <class ELFT> static void createSyntheticSections() {
Add(InX::IgotPlt);
if (Config->GdbIndex) {
- InX::GdbIndex = createGdbIndex<ELFT>();
+ InX::GdbIndex = GdbIndexSection::create<ELFT>();
Add(InX::GdbIndex);
}
@@ -369,9 +393,9 @@ template <class ELFT> static void createSyntheticSections() {
false /*Sort*/);
Add(InX::RelaIplt);
- InX::Plt = make<PltSection>(Target->PltHeaderSize);
+ InX::Plt = make<PltSection>(false);
Add(InX::Plt);
- InX::Iplt = make<PltSection>(0);
+ InX::Iplt = make<PltSection>(true);
Add(InX::Iplt);
if (!Config->Relocatable) {
@@ -427,13 +451,14 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
+ 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.
- parallelForEach(OutputSections,
- [](OutputSection *Sec) { Sec->maybeCompress<ELFT>(); });
+ for (OutputSection *Sec : OutputSections)
+ Sec->maybeCompress<ELFT>();
- Script->assignAddresses();
Script->allocateHeaders(Phdrs);
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
@@ -453,6 +478,9 @@ template <class ELFT> void Writer<ELFT>::run() {
Sec->Addr = 0;
}
+ if (Config->CheckSections)
+ checkSections();
+
// It does not make sense try to open the file if we have error already.
if (errorCount())
return;
@@ -475,8 +503,9 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
- // Handle -Map option.
+ // Handle -Map and -cref options.
writeMapFile();
+ writeCrossReferenceTable();
if (errorCount())
return;
@@ -486,12 +515,9 @@ template <class ELFT> void Writer<ELFT>::run() {
static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
const Symbol &B) {
- if (B.isFile() || B.isSection())
+ if (B.isSection())
return false;
- // If sym references a section in a discarded group, don't keep it.
- if (Sec == &InputSection::Discarded)
- return false;
if (Config->Discard == DiscardPolicy::None)
return true;
@@ -637,6 +663,9 @@ static bool isRelroSection(const OutputSection *Sec) {
if (InX::Got && Sec == InX::Got->getParent())
return true;
+ 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
@@ -668,20 +697,22 @@ 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 << 16,
- RF_NOT_INTERP = 1 << 15,
- RF_NOT_ALLOC = 1 << 14,
- RF_WRITE = 1 << 13,
- RF_EXEC_WRITE = 1 << 12,
- RF_EXEC = 1 << 11,
- RF_NON_TLS_BSS = 1 << 10,
- RF_NON_TLS_BSS_RO = 1 << 9,
- RF_NOT_TLS = 1 << 8,
- RF_BSS = 1 << 7,
+ RF_NOT_ADDR_SET = 1 << 18,
+ RF_NOT_INTERP = 1 << 17,
+ RF_NOT_ALLOC = 1 << 16,
+ RF_WRITE = 1 << 15,
+ RF_EXEC_WRITE = 1 << 14,
+ RF_EXEC = 1 << 13,
+ RF_RODATA = 1 << 12,
+ RF_NON_TLS_BSS = 1 << 11,
+ RF_NON_TLS_BSS_RO = 1 << 10,
+ RF_NOT_TLS = 1 << 9,
+ RF_BSS = 1 << 8,
+ RF_NOTE = 1 << 7,
RF_PPC_NOT_TOCBSS = 1 << 6,
- RF_PPC_OPD = 1 << 5,
- RF_PPC_TOCL = 1 << 4,
- RF_PPC_TOC = 1 << 3,
+ RF_PPC_TOCL = 1 << 5,
+ RF_PPC_TOC = 1 << 4,
+ RF_PPC_GOT = 1 << 3,
RF_PPC_BRANCH_LT = 1 << 2,
RF_MIPS_GPREL = 1 << 1,
RF_MIPS_NOT_GOT = 1 << 0
@@ -712,8 +743,7 @@ static unsigned getSectionRank(const OutputSection *Sec) {
// considerations:
// * Read-only sections come first such that they go in the
// PT_LOAD covering the program headers at the start of the file.
- // * Read-only, executable sections come next, unless the
- // -no-rosegment option is used.
+ // * Read-only, executable sections come next.
// * Writable, executable sections follow such that .plt on
// architectures where it needs to be writable will be placed
// between .text and .data.
@@ -725,11 +755,16 @@ static unsigned getSectionRank(const OutputSection *Sec) {
if (IsExec) {
if (IsWrite)
Rank |= RF_EXEC_WRITE;
- else if (!Config->SingleRoRx)
+ else
Rank |= RF_EXEC;
- } else {
- if (IsWrite)
- Rank |= RF_WRITE;
+ } 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;
}
// If we got here we know that both A and B are in the same PT_LOAD.
@@ -765,6 +800,12 @@ static unsigned getSectionRank(const OutputSection *Sec) {
if (IsNoBits)
Rank |= RF_BSS;
+ // We create a NOTE segment for contiguous .note sections, so make
+ // them contigous if there are more than one .note section with the
+ // same attributes.
+ if (Sec->Type == SHT_NOTE)
+ Rank |= RF_NOTE;
+
// Some architectures have additional ordering restrictions for sections
// within the same PT_LOAD.
if (Config->EMachine == EM_PPC64) {
@@ -778,18 +819,19 @@ static unsigned getSectionRank(const OutputSection *Sec) {
if (Name != ".tocbss")
Rank |= RF_PPC_NOT_TOCBSS;
- if (Name == ".opd")
- Rank |= RF_PPC_OPD;
-
if (Name == ".toc1")
Rank |= RF_PPC_TOCL;
if (Name == ".toc")
Rank |= RF_PPC_TOC;
+ if (Name == ".got")
+ Rank |= RF_PPC_GOT;
+
if (Name == ".branch_lt")
Rank |= RF_PPC_BRANCH_LT;
}
+
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.
@@ -830,17 +872,19 @@ 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->Static)
+ if (needsInterpSection())
return;
StringRef S = Config->IsRela ? "__rela_iplt_start" : "__rel_iplt_start";
addOptionalRegular(S, InX::RelaIplt, 0, STV_HIDDEN, STB_WEAK);
S = Config->IsRela ? "__rela_iplt_end" : "__rel_iplt_end";
- addOptionalRegular(S, InX::RelaIplt, -1, STV_HIDDEN, STB_WEAK);
+ ElfSym::RelaIpltEnd =
+ addOptionalRegular(S, InX::RelaIplt, 0, STV_HIDDEN, STB_WEAK);
}
template <class ELFT>
-void Writer<ELFT>::forEachRelSec(std::function<void(InputSectionBase &)> Fn) {
+void Writer<ELFT>::forEachRelSec(
+ 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.
@@ -860,14 +904,18 @@ void Writer<ELFT>::forEachRelSec(std::function<void(InputSectionBase &)> Fn) {
// defining these symbols explicitly in the linker script.
template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
if (ElfSym::GlobalOffsetTable) {
- // 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
- InputSection *GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot)
- : cast<InputSection>(InX::Got);
+ // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
+ // to the start of the .got or .got.plt section.
+ InputSection *GotSection = InX::GotPlt;
+ if (!Target->GotBaseSymInGotPlt)
+ GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot)
+ : cast<InputSection>(InX::Got);
ElfSym::GlobalOffsetTable->Section = GotSection;
}
+ if (ElfSym::RelaIpltEnd)
+ ElfSym::RelaIpltEnd->Value = InX::RelaIplt->getSize();
+
PhdrEntry *Last = nullptr;
PhdrEntry *LastRO = nullptr;
@@ -937,8 +985,7 @@ static int getRankProximityAux(OutputSection *A, OutputSection *B) {
static int getRankProximity(OutputSection *A, BaseCommand *B) {
if (auto *Sec = dyn_cast<OutputSection>(B))
- if (Sec->Live)
- return getRankProximityAux(A, Sec);
+ return getRankProximityAux(A, Sec);
return -1;
}
@@ -957,11 +1004,9 @@ static int getRankProximity(OutputSection *A, BaseCommand *B) {
// rw_sec : { *(rw_sec) }
// would mean that the RW PT_LOAD would become unaligned.
static bool shouldSkip(BaseCommand *Cmd) {
- if (isa<OutputSection>(Cmd))
- return false;
if (auto *Assign = dyn_cast<SymbolAssignment>(Cmd))
return Assign->Name != ".";
- return true;
+ return false;
}
// We want to place orphan sections so that they share as much
@@ -984,20 +1029,16 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B,
int Proximity = getRankProximity(Sec, *I);
for (; I != E; ++I) {
auto *CurSec = dyn_cast<OutputSection>(*I);
- if (!CurSec || !CurSec->Live)
+ if (!CurSec)
continue;
if (getRankProximity(Sec, CurSec) != Proximity ||
Sec->SortRank < CurSec->SortRank)
break;
}
- auto IsLiveSection = [](BaseCommand *Cmd) {
- auto *OS = dyn_cast<OutputSection>(Cmd);
- return OS && OS->Live;
- };
-
+ auto IsOutputSec = [](BaseCommand *Cmd) { return isa<OutputSection>(Cmd); };
auto J = std::find_if(llvm::make_reverse_iterator(I),
- llvm::make_reverse_iterator(B), IsLiveSection);
+ llvm::make_reverse_iterator(B), IsOutputSec);
I = J.base();
// As a special case, if the orphan section is the last section, put
@@ -1005,7 +1046,7 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B,
// 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, IsLiveSection);
+ auto NextSec = std::find_if(I, E, IsOutputSec);
if (NextSec == E)
return E;
@@ -1014,32 +1055,172 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B,
return I;
}
-// If no layout was provided by linker script, we want to apply default
-// sorting for special input sections and handle --symbol-ordering-file.
-template <class ELFT> void Writer<ELFT>::sortInputSections() {
- assert(!Script->HasSectionsCommand);
+// Builds section order for handling --symbol-ordering-file.
+static DenseMap<const InputSectionBase *, int> buildSectionOrder() {
+ DenseMap<const InputSectionBase *, int> SectionOrder;
+ // Use the rarely used option -call-graph-ordering-file to sort sections.
+ if (!Config->CallGraphProfile.empty())
+ return computeCallGraphProfileOrder();
- // Sort input sections by priority using the list provided
- // by --symbol-ordering-file.
- DenseMap<SectionBase *, int> Order = buildSectionOrder();
- if (!Order.empty())
- for (BaseCommand *Base : Script->SectionCommands)
- if (auto *Sec = dyn_cast<OutputSection>(Base))
- if (Sec->Live)
- Sec->sort([&](InputSectionBase *S) { return Order.lookup(S); });
+ if (Config->SymbolOrderingFile.empty())
+ return SectionOrder;
+
+ struct SymbolOrderEntry {
+ 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}});
+
+ // Build a map from sections to their priorities.
+ auto AddSym = [&](Symbol &Sym) {
+ auto It = SymbolOrder.find(Sym.getName());
+ if (It == SymbolOrder.end())
+ return;
+ SymbolOrderEntry &Ent = It->second;
+ Ent.Present = true;
+
+ warnUnorderableSymbol(&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);
+ }
+ }
+ };
+ // 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);
+
+ 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();
+ continue;
+ }
+ OrderedSections.push_back({IS, I->second});
+ }
+ llvm::sort(
+ OrderedSections.begin(), OrderedSections.end(),
+ [&](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
+ // section list. On targets with limited-range branches, this is the mid-point
+ // of the unordered section list. This decreases the likelihood that a range
+ // extension thunk will be needed to enter or exit the ordered region. If the
+ // ordered section list is a list of hot functions, we can generally expect
+ // the ordered functions to be called more often than the unordered functions,
+ // making it more likely that any particular call will be within range, and
+ // therefore reducing the number of thunks required.
+ //
+ // For example, imagine that you have 8MB of hot code and 32MB of cold code.
+ // If the layout is:
+ //
+ // 8MB hot
+ // 32MB cold
+ //
+ // only the first 8-16MB of the cold code (depending on which hot function it
+ // is actually calling) can call the hot code without a range extension thunk.
+ // However, if we use this layout:
+ //
+ // 16MB cold
+ // 8MB h